@faasjs/ant-design 8.0.0-beta.15 → 8.0.0-beta.17

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
@@ -23,6 +23,7 @@
23
23
  - [Title](functions/Title.md)
24
24
  - [transferOptions](functions/transferOptions.md)
25
25
  - [transferValue](functions/transferValue.md)
26
+ - [useApp](functions/useApp.md)
26
27
  - [useConfigContext](functions/useConfigContext.md)
27
28
  - [useDrawer](functions/useDrawer.md)
28
29
  - [useFaas](functions/useFaas.md)
@@ -46,7 +47,6 @@
46
47
  - [FaasDataWrapperProps](interfaces/FaasDataWrapperProps.md)
47
48
  - [FaasItemProps](interfaces/FaasItemProps.md)
48
49
  - [FormItemProps](interfaces/FormItemProps.md)
49
- - [FormProps](interfaces/FormProps.md)
50
50
  - [LinkProps](interfaces/LinkProps.md)
51
51
  - [ModalProps](interfaces/ModalProps.md)
52
52
  - [RoutesProps](interfaces/RoutesProps.md)
@@ -70,6 +70,8 @@
70
70
  - [FaasItemType](type-aliases/FaasItemType.md)
71
71
  - [FaasItemTypeValue](type-aliases/FaasItemTypeValue.md)
72
72
  - [FaasReactClientOptions](type-aliases/FaasReactClientOptions.md)
73
+ - [FormFaasProps](type-aliases/FormFaasProps.md)
74
+ - [FormProps](type-aliases/FormProps.md)
73
75
  - [FormSubmitProps](type-aliases/FormSubmitProps.md)
74
76
  - [LoadingProps](type-aliases/LoadingProps.md)
75
77
  - [ResolvedTheme](type-aliases/ResolvedTheme.md)
@@ -85,5 +87,5 @@
85
87
 
86
88
  ## Variables
87
89
 
90
+ - [AppContext](variables/AppContext.md)
88
91
  - [ConfigContext](variables/ConfigContext.md)
89
- - [useApp](variables/useApp.md)
package/dist/index.cjs CHANGED
@@ -272,7 +272,7 @@ function useModal(init) {
272
272
  };
273
273
  }
274
274
  //#endregion
275
- //#region src/App.tsx
275
+ //#region src/useApp.ts
276
276
  const AppContext = (0, _faasjs_react.createSplittingContext)([
277
277
  "message",
278
278
  "notification",
@@ -281,6 +281,20 @@ const AppContext = (0, _faasjs_react.createSplittingContext)([
281
281
  "drawerProps",
282
282
  "setDrawerProps"
283
283
  ]);
284
+ /**
285
+ * Get app context.
286
+ *
287
+ * ```ts
288
+ * import { useApp } from '@faasjs/ant-design'
289
+ *
290
+ * const { message, notification, setModalProps, setDrawerProps } = useApp()
291
+ * ```
292
+ */
293
+ function useApp() {
294
+ return AppContext.use();
295
+ }
296
+ //#endregion
297
+ //#region src/App.tsx
284
298
  function RoutesApp(props) {
285
299
  const location = (0, react_router_dom.useLocation)();
286
300
  const { drawerProps, setDrawerProps, modalProps, setModalProps } = useApp();
@@ -365,17 +379,6 @@ function App(props) {
365
379
  })
366
380
  });
367
381
  }
368
- /**
369
- * Get app context.
370
- *
371
- * ```ts
372
- * import { useApp } from '@faasjs/ant-design'
373
- *
374
- * const { message, notification, setModalProps, setDrawerProps } = useApp()
375
- * ```
376
- */
377
- const useApp = AppContext.use;
378
- App.useApp = useApp;
379
382
  //#endregion
380
383
  //#region src/Blank.tsx
381
384
  /**
@@ -895,105 +898,183 @@ function isFormItemProps(item) {
895
898
  * Form component with Ant Design & FaasJS
896
899
  *
897
900
  * - Based on [Ant Design Form](https://ant.design/components/form/).
901
+ * - Use `onFinish` for custom submit logic.
902
+ * - Use `faas` for the built-in FaasJS submit flow.
903
+ *
904
+ * @example
905
+ * ```tsx
906
+ * import { Form } from '@faasjs/ant-design'
907
+ *
908
+ * export function ProfileForm() {
909
+ * return (
910
+ * <Form
911
+ * items={[
912
+ * { id: 'name', required: true },
913
+ * { id: 'email', required: true },
914
+ * ]}
915
+ * onFinish={async (values) => {
916
+ * console.log(values)
917
+ * }}
918
+ * />
919
+ * )
920
+ * }
921
+ * ```
922
+ *
923
+ * @example
924
+ * ```tsx
925
+ * import { Form } from '@faasjs/ant-design'
926
+ *
927
+ * export function CreateUserForm() {
928
+ * return (
929
+ * <Form
930
+ * initialValues={{ role: 'user' }}
931
+ * items={[
932
+ * { id: 'name', required: true },
933
+ * { id: 'role', options: ['user', 'admin'] },
934
+ * ]}
935
+ * faas={{
936
+ * action: 'user/create',
937
+ * params: (values) => ({
938
+ * role: values.role || 'user',
939
+ * }),
940
+ * }}
941
+ * />
942
+ * )
943
+ * }
944
+ * ```
898
945
  */
899
946
  function Form(props) {
900
947
  const [loading, setLoading] = (0, react.useState)(false);
901
- const [computedProps, setComputedProps] = (0, react.useState)();
902
- const [submit, setSubmit] = (0, react.useState)();
948
+ const [antdProps, setAntdProps] = (0, react.useState)();
949
+ const [submit, setSubmit] = (0, react.useState)(props.submit === false ? false : {});
950
+ const [items, setItems] = (0, react.useState)([]);
903
951
  const config = useConfigContext();
904
952
  const [extendTypes, setExtendTypes] = (0, react.useState)();
905
953
  const [form] = antd.Form.useForm(props.form);
906
954
  const [initialValues, setInitialValues] = (0, react.useState)(props.initialValues || Object.create(null));
955
+ const [onFinish, setOnFinish] = (0, react.useState)();
907
956
  (0, _faasjs_react.useEqualEffect)(() => {
908
- const { submit, ...propsCopy } = {
909
- ...props,
910
- form
911
- };
912
- let nextInitialValues = propsCopy.initialValues;
913
- if (typeof submit !== "undefined") setSubmit(submit);
914
- if (propsCopy.initialValues && propsCopy.items?.length) {
915
- for (const key in propsCopy.initialValues) propsCopy.initialValues[key] = transferValue(propsCopy.items.find((item) => isFormItemProps(item) && item.id === key)?.type, propsCopy.initialValues[key]);
916
- nextInitialValues = propsCopy.initialValues;
917
- setInitialValues(propsCopy.initialValues);
918
- delete propsCopy.initialValues;
919
- }
920
- if (propsCopy.items?.length) {
921
- for (const item of propsCopy.items) if (isFormItemProps(item) && item.if) item.hidden = !item.if(nextInitialValues || Object.create(null));
957
+ if (props.onFinish) {
958
+ setOnFinish(() => async (values) => {
959
+ if (!props.onFinish) return;
960
+ setLoading(true);
961
+ try {
962
+ return await props.onFinish(values);
963
+ } finally {
964
+ setLoading(false);
965
+ }
966
+ });
967
+ return;
922
968
  }
923
- const submitTo = typeof submit === "object" ? submit.to : void 0;
924
- if (propsCopy.onFinish) {
925
- const originOnFinish = propsCopy.onFinish;
926
- propsCopy.onFinish = async (values) => {
969
+ if (props.faas?.action) {
970
+ setOnFinish(() => async (values) => {
971
+ if (!props.faas?.action) return;
927
972
  setLoading(true);
973
+ let submitValues = values;
928
974
  try {
929
- if (submitTo?.action) await originOnFinish(values, async (nextValues) => (0, _faasjs_react.faas)(submitTo.action, submitTo.params ? {
930
- ...nextValues,
931
- ...submitTo.params
932
- } : nextValues));
933
- else await originOnFinish(values);
975
+ if (props.faas?.transformValues) submitValues = await props.faas.transformValues(values);
976
+ const extraParams = typeof props.faas?.params === "function" ? props.faas.params(submitValues) : props.faas.params;
977
+ if (extraParams) submitValues = {
978
+ ...submitValues,
979
+ ...extraParams
980
+ };
981
+ const result = await (0, _faasjs_react.faas)(props.faas.action, submitValues);
982
+ props.faas.onSuccess?.(result, submitValues);
983
+ return result;
934
984
  } catch (error) {
935
- console.error(error);
985
+ props.faas.onError?.(error, submitValues);
986
+ throw error;
987
+ } finally {
988
+ props.faas.onFinally?.();
989
+ setLoading(false);
936
990
  }
937
- setLoading(false);
938
- };
939
- } else if (submitTo?.action) propsCopy.onFinish = async (values) => {
940
- setLoading(true);
941
- return (0, _faasjs_react.faas)(submitTo.action, submitTo.params ? {
942
- ...values,
943
- ...submitTo.params
944
- } : values).then((result) => {
945
- submitTo.then?.(result);
946
- return result;
947
- }).catch((error) => {
948
- submitTo.catch?.(error);
949
- return Promise.reject(error);
950
- }).finally(() => {
951
- submitTo.finally?.();
952
- setLoading(false);
953
991
  });
954
- };
955
- if (propsCopy.extendTypes) {
956
- setExtendTypes(propsCopy.extendTypes);
957
- delete propsCopy.extendTypes;
992
+ return;
958
993
  }
959
- setComputedProps(propsCopy);
960
- }, [form, props]);
994
+ setOnFinish(void 0);
995
+ }, [props.onFinish, props.faas]);
996
+ (0, _faasjs_react.useEqualEffect)(() => {
997
+ setExtendTypes(props.extendTypes);
998
+ }, [props.extendTypes]);
999
+ (0, _faasjs_react.useEqualEffect)(() => {
1000
+ setSubmit(props.submit === false ? false : props.submit || {});
1001
+ }, [props.submit]);
1002
+ (0, _faasjs_react.useEqualEffect)(() => {
1003
+ const nextInitialValues = props.initialValues ? JSON.parse(JSON.stringify(props.initialValues)) : Object.create(null);
1004
+ for (const key in nextInitialValues) nextInitialValues[key] = transferValue(props.items?.find((item) => isFormItemProps(item) && item.id === key)?.type, nextInitialValues[key]);
1005
+ if (props.items?.length) setItems(props.items.map((item) => {
1006
+ if (!isFormItemProps(item) || !item.if) return item;
1007
+ return {
1008
+ ...item,
1009
+ hidden: !item.if(nextInitialValues)
1010
+ };
1011
+ }));
1012
+ else setItems([]);
1013
+ if (props.initialValues) {
1014
+ setInitialValues(nextInitialValues);
1015
+ return;
1016
+ }
1017
+ setInitialValues(null);
1018
+ }, [props.initialValues, props.items]);
1019
+ (0, _faasjs_react.useEqualEffect)(() => {
1020
+ const propsCopy = { ...props };
1021
+ delete propsCopy.onFinish;
1022
+ delete propsCopy.faas;
1023
+ delete propsCopy.extendTypes;
1024
+ delete propsCopy.submit;
1025
+ delete propsCopy.items;
1026
+ delete propsCopy.initialValues;
1027
+ setAntdProps(propsCopy);
1028
+ }, [props]);
961
1029
  const onValuesChange = (0, _faasjs_react.useEqualCallback)((changedValues, allValues) => {
962
1030
  console.debug("Form:onValuesChange", changedValues, allValues);
963
1031
  if (props.onValuesChange) props.onValuesChange(changedValues, allValues);
964
- if (!props.items) return;
1032
+ if (!items.length) return;
965
1033
  for (const key in changedValues) {
966
- const item = computedProps?.items?.find((i) => isFormItemProps(i) && i.id === key);
1034
+ const item = items.find((i) => isFormItemProps(i) && i.id === key);
967
1035
  if (item?.onValueChange) item.onValueChange(changedValues[key], allValues, form);
968
1036
  }
969
- }, [computedProps]);
1037
+ }, [
1038
+ items,
1039
+ props.onValuesChange,
1040
+ form
1041
+ ]);
970
1042
  (0, _faasjs_react.useEqualEffect)(() => {
971
1043
  if (!initialValues) return;
972
1044
  console.debug("Form:initialValues", initialValues);
973
1045
  form.setFieldsValue(initialValues);
974
1046
  setInitialValues(null);
975
- }, [form, initialValues]);
976
- if (!computedProps) return null;
1047
+ }, [
1048
+ form,
1049
+ initialValues,
1050
+ items
1051
+ ]);
1052
+ if (!antdProps) return null;
1053
+ const submitButtonProps = typeof submit === "object" ? submit.buttonProps : void 0;
1054
+ const submitButtonLoading = loading ? true : submitButtonProps?.loading ?? false;
977
1055
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(antd.Form, {
978
- ...computedProps,
1056
+ ...antdProps,
1057
+ form,
1058
+ onFinish,
979
1059
  onValuesChange,
980
1060
  children: [
981
- computedProps.beforeItems,
982
- computedProps.items?.map((item) => {
1061
+ props.beforeItems,
1062
+ items.map((item) => {
983
1063
  if (isFormItemProps(item)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormItem, {
984
1064
  ...item,
985
1065
  ...extendTypes ? { extendTypes } : {}
986
1066
  }, item.id);
987
1067
  return item;
988
1068
  }),
989
- computedProps.children,
1069
+ props.children,
990
1070
  typeof submit !== "boolean" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(antd.Button, {
1071
+ ...submitButtonProps,
991
1072
  htmlType: "submit",
992
- type: "primary",
993
- loading,
1073
+ type: submitButtonProps?.type || "primary",
1074
+ loading: submitButtonLoading,
994
1075
  children: submit?.text || config.theme.Form.submit.text
995
1076
  }),
996
- computedProps.footer
1077
+ props.footer
997
1078
  ]
998
1079
  });
999
1080
  }
@@ -1614,6 +1695,7 @@ function useThemeToken() {
1614
1695
  }
1615
1696
  //#endregion
1616
1697
  exports.App = App;
1698
+ exports.AppContext = AppContext;
1617
1699
  exports.Blank = Blank;
1618
1700
  exports.ConfigContext = ConfigContext;
1619
1701
  exports.ConfigProvider = ConfigProvider;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,9 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
1
+ import { ErrorBoundaryProps, FaasDataInjection as FaasDataInjection$1, FaasDataWrapperProps as FaasDataWrapperProps$1, FaasDataWrapperRef, FaasReactClient, FaasReactClientOptions, faas, useFaas } from "@faasjs/react";
2
2
  import { ButtonProps, ConfigProviderProps as ConfigProviderProps$1, DatePickerProps, DescriptionsProps, Drawer, DrawerProps as DrawerProps$1, FormInstance, FormItemProps as FormItemProps$1, FormProps as FormProps$1, GlobalToken, InputNumberProps, InputProps, Modal, ModalProps as ModalProps$1, RadioProps, SelectProps, SwitchProps, TableColumnProps, TablePaginationConfig, TableProps as TableProps$1, TabsProps as TabsProps$1 } from "antd";
3
- import { MessageInstance } from "antd/es/message/interface";
4
- import { NotificationInstance } from "antd/es/notification/interface";
5
3
  import { BrowserRouterProps, RouteProps } from "react-router-dom";
6
4
  import * as react from "react";
7
5
  import { CSSProperties, ComponentType, Dispatch, FC, JSX, LazyExoticComponent, ReactElement, ReactNode, SetStateAction, lazy } from "react";
8
- import { ErrorBoundaryProps, FaasDataInjection as FaasDataInjection$1, FaasDataWrapperProps as FaasDataWrapperProps$1, FaasDataWrapperRef, FaasReactClient, FaasReactClientOptions, faas, useFaas } from "@faasjs/react";
6
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
9
7
  import { Dayjs } from "dayjs";
10
8
  import * as antd_es_form_FormItem0 from "antd/es/form/FormItem";
11
9
  import * as antd_es_form0 from "antd/es/form";
@@ -16,6 +14,8 @@ import * as antd_es_form_hooks_useFormInstance0 from "antd/es/form/hooks/useForm
16
14
  import * as _rc_component_form0 from "@rc-component/form";
17
15
  import * as antd_es_form_context0 from "antd/es/form/context";
18
16
  import { Tab } from "@rc-component/tabs/lib/interface";
17
+ import { MessageInstance } from "antd/es/message/interface";
18
+ import { NotificationInstance } from "antd/es/notification/interface";
19
19
 
20
20
  //#region ../types/src/index.d.ts
21
21
  /**
@@ -206,64 +206,12 @@ declare const ConfigContext: react.Context<ConfigContextValue>;
206
206
  declare function ConfigProvider(props: ConfigProviderProps): react_jsx_runtime0.JSX.Element | null;
207
207
  declare function useConfigContext(): ConfigContextValue;
208
208
  //#endregion
209
- //#region src/Drawer.d.ts
210
- interface DrawerProps extends DrawerProps$1 {
211
- children?: JSX.Element | JSX.Element[];
212
- }
213
- type setDrawerProps = Dispatch<SetStateAction<DrawerProps>>;
214
- /**
215
- * Hook style drawer
216
- *
217
- * ```tsx
218
- * function Example() {
219
- * const { drawer, setDrawerProps } = useDrawer()
220
- *
221
- * return <>
222
- * <Button onClick={ () => setDrawerProps(prev => ({ open: !prev.open})) }>
223
- * Toggle
224
- * </Button>
225
- * {drawer}
226
- * </>
227
- * }
228
- * ```
229
- */
230
- declare function useDrawer(init?: DrawerProps): {
231
- drawer: react_jsx_runtime0.JSX.Element;
232
- drawerProps: DrawerProps;
233
- setDrawerProps: setDrawerProps;
234
- };
235
- //#endregion
236
209
  //#region src/ErrorBoundary.d.ts
237
210
  /**
238
211
  * Styled error boundary.
239
212
  */
240
213
  declare function ErrorBoundary(props: ErrorBoundaryProps): react_jsx_runtime0.JSX.Element;
241
214
  //#endregion
242
- //#region src/Modal.d.ts
243
- interface ModalProps extends ModalProps$1 {
244
- children?: JSX.Element | JSX.Element[] | string;
245
- }
246
- type setModalProps = Dispatch<SetStateAction<ModalProps>>;
247
- /**
248
- * Hook style modal
249
- *
250
- * ```tsx
251
- * function Example() {
252
- * const { modal, setModalProps } = useModal()
253
- *
254
- * return <>
255
- * <Button onClick={() => setModalProps({ open: true })}>Open Modal</Button>
256
- * {modal}
257
- * </>
258
- * }
259
- * ```
260
- */
261
- declare function useModal(init?: ModalProps): {
262
- modal: react_jsx_runtime0.JSX.Element;
263
- modalProps: ModalProps;
264
- setModalProps: setModalProps;
265
- };
266
- //#endregion
267
215
  //#region src/App.d.ts
268
216
  interface AppProps {
269
217
  children: React.ReactNode;
@@ -282,14 +230,6 @@ interface AppProps {
282
230
  /** @see https://faasjs.com/doc/ant-design/#configprovider */
283
231
  faasConfigProviderProps?: Omit<ConfigProviderProps, 'children'> | false;
284
232
  }
285
- interface useAppProps {
286
- message: MessageInstance;
287
- notification: NotificationInstance;
288
- modalProps: ModalProps;
289
- setModalProps: setModalProps;
290
- drawerProps: DrawerProps;
291
- setDrawerProps: setDrawerProps;
292
- }
293
233
  /**
294
234
  * App component with Ant Design & FaasJS
295
235
  *
@@ -318,19 +258,6 @@ interface useAppProps {
318
258
  * ```
319
259
  */
320
260
  declare function App(props: AppProps): react_jsx_runtime0.JSX.Element;
321
- declare namespace App {
322
- var useApp: any;
323
- }
324
- /**
325
- * Get app context.
326
- *
327
- * ```ts
328
- * import { useApp } from '@faasjs/ant-design'
329
- *
330
- * const { message, notification, setModalProps, setDrawerProps } = useApp()
331
- * ```
332
- */
333
- declare const useApp: any;
334
261
  //#endregion
335
262
  //#region src/Blank.d.ts
336
263
  interface BlankProps {
@@ -576,7 +503,7 @@ type UnionFaasItemInjection<Value = any, Values = any> = {
576
503
  * @param value - The value of the current item.
577
504
  * @param values - The entire list of values.
578
505
  * @param index - The index of the current item in the list.
579
- * @param scene {@link UnionScene} - The scene in which the rendering is taking place.
506
+ * @param scene - The scene in which the rendering is taking place. See {@link UnionScene}.
580
507
  *
581
508
  * @example
582
509
  * ```tsx
@@ -796,57 +723,126 @@ declare namespace Description {
796
723
  var displayName: string;
797
724
  }
798
725
  //#endregion
726
+ //#region src/Drawer.d.ts
727
+ interface DrawerProps extends DrawerProps$1 {
728
+ children?: JSX.Element | JSX.Element[];
729
+ }
730
+ type setDrawerProps = Dispatch<SetStateAction<DrawerProps>>;
731
+ /**
732
+ * Hook style drawer
733
+ *
734
+ * ```tsx
735
+ * function Example() {
736
+ * const { drawer, setDrawerProps } = useDrawer()
737
+ *
738
+ * return <>
739
+ * <Button onClick={ () => setDrawerProps(prev => ({ open: !prev.open})) }>
740
+ * Toggle
741
+ * </Button>
742
+ * {drawer}
743
+ * </>
744
+ * }
745
+ * ```
746
+ */
747
+ declare function useDrawer(init?: DrawerProps): {
748
+ drawer: react_jsx_runtime0.JSX.Element;
749
+ drawerProps: DrawerProps;
750
+ setDrawerProps: setDrawerProps;
751
+ };
752
+ //#endregion
799
753
  //#region src/Form.d.ts
800
754
  type FormSubmitProps = {
801
- /** Default: Submit */text?: string;
802
- /**
803
- * Submit to FaasJS server.
804
- *
805
- * If use onFinish, you should call submit manually.
806
- * ```ts
807
- * {
808
- * submit: {
809
- * to: {
810
- * action: 'action_name'
811
- * }
812
- * },
813
- * onFinish: (values, submit) => {
814
- * // do something before submit
815
- *
816
- * // submit
817
- * await submit({
818
- * ...values,
819
- * extraProps: 'some extra props'
820
- * })
821
- *
822
- * // do something after submit
823
- * }
824
- * }
825
- * ```
826
- */
827
- to?: {
828
- action: FaasAction; /** params will overwrite form values before submit */
829
- params?: Record<string, any>;
830
- then?: (result: any) => void;
831
- catch?: (error: any) => void;
832
- finally?: () => void;
833
- };
755
+ /** Default: Submit */text?: string; /** Props for the built-in submit button */
756
+ buttonProps?: ButtonProps;
757
+ };
758
+ /**
759
+ * Built-in FaasJS submit handler for Form.
760
+ *
761
+ * @example
762
+ * ```ts
763
+ * {
764
+ * action: 'user/create',
765
+ * params: (values) => ({
766
+ * ...values,
767
+ * role: values.role || 'user',
768
+ * }),
769
+ * onSuccess: (result) => {
770
+ * console.log(result)
771
+ * },
772
+ * }
773
+ * ```
774
+ */
775
+ type FormFaasProps<Values extends Record<string, any> = any> = {
776
+ /** Action name to submit to */action: FaasAction; /** params will overwrite form values before submit */
777
+ params?: Record<string, any> | ((values: Record<string, any>) => Record<string, any>); /** Transform form values before sending the request */
778
+ transformValues?: (values: Values) => Record<string, any> | Promise<Record<string, any>>; /** Called when the request succeeds */
779
+ onSuccess?: (result: any, values: Record<string, any>) => void; /** Called when the request fails */
780
+ onError?: (error: any, values: Record<string, any>) => void; /** Called after the request settles */
781
+ onFinally?: () => void;
834
782
  };
835
- interface FormProps<Values extends Record<string, any> = any, ExtendItemProps extends ExtendFormItemProps = ExtendFormItemProps> extends Omit<FormProps$1<Values>, 'onFinish' | 'children' | 'initialValues'> {
836
- items?: ((ExtendItemProps extends ExtendFormItemProps ? ExtendItemProps | FormItemProps : FormItemProps) | JSX.Element)[];
837
- /** Default: { text: 'Submit' }, set false to disable it */
783
+ type FormProps<Values extends Record<string, any> = any, ExtendItemProps extends ExtendFormItemProps = ExtendFormItemProps> = Omit<FormProps$1<Values>, 'onFinish' | 'children' | 'initialValues'> & {
784
+ items?: ((ExtendItemProps extends ExtendFormItemProps ? ExtendItemProps | FormItemProps : FormItemProps) | JSX.Element)[]; /** Default: { text: 'Submit' }, set false to disable it */
838
785
  submit?: false | FormSubmitProps;
839
- onFinish?: (values: Values, submit?: (values: any) => Promise<any>) => Promise<any>;
840
786
  beforeItems?: JSX.Element | JSX.Element[];
841
787
  footer?: JSX.Element | JSX.Element[];
842
788
  extendTypes?: ExtendTypes;
843
789
  children?: ReactNode;
844
790
  initialValues?: Partial<Values>;
845
- }
791
+ } & ({
792
+ /** Built-in FaasJS submit handler, ignored when onFinish is provided */faas?: FormFaasProps<Values>;
793
+ onFinish?: never;
794
+ } | {
795
+ faas?: never; /** Custom submit handler, takes precedence over faas when both are provided */
796
+ onFinish?: (values: Values) => void | Promise<void>;
797
+ });
846
798
  /**
847
799
  * Form component with Ant Design & FaasJS
848
800
  *
849
801
  * - Based on [Ant Design Form](https://ant.design/components/form/).
802
+ * - Use `onFinish` for custom submit logic.
803
+ * - Use `faas` for the built-in FaasJS submit flow.
804
+ *
805
+ * @example
806
+ * ```tsx
807
+ * import { Form } from '@faasjs/ant-design'
808
+ *
809
+ * export function ProfileForm() {
810
+ * return (
811
+ * <Form
812
+ * items={[
813
+ * { id: 'name', required: true },
814
+ * { id: 'email', required: true },
815
+ * ]}
816
+ * onFinish={async (values) => {
817
+ * console.log(values)
818
+ * }}
819
+ * />
820
+ * )
821
+ * }
822
+ * ```
823
+ *
824
+ * @example
825
+ * ```tsx
826
+ * import { Form } from '@faasjs/ant-design'
827
+ *
828
+ * export function CreateUserForm() {
829
+ * return (
830
+ * <Form
831
+ * initialValues={{ role: 'user' }}
832
+ * items={[
833
+ * { id: 'name', required: true },
834
+ * { id: 'role', options: ['user', 'admin'] },
835
+ * ]}
836
+ * faas={{
837
+ * action: 'user/create',
838
+ * params: (values) => ({
839
+ * role: values.role || 'user',
840
+ * }),
841
+ * }}
842
+ * />
843
+ * )
844
+ * }
845
+ * ```
850
846
  */
851
847
  declare function Form<Values extends Record<string, any> = any>(props: FormProps<Values>): react_jsx_runtime0.JSX.Element | null;
852
848
  declare namespace Form {
@@ -886,6 +882,31 @@ interface LinkProps {
886
882
  */
887
883
  declare function Link(props: LinkProps): react_jsx_runtime0.JSX.Element;
888
884
  //#endregion
885
+ //#region src/Modal.d.ts
886
+ interface ModalProps extends ModalProps$1 {
887
+ children?: JSX.Element | JSX.Element[] | string;
888
+ }
889
+ type setModalProps = Dispatch<SetStateAction<ModalProps>>;
890
+ /**
891
+ * Hook style modal
892
+ *
893
+ * ```tsx
894
+ * function Example() {
895
+ * const { modal, setModalProps } = useModal()
896
+ *
897
+ * return <>
898
+ * <Button onClick={() => setModalProps({ open: true })}>Open Modal</Button>
899
+ * {modal}
900
+ * </>
901
+ * }
902
+ * ```
903
+ */
904
+ declare function useModal(init?: ModalProps): {
905
+ modal: react_jsx_runtime0.JSX.Element;
906
+ modalProps: ModalProps;
907
+ setModalProps: setModalProps;
908
+ };
909
+ //#endregion
889
910
  //#region src/Routers.d.ts
890
911
  declare function PageNotFound(): react_jsx_runtime0.JSX.Element;
891
912
  interface RoutesProps {
@@ -990,6 +1011,27 @@ interface TitleProps {
990
1011
  */
991
1012
  declare function Title(props: TitleProps): JSX.Element | null;
992
1013
  //#endregion
1014
+ //#region src/useApp.d.ts
1015
+ interface useAppProps {
1016
+ message: MessageInstance;
1017
+ notification: NotificationInstance;
1018
+ modalProps: ModalProps;
1019
+ setModalProps: setModalProps;
1020
+ drawerProps: DrawerProps;
1021
+ setDrawerProps: setDrawerProps;
1022
+ }
1023
+ declare const AppContext: any;
1024
+ /**
1025
+ * Get app context.
1026
+ *
1027
+ * ```ts
1028
+ * import { useApp } from '@faasjs/ant-design'
1029
+ *
1030
+ * const { message, notification, setModalProps, setDrawerProps } = useApp()
1031
+ * ```
1032
+ */
1033
+ declare function useApp<NewT extends useAppProps = useAppProps>(this: void): Readonly<NewT>;
1034
+ //#endregion
993
1035
  //#region src/useThemeToken.d.ts
994
1036
  /**
995
1037
  * Hook to retrieve the theme token from the Ant Design theme configuration.
@@ -1001,4 +1043,4 @@ declare function Title(props: TitleProps): JSX.Element | null;
1001
1043
  */
1002
1044
  declare function useThemeToken(): GlobalToken;
1003
1045
  //#endregion
1004
- export { App, AppProps, BaseItemProps, BaseOption, Blank, BlankProps, ConfigContext, ConfigProvider, ConfigProviderProps, Description, DescriptionItemContentProps, DescriptionItemProps, DescriptionProps, Drawer, DrawerProps, ErrorBoundary, type ErrorBoundaryProps, ExtendDescriptionItemProps, ExtendDescriptionTypeProps, type ExtendFormItemProps, type ExtendFormTypeProps, ExtendTableItemProps, ExtendTableTypeProps, ExtendTypes, FaasDataInjection, FaasDataWrapper, FaasDataWrapperProps, type FaasDataWrapperRef, FaasItemProps, FaasItemType, FaasItemTypeValue, FaasReactClient, type FaasReactClientOptions, Form, FormItem, FormItemProps, FormProps, FormSubmitProps, Link, LinkProps, Loading, LoadingProps, Modal, ModalProps, PageNotFound, ResolvedTheme, Routes, RoutesProps, TabProps, Table, TableFaasDataParams, TableFaasDataResponse, TableItemProps, TableProps, Tabs, TabsProps, Title, TitleProps, UnionFaasItemElement, UnionFaasItemInjection, UnionFaasItemProps, UnionFaasItemRender, UnionScene, cloneUnionFaasItemElement, faas, idToTitle, lazy, setDrawerProps, setModalProps, transferOptions, transferValue, useApp, useAppProps, useConfigContext, useDrawer, useFaas, useModal, useThemeToken, withFaasData };
1046
+ export { App, AppContext, AppProps, BaseItemProps, BaseOption, Blank, BlankProps, ConfigContext, ConfigProvider, ConfigProviderProps, Description, DescriptionItemContentProps, DescriptionItemProps, DescriptionProps, Drawer, DrawerProps, ErrorBoundary, type ErrorBoundaryProps, ExtendDescriptionItemProps, ExtendDescriptionTypeProps, type ExtendFormItemProps, type ExtendFormTypeProps, ExtendTableItemProps, ExtendTableTypeProps, ExtendTypes, FaasDataInjection, FaasDataWrapper, FaasDataWrapperProps, type FaasDataWrapperRef, FaasItemProps, FaasItemType, FaasItemTypeValue, FaasReactClient, type FaasReactClientOptions, Form, FormFaasProps, FormItem, FormItemProps, FormProps, FormSubmitProps, Link, LinkProps, Loading, LoadingProps, Modal, ModalProps, PageNotFound, ResolvedTheme, Routes, RoutesProps, TabProps, Table, TableFaasDataParams, TableFaasDataResponse, TableItemProps, TableProps, Tabs, TabsProps, Title, TitleProps, UnionFaasItemElement, UnionFaasItemInjection, UnionFaasItemProps, UnionFaasItemRender, UnionScene, cloneUnionFaasItemElement, faas, idToTitle, lazy, setDrawerProps, setModalProps, transferOptions, transferValue, useApp, useAppProps, useConfigContext, useDrawer, useFaas, useModal, useThemeToken, withFaasData };
package/dist/index.mjs CHANGED
@@ -248,7 +248,7 @@ function useModal(init) {
248
248
  };
249
249
  }
250
250
  //#endregion
251
- //#region src/App.tsx
251
+ //#region src/useApp.ts
252
252
  const AppContext = createSplittingContext([
253
253
  "message",
254
254
  "notification",
@@ -257,6 +257,20 @@ const AppContext = createSplittingContext([
257
257
  "drawerProps",
258
258
  "setDrawerProps"
259
259
  ]);
260
+ /**
261
+ * Get app context.
262
+ *
263
+ * ```ts
264
+ * import { useApp } from '@faasjs/ant-design'
265
+ *
266
+ * const { message, notification, setModalProps, setDrawerProps } = useApp()
267
+ * ```
268
+ */
269
+ function useApp() {
270
+ return AppContext.use();
271
+ }
272
+ //#endregion
273
+ //#region src/App.tsx
260
274
  function RoutesApp(props) {
261
275
  const location = useLocation();
262
276
  const { drawerProps, setDrawerProps, modalProps, setModalProps } = useApp();
@@ -341,17 +355,6 @@ function App(props) {
341
355
  })
342
356
  });
343
357
  }
344
- /**
345
- * Get app context.
346
- *
347
- * ```ts
348
- * import { useApp } from '@faasjs/ant-design'
349
- *
350
- * const { message, notification, setModalProps, setDrawerProps } = useApp()
351
- * ```
352
- */
353
- const useApp = AppContext.use;
354
- App.useApp = useApp;
355
358
  //#endregion
356
359
  //#region src/Blank.tsx
357
360
  /**
@@ -871,105 +874,183 @@ function isFormItemProps(item) {
871
874
  * Form component with Ant Design & FaasJS
872
875
  *
873
876
  * - Based on [Ant Design Form](https://ant.design/components/form/).
877
+ * - Use `onFinish` for custom submit logic.
878
+ * - Use `faas` for the built-in FaasJS submit flow.
879
+ *
880
+ * @example
881
+ * ```tsx
882
+ * import { Form } from '@faasjs/ant-design'
883
+ *
884
+ * export function ProfileForm() {
885
+ * return (
886
+ * <Form
887
+ * items={[
888
+ * { id: 'name', required: true },
889
+ * { id: 'email', required: true },
890
+ * ]}
891
+ * onFinish={async (values) => {
892
+ * console.log(values)
893
+ * }}
894
+ * />
895
+ * )
896
+ * }
897
+ * ```
898
+ *
899
+ * @example
900
+ * ```tsx
901
+ * import { Form } from '@faasjs/ant-design'
902
+ *
903
+ * export function CreateUserForm() {
904
+ * return (
905
+ * <Form
906
+ * initialValues={{ role: 'user' }}
907
+ * items={[
908
+ * { id: 'name', required: true },
909
+ * { id: 'role', options: ['user', 'admin'] },
910
+ * ]}
911
+ * faas={{
912
+ * action: 'user/create',
913
+ * params: (values) => ({
914
+ * role: values.role || 'user',
915
+ * }),
916
+ * }}
917
+ * />
918
+ * )
919
+ * }
920
+ * ```
874
921
  */
875
922
  function Form(props) {
876
923
  const [loading, setLoading] = useState(false);
877
- const [computedProps, setComputedProps] = useState();
878
- const [submit, setSubmit] = useState();
924
+ const [antdProps, setAntdProps] = useState();
925
+ const [submit, setSubmit] = useState(props.submit === false ? false : {});
926
+ const [items, setItems] = useState([]);
879
927
  const config = useConfigContext();
880
928
  const [extendTypes, setExtendTypes] = useState();
881
929
  const [form] = Form$1.useForm(props.form);
882
930
  const [initialValues, setInitialValues] = useState(props.initialValues || Object.create(null));
931
+ const [onFinish, setOnFinish] = useState();
883
932
  useEqualEffect(() => {
884
- const { submit, ...propsCopy } = {
885
- ...props,
886
- form
887
- };
888
- let nextInitialValues = propsCopy.initialValues;
889
- if (typeof submit !== "undefined") setSubmit(submit);
890
- if (propsCopy.initialValues && propsCopy.items?.length) {
891
- for (const key in propsCopy.initialValues) propsCopy.initialValues[key] = transferValue(propsCopy.items.find((item) => isFormItemProps(item) && item.id === key)?.type, propsCopy.initialValues[key]);
892
- nextInitialValues = propsCopy.initialValues;
893
- setInitialValues(propsCopy.initialValues);
894
- delete propsCopy.initialValues;
895
- }
896
- if (propsCopy.items?.length) {
897
- for (const item of propsCopy.items) if (isFormItemProps(item) && item.if) item.hidden = !item.if(nextInitialValues || Object.create(null));
933
+ if (props.onFinish) {
934
+ setOnFinish(() => async (values) => {
935
+ if (!props.onFinish) return;
936
+ setLoading(true);
937
+ try {
938
+ return await props.onFinish(values);
939
+ } finally {
940
+ setLoading(false);
941
+ }
942
+ });
943
+ return;
898
944
  }
899
- const submitTo = typeof submit === "object" ? submit.to : void 0;
900
- if (propsCopy.onFinish) {
901
- const originOnFinish = propsCopy.onFinish;
902
- propsCopy.onFinish = async (values) => {
945
+ if (props.faas?.action) {
946
+ setOnFinish(() => async (values) => {
947
+ if (!props.faas?.action) return;
903
948
  setLoading(true);
949
+ let submitValues = values;
904
950
  try {
905
- if (submitTo?.action) await originOnFinish(values, async (nextValues) => faas(submitTo.action, submitTo.params ? {
906
- ...nextValues,
907
- ...submitTo.params
908
- } : nextValues));
909
- else await originOnFinish(values);
951
+ if (props.faas?.transformValues) submitValues = await props.faas.transformValues(values);
952
+ const extraParams = typeof props.faas?.params === "function" ? props.faas.params(submitValues) : props.faas.params;
953
+ if (extraParams) submitValues = {
954
+ ...submitValues,
955
+ ...extraParams
956
+ };
957
+ const result = await faas(props.faas.action, submitValues);
958
+ props.faas.onSuccess?.(result, submitValues);
959
+ return result;
910
960
  } catch (error) {
911
- console.error(error);
961
+ props.faas.onError?.(error, submitValues);
962
+ throw error;
963
+ } finally {
964
+ props.faas.onFinally?.();
965
+ setLoading(false);
912
966
  }
913
- setLoading(false);
914
- };
915
- } else if (submitTo?.action) propsCopy.onFinish = async (values) => {
916
- setLoading(true);
917
- return faas(submitTo.action, submitTo.params ? {
918
- ...values,
919
- ...submitTo.params
920
- } : values).then((result) => {
921
- submitTo.then?.(result);
922
- return result;
923
- }).catch((error) => {
924
- submitTo.catch?.(error);
925
- return Promise.reject(error);
926
- }).finally(() => {
927
- submitTo.finally?.();
928
- setLoading(false);
929
967
  });
930
- };
931
- if (propsCopy.extendTypes) {
932
- setExtendTypes(propsCopy.extendTypes);
933
- delete propsCopy.extendTypes;
968
+ return;
934
969
  }
935
- setComputedProps(propsCopy);
936
- }, [form, props]);
970
+ setOnFinish(void 0);
971
+ }, [props.onFinish, props.faas]);
972
+ useEqualEffect(() => {
973
+ setExtendTypes(props.extendTypes);
974
+ }, [props.extendTypes]);
975
+ useEqualEffect(() => {
976
+ setSubmit(props.submit === false ? false : props.submit || {});
977
+ }, [props.submit]);
978
+ useEqualEffect(() => {
979
+ const nextInitialValues = props.initialValues ? JSON.parse(JSON.stringify(props.initialValues)) : Object.create(null);
980
+ for (const key in nextInitialValues) nextInitialValues[key] = transferValue(props.items?.find((item) => isFormItemProps(item) && item.id === key)?.type, nextInitialValues[key]);
981
+ if (props.items?.length) setItems(props.items.map((item) => {
982
+ if (!isFormItemProps(item) || !item.if) return item;
983
+ return {
984
+ ...item,
985
+ hidden: !item.if(nextInitialValues)
986
+ };
987
+ }));
988
+ else setItems([]);
989
+ if (props.initialValues) {
990
+ setInitialValues(nextInitialValues);
991
+ return;
992
+ }
993
+ setInitialValues(null);
994
+ }, [props.initialValues, props.items]);
995
+ useEqualEffect(() => {
996
+ const propsCopy = { ...props };
997
+ delete propsCopy.onFinish;
998
+ delete propsCopy.faas;
999
+ delete propsCopy.extendTypes;
1000
+ delete propsCopy.submit;
1001
+ delete propsCopy.items;
1002
+ delete propsCopy.initialValues;
1003
+ setAntdProps(propsCopy);
1004
+ }, [props]);
937
1005
  const onValuesChange = useEqualCallback((changedValues, allValues) => {
938
1006
  console.debug("Form:onValuesChange", changedValues, allValues);
939
1007
  if (props.onValuesChange) props.onValuesChange(changedValues, allValues);
940
- if (!props.items) return;
1008
+ if (!items.length) return;
941
1009
  for (const key in changedValues) {
942
- const item = computedProps?.items?.find((i) => isFormItemProps(i) && i.id === key);
1010
+ const item = items.find((i) => isFormItemProps(i) && i.id === key);
943
1011
  if (item?.onValueChange) item.onValueChange(changedValues[key], allValues, form);
944
1012
  }
945
- }, [computedProps]);
1013
+ }, [
1014
+ items,
1015
+ props.onValuesChange,
1016
+ form
1017
+ ]);
946
1018
  useEqualEffect(() => {
947
1019
  if (!initialValues) return;
948
1020
  console.debug("Form:initialValues", initialValues);
949
1021
  form.setFieldsValue(initialValues);
950
1022
  setInitialValues(null);
951
- }, [form, initialValues]);
952
- if (!computedProps) return null;
1023
+ }, [
1024
+ form,
1025
+ initialValues,
1026
+ items
1027
+ ]);
1028
+ if (!antdProps) return null;
1029
+ const submitButtonProps = typeof submit === "object" ? submit.buttonProps : void 0;
1030
+ const submitButtonLoading = loading ? true : submitButtonProps?.loading ?? false;
953
1031
  return /* @__PURE__ */ jsxs(Form$1, {
954
- ...computedProps,
1032
+ ...antdProps,
1033
+ form,
1034
+ onFinish,
955
1035
  onValuesChange,
956
1036
  children: [
957
- computedProps.beforeItems,
958
- computedProps.items?.map((item) => {
1037
+ props.beforeItems,
1038
+ items.map((item) => {
959
1039
  if (isFormItemProps(item)) return /* @__PURE__ */ jsx(FormItem, {
960
1040
  ...item,
961
1041
  ...extendTypes ? { extendTypes } : {}
962
1042
  }, item.id);
963
1043
  return item;
964
1044
  }),
965
- computedProps.children,
1045
+ props.children,
966
1046
  typeof submit !== "boolean" && /* @__PURE__ */ jsx(Button, {
1047
+ ...submitButtonProps,
967
1048
  htmlType: "submit",
968
- type: "primary",
969
- loading,
1049
+ type: submitButtonProps?.type || "primary",
1050
+ loading: submitButtonLoading,
970
1051
  children: submit?.text || config.theme.Form.submit.text
971
1052
  }),
972
- computedProps.footer
1053
+ props.footer
973
1054
  ]
974
1055
  });
975
1056
  }
@@ -1589,4 +1670,4 @@ function useThemeToken() {
1589
1670
  return theme.useToken().token;
1590
1671
  }
1591
1672
  //#endregion
1592
- export { App, Blank, ConfigContext, ConfigProvider, Description, Drawer, ErrorBoundary, FaasDataWrapper, FaasReactClient, Form, FormItem, Link, Loading, Modal, PageNotFound, Routes, Table, Tabs, Title, cloneUnionFaasItemElement, faas, idToTitle, lazy, transferOptions, transferValue, useApp, useConfigContext, useDrawer, useFaas, useModal, useThemeToken, withFaasData };
1673
+ export { App, AppContext, Blank, ConfigContext, ConfigProvider, Description, Drawer, ErrorBoundary, FaasDataWrapper, FaasReactClient, Form, FormItem, Link, Loading, Modal, PageNotFound, Routes, Table, Tabs, Title, cloneUnionFaasItemElement, faas, idToTitle, lazy, transferOptions, transferValue, useApp, useConfigContext, useDrawer, useFaas, useModal, useThemeToken, withFaasData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/ant-design",
3
- "version": "8.0.0-beta.15",
3
+ "version": "8.0.0-beta.17",
4
4
  "homepage": "https://faasjs.com/doc/ant-design",
5
5
  "bugs": {
6
6
  "url": "https://github.com/faasjs/faasjs/issues"
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "devDependencies": {
30
30
  "@ant-design/icons": "*",
31
- "@faasjs/react": ">=8.0.0-beta.15",
31
+ "@faasjs/react": ">=8.0.0-beta.17",
32
32
  "@types/lodash-es": "*",
33
33
  "@types/react": "^19.0.0",
34
34
  "@types/react-dom": "^19.0.0",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@ant-design/icons": "*",
43
- "@faasjs/react": ">=8.0.0-beta.15",
43
+ "@faasjs/react": ">=8.0.0-beta.17",
44
44
  "antd": "^6.0.0",
45
45
  "lodash-es": "*",
46
46
  "react": "^19.0.0",