@faasjs/ant-design 8.0.0-beta.2 → 8.0.0-beta.20

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.mjs CHANGED
@@ -1,899 +1,1342 @@
1
- import { createSplittingContext, FaasDataWrapper as FaasDataWrapper$1, withFaasData as withFaasData$1, useEqualEffect, FaasReactClient, useEqualCallback, ErrorBoundary as ErrorBoundary$1, OptionalWrapper, faas } from '@faasjs/react';
2
- export { FaasReactClient, faas, useFaas } from '@faasjs/react';
3
- import { Drawer as Drawer$1, Modal as Modal$1, Form as Form$1, Spin, message, notification, ConfigProvider as ConfigProvider$1, Typography, Descriptions, Input, Button, Row, Col, DatePicker, Switch, Select, InputNumber, Radio, Result, Skeleton, Table as Table$1, Tabs as Tabs$1, theme, Alert, Space } from 'antd';
4
- import { BrowserRouter, useNavigate, Routes as Routes$1, Route, useLocation } from 'react-router-dom';
5
- import { defaultsDeep, cloneDeep, isNil, uniqBy } from 'lodash-es';
6
- import { createContext, useState, useContext, cloneElement, isValidElement, createElement, useEffect, Suspense } from 'react';
7
- export { lazy } from 'react';
8
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
9
- import { PlusOutlined, MinusCircleOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons';
10
- import dayjs2 from 'dayjs';
11
-
12
- // src/App.tsx
1
+ import { ErrorBoundary as ErrorBoundary$1, FaasDataWrapper as FaasDataWrapper$1, FaasReactClient, OptionalWrapper, createSplittingContext, faas, useEqualCallback, useEqualEffect, useFaas, withFaasData as withFaasData$1 } from "@faasjs/react";
2
+ import { Alert, Button, Col, ConfigProvider as ConfigProvider$1, DatePicker, Descriptions, Drawer, Form as Form$1, Input, InputNumber, Modal, Radio, Result, Row, Select, Skeleton, Space, Spin, Switch, Table as Table$1, Tabs as Tabs$1, Typography, message, notification, theme } from "antd";
3
+ import { BrowserRouter, Route, Routes as Routes$1, useLocation, useNavigate } from "react-router-dom";
4
+ import { cloneDeep, defaultsDeep, isNil, uniqBy } from "lodash-es";
5
+ import { Suspense, cloneElement, createContext, createElement, isValidElement, lazy, useCallback, useContext, useState } from "react";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { CheckOutlined, CloseOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
8
+ import dayjs from "dayjs";
9
+ //#region src/Loading.tsx
10
+ /**
11
+ * Render an Ant Design loading spinner with an optional content fallback.
12
+ *
13
+ * @param {LoadingProps} props - Loading indicator props and optional wrapped children.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * import { Loading } from '@faasjs/ant-design'
18
+ *
19
+ * export function Page({ remoteData }: { remoteData?: string }) {
20
+ * return (
21
+ * <>
22
+ * <Loading />
23
+ * <Loading loading={!remoteData}>
24
+ * <div>{remoteData}</div>
25
+ * </Loading>
26
+ * </>
27
+ * )
28
+ * }
29
+ * ```
30
+ */
13
31
  function Loading(props) {
14
- if (props.loading === false) return /* @__PURE__ */ jsx(Fragment, { children: props.children });
15
- return /* @__PURE__ */ jsx(
16
- "div",
17
- {
18
- style: {
19
- ...props.style || {},
20
- ...!props.size || props.size === "large" ? {
21
- margin: "20vh auto",
22
- textAlign: "center"
23
- } : {}
24
- },
25
- children: /* @__PURE__ */ jsx(Spin, { size: props.size || "large" })
26
- }
27
- );
32
+ if (props.loading === false) return /* @__PURE__ */ jsx(Fragment, { children: props.children });
33
+ return /* @__PURE__ */ jsx("div", {
34
+ style: {
35
+ ...props.style,
36
+ ...!props.size || props.size === "large" ? {
37
+ margin: "20vh auto",
38
+ textAlign: "center"
39
+ } : {}
40
+ },
41
+ children: /* @__PURE__ */ jsx(Spin, { size: props.size || "large" })
42
+ });
28
43
  }
29
- Loading.whyDidYouRender = true;
44
+ //#endregion
45
+ //#region src/FaasDataWrapper.tsx
46
+ /**
47
+ * Render the `@faasjs/react` data wrapper with an Ant Design loading fallback.
48
+ *
49
+ * When `loading` is not provided, the component renders {@link Loading} with `loadingProps` while
50
+ * the wrapped FaasJS request is pending.
51
+ *
52
+ * @template T - Action path or response data type used for inference.
53
+ * @param {FaasDataWrapperProps<T>} props - Wrapper props including loading fallbacks and request configuration.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * import { Alert, Button } from 'antd'
58
+ * import { FaasDataWrapper } from '@faasjs/ant-design'
59
+ *
60
+ * type User = {
61
+ * name: string
62
+ * }
63
+ *
64
+ * function UserView(props: {
65
+ * data?: User
66
+ * error?: Error
67
+ * reload?: () => void
68
+ * }) {
69
+ * if (props.error) {
70
+ * return (
71
+ * <Alert
72
+ * type="error"
73
+ * message={props.error.message}
74
+ * action={
75
+ * <Button size="small" onClick={() => props.reload?.()}>
76
+ * Retry
77
+ * </Button>
78
+ * }
79
+ * />
80
+ * )
81
+ * }
82
+ *
83
+ * return <div>Hello, {props.data?.name}</div>
84
+ * }
85
+ *
86
+ * // Render-prop mode
87
+ * export function UserProfile(props: { id: number }) {
88
+ * return (
89
+ * <FaasDataWrapper<User>
90
+ * action="user/get"
91
+ * params={{ id: props.id }}
92
+ * loading={<div>Loading user...</div>}
93
+ * render={({ data, error, reload }) => {
94
+ * if (error) {
95
+ * return (
96
+ * <Alert
97
+ * type="error"
98
+ * message={error.message}
99
+ * action={
100
+ * <Button size="small" onClick={() => reload()}>
101
+ * Retry
102
+ * </Button>
103
+ * }
104
+ * />
105
+ * )
106
+ * }
107
+ *
108
+ * return <div>Hello, {data.name}</div>
109
+ * }}
110
+ * />
111
+ * )
112
+ * }
113
+ *
114
+ * // Children injection mode
115
+ * export function UserProfileWithChildren(props: { id: number }) {
116
+ * return (
117
+ * <FaasDataWrapper<User>
118
+ * action="user/get"
119
+ * params={{ id: props.id }}
120
+ * loading={<div>Loading user...</div>}
121
+ * >
122
+ * <UserView />
123
+ * </FaasDataWrapper>
124
+ * )
125
+ * }
126
+ * ```
127
+ */
30
128
  function FaasDataWrapper(props) {
31
- return /* @__PURE__ */ jsx(
32
- FaasDataWrapper$1,
33
- {
34
- fallback: props.loading || /* @__PURE__ */ jsx(Loading, { ...props.loadingProps }),
35
- ...props
36
- }
37
- );
129
+ return /* @__PURE__ */ jsx(FaasDataWrapper$1, {
130
+ fallback: props.loading || /* @__PURE__ */ jsx(Loading, { ...props.loadingProps }),
131
+ ...props
132
+ });
38
133
  }
39
- FaasDataWrapper.whyDidYouRender = true;
134
+ /**
135
+ * Wrap a component with {@link FaasDataWrapper} and its Ant Design loading fallback.
136
+ *
137
+ * @template PathOrData - Action path or response data type used for inference.
138
+ * @template TComponentProps - Component props including injected Faas data fields.
139
+ * @param {React.FC<TComponentProps & Record<string, any>>} Component - Component that consumes injected Faas data props.
140
+ * @param {FaasDataWrapperProps<PathOrData>} faasProps - Request configuration forwarded to {@link FaasDataWrapper}.
141
+ * @returns Higher-order component that injects Faas data props.
142
+ *
143
+ * @example
144
+ * ```tsx
145
+ * import { withFaasData } from '@faasjs/ant-design'
146
+ *
147
+ * const UserCard = withFaasData(
148
+ * ({ data, error, reload }) =>
149
+ * error ? (
150
+ * <a onClick={() => reload()}>Retry</a>
151
+ * ) : (
152
+ * <div>{data.name}</div>
153
+ * ),
154
+ * { action: 'user/get', params: { id: 1 } },
155
+ * )
156
+ * ```
157
+ */
40
158
  function withFaasData(Component, faasProps) {
41
- return withFaasData$1(Component, {
42
- fallback: faasProps.loading || /* @__PURE__ */ jsx(Loading, { ...faasProps.loadingProps }),
43
- ...faasProps
44
- });
159
+ return withFaasData$1(Component, {
160
+ fallback: faasProps.loading || /* @__PURE__ */ jsx(Loading, { ...faasProps.loadingProps }),
161
+ ...faasProps
162
+ });
45
163
  }
46
- var zh = {
47
- lang: "zh",
48
- blank: "\u7A7A",
49
- all: "\u5168\u90E8",
50
- submit: "\u63D0\u4EA4",
51
- pageNotFound: "\u9875\u9762\u672A\u627E\u5230",
52
- add: "\u6DFB\u52A0",
53
- delete: "\u5220\u9664",
54
- required: "\u5FC5\u586B",
55
- search: "\u641C\u7D22",
56
- reset: "\u91CD\u7F6E"
164
+ //#endregion
165
+ //#region src/Config.tsx
166
+ const zh = {
167
+ lang: "zh",
168
+ blank: "",
169
+ all: "全部",
170
+ submit: "提交",
171
+ pageNotFound: "页面未找到",
172
+ add: "添加",
173
+ delete: "删除",
174
+ required: "必填",
175
+ search: "搜索",
176
+ reset: "重置"
57
177
  };
58
- var en = {
59
- lang: "en",
60
- blank: "Empty",
61
- all: "All",
62
- submit: "Submit",
63
- pageNotFound: "Page Not Found",
64
- add: "Add",
65
- delete: "Delete",
66
- required: "is required",
67
- search: "Search",
68
- reset: "Reset"
178
+ const en = {
179
+ lang: "en",
180
+ blank: "Empty",
181
+ all: "All",
182
+ submit: "Submit",
183
+ pageNotFound: "Page Not Found",
184
+ add: "Add",
185
+ delete: "Delete",
186
+ required: "is required",
187
+ search: "Search",
188
+ reset: "Reset"
69
189
  };
70
- var baseTheme = {
71
- lang: "en",
72
- common: en,
73
- Blank: { text: en.blank },
74
- Form: { submit: { text: en.submit } },
75
- Title: {
76
- separator: " - ",
77
- suffix: ""
78
- },
79
- Link: { style: {} }
190
+ const baseTheme = {
191
+ lang: "en",
192
+ common: en,
193
+ Blank: { text: en.blank },
194
+ Form: { submit: { text: en.submit } },
195
+ Title: {
196
+ separator: " - ",
197
+ suffix: ""
198
+ },
199
+ Link: { style: {} }
80
200
  };
81
- var ConfigContext = createContext({
82
- theme: baseTheme
83
- });
201
+ /**
202
+ * React context storing the resolved FaasJS Ant Design theme.
203
+ */
204
+ const ConfigContext = createContext({ theme: baseTheme });
205
+ /**
206
+ * Provide theme overrides and optional FaasJS client initialization for descendants.
207
+ *
208
+ * Theme overrides are merged with the built-in defaults. When `theme.lang` is omitted, the
209
+ * provider infers a default language from `navigator.language`.
210
+ *
211
+ * @param {ConfigProviderProps} props - Theme overrides and optional FaasJS client configuration.
212
+ *
213
+ * @example
214
+ * ```tsx
215
+ * import { Blank, ConfigProvider } from '@faasjs/ant-design'
216
+ *
217
+ * export function Page() {
218
+ * return (
219
+ * <ConfigProvider theme={{ common: { blank: 'Empty' } }}>
220
+ * <Blank />
221
+ * </ConfigProvider>
222
+ * )
223
+ * }
224
+ * ```
225
+ */
84
226
  function ConfigProvider(props) {
85
- const [theme2, setTheme] = useState();
86
- useEqualEffect(() => {
87
- const lang = props.theme?.lang || (!props.theme?.lang && /^zh/i.test(navigator.language) ? "zh" : "en");
88
- if (lang === "zh") {
89
- setTheme(
90
- defaultsDeep(
91
- props.theme,
92
- {
93
- lang: "zh",
94
- common: zh,
95
- Blank: { text: zh.blank },
96
- Form: { submit: { text: zh.submit } }
97
- },
98
- baseTheme
99
- )
100
- );
101
- } else setTheme(defaultsDeep(props.theme, baseTheme));
102
- if (props.faasClientOptions) FaasReactClient(props.faasClientOptions);
103
- }, [props.theme]);
104
- if (!theme2) return null;
105
- return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: { theme: theme2 }, children: props.children });
227
+ const [theme, setTheme] = useState();
228
+ useEqualEffect(() => {
229
+ if ((props.theme?.lang || (!props.theme?.lang && /^zh/i.test(navigator.language) ? "zh" : "en")) === "zh") setTheme(defaultsDeep(props.theme, {
230
+ lang: "zh",
231
+ common: zh,
232
+ Blank: { text: zh.blank },
233
+ Form: { submit: { text: zh.submit } }
234
+ }, baseTheme));
235
+ else setTheme(defaultsDeep(props.theme, baseTheme));
236
+ if (props.faasClientOptions) FaasReactClient(props.faasClientOptions);
237
+ }, [props.theme]);
238
+ if (!theme) return null;
239
+ return /* @__PURE__ */ jsx(ConfigContext.Provider, {
240
+ value: { theme },
241
+ children: props.children
242
+ });
106
243
  }
244
+ /**
245
+ * Read the current `@faasjs/ant-design` config context.
246
+ *
247
+ * @returns Current config context value containing the resolved theme.
248
+ *
249
+ * @example
250
+ * ```tsx
251
+ * import { Blank, ConfigProvider, useConfigContext } from '@faasjs/ant-design'
252
+ *
253
+ * function EmptyState() {
254
+ * const { theme } = useConfigContext()
255
+ *
256
+ * return <span>{theme.common.blank}</span>
257
+ * }
258
+ *
259
+ * export function Page() {
260
+ * return (
261
+ * <ConfigProvider theme={{ common: { blank: 'N/A' } }}>
262
+ * <EmptyState />
263
+ * </ConfigProvider>
264
+ * )
265
+ * }
266
+ * ```
267
+ */
107
268
  function useConfigContext() {
108
- return useContext(ConfigContext);
269
+ return useContext(ConfigContext);
109
270
  }
110
- var Drawer = Drawer$1;
111
- Drawer.whyDidYouRender = true;
271
+ //#endregion
272
+ //#region src/Drawer.tsx
273
+ /**
274
+ * Create a hook-managed Ant Design drawer instance.
275
+ *
276
+ * The returned setter merges partial updates into the current drawer props instead of replacing the
277
+ * entire state object.
278
+ *
279
+ * @param {DrawerProps} [init] - Initial drawer props.
280
+ * @returns Hook-managed drawer element, current props, and a state-merging setter.
281
+ *
282
+ * @example
283
+ * ```tsx
284
+ * import { useDrawer } from '@faasjs/ant-design'
285
+ * import { Button } from 'antd'
286
+ *
287
+ * function Example() {
288
+ * const { drawer, setDrawerProps } = useDrawer()
289
+ *
290
+ * return (
291
+ * <>
292
+ * <Button onClick={() => setDrawerProps({ open: true, title: 'Details', children: <div>Content</div> })}>
293
+ * Open
294
+ * </Button>
295
+ * {drawer}
296
+ * </>
297
+ * )
298
+ * }
299
+ * ```
300
+ */
112
301
  function useDrawer(init) {
113
- const [props, setProps] = useState({
114
- open: false,
115
- ...init
116
- });
117
- const setDrawerProps = useEqualCallback(
118
- (changes) => {
119
- const changed = typeof changes === "function" ? changes(props) : changes;
120
- setProps((prev) => ({ ...prev, ...changed }));
121
- },
122
- [setProps]
123
- );
124
- return {
125
- drawer: /* @__PURE__ */ jsx(
126
- Drawer,
127
- {
128
- onClose: () => setProps((prev) => ({
129
- ...prev,
130
- open: false
131
- })),
132
- ...props
133
- }
134
- ),
135
- drawerProps: props,
136
- setDrawerProps
137
- };
302
+ const [props, setProps] = useState({
303
+ open: false,
304
+ ...init
305
+ });
306
+ const setDrawerProps = useEqualCallback((changes) => {
307
+ const changed = typeof changes === "function" ? changes(props) : changes;
308
+ setProps((prev) => ({
309
+ ...prev,
310
+ ...changed
311
+ }));
312
+ }, [setProps]);
313
+ return {
314
+ drawer: /* @__PURE__ */ jsx(Drawer, {
315
+ onClose: () => setProps((prev) => ({
316
+ ...prev,
317
+ open: false
318
+ })),
319
+ ...props
320
+ }),
321
+ drawerProps: props,
322
+ setDrawerProps
323
+ };
138
324
  }
325
+ //#endregion
326
+ //#region src/ErrorBoundary.tsx
139
327
  function ErrorChildren(props) {
140
- return /* @__PURE__ */ jsx(
141
- Alert,
142
- {
143
- type: "error",
144
- message: props.errorMessage,
145
- description: /* @__PURE__ */ jsx(
146
- "pre",
147
- {
148
- style: {
149
- fontSize: "0.9em",
150
- overflowX: "auto"
151
- },
152
- children: props.errorDescription
153
- }
154
- )
155
- }
156
- );
328
+ return /* @__PURE__ */ jsx(Alert, {
329
+ type: "error",
330
+ message: props.errorMessage,
331
+ description: /* @__PURE__ */ jsx("pre", {
332
+ style: {
333
+ fontSize: "0.9em",
334
+ overflowX: "auto"
335
+ },
336
+ children: props.errorDescription
337
+ })
338
+ });
157
339
  }
340
+ /**
341
+ * Styled error boundary.
342
+ *
343
+ * When `errorChildren` is not provided, the fallback UI renders an Ant Design `Alert` containing
344
+ * the captured error message and description.
345
+ *
346
+ * @param {ErrorBoundaryProps} props - Error boundary props forwarded to the underlying React implementation.
347
+ *
348
+ * @example
349
+ * ```tsx
350
+ * import { ErrorBoundary } from '@faasjs/ant-design'
351
+ *
352
+ * export function Page() {
353
+ * return (
354
+ * <ErrorBoundary>
355
+ * <DangerousWidget />
356
+ * </ErrorBoundary>
357
+ * )
358
+ * }
359
+ * ```
360
+ */
158
361
  function ErrorBoundary(props) {
159
- return /* @__PURE__ */ jsx(ErrorBoundary$1, { errorChildren: /* @__PURE__ */ jsx(ErrorChildren, {}), ...props });
362
+ return /* @__PURE__ */ jsx(ErrorBoundary$1, {
363
+ errorChildren: /* @__PURE__ */ jsx(ErrorChildren, {}),
364
+ ...props
365
+ });
160
366
  }
161
- ErrorBoundary.whyDidYouRender = true;
162
- var Modal = Modal$1;
163
- Modal.whyDidYouRender = true;
367
+ //#endregion
368
+ //#region src/Modal.tsx
369
+ /**
370
+ * Create a hook-managed Ant Design modal instance.
371
+ *
372
+ * The returned setter merges partial updates into the current modal props instead of replacing the
373
+ * entire state object.
374
+ *
375
+ * @param {ModalProps} [init] - Initial modal props.
376
+ * @returns Hook-managed modal element, current props, and a state-merging setter.
377
+ *
378
+ * @example
379
+ * ```tsx
380
+ * import { useModal } from '@faasjs/ant-design'
381
+ * import { Button } from 'antd'
382
+ *
383
+ * function Example() {
384
+ * const { modal, setModalProps } = useModal()
385
+ *
386
+ * return (
387
+ * <>
388
+ * <Button onClick={() => setModalProps({ open: true, title: 'Delete', children: 'Are you sure?' })}>
389
+ * Open Modal
390
+ * </Button>
391
+ * {modal}
392
+ * </>
393
+ * )
394
+ * }
395
+ * ```
396
+ */
164
397
  function useModal(init) {
165
- const [props, setProps] = useState({ open: false, ...init });
166
- const setModalProps = useEqualCallback(
167
- (changes) => {
168
- const changed = typeof changes === "function" ? changes(props) : changes;
169
- setProps((prev) => ({ ...prev, ...changed }));
170
- },
171
- [setProps]
172
- );
173
- return {
174
- modal: /* @__PURE__ */ jsx(
175
- Modal,
176
- {
177
- onCancel: () => setProps((prev) => ({
178
- ...prev,
179
- open: false
180
- })),
181
- ...props
182
- }
183
- ),
184
- modalProps: props,
185
- setModalProps
186
- };
398
+ const [props, setProps] = useState({
399
+ open: false,
400
+ ...init
401
+ });
402
+ const setModalProps = useEqualCallback((changes) => {
403
+ const changed = typeof changes === "function" ? changes(props) : changes;
404
+ setProps((prev) => ({
405
+ ...prev,
406
+ ...changed
407
+ }));
408
+ }, [setProps]);
409
+ return {
410
+ modal: /* @__PURE__ */ jsx(Modal, {
411
+ onCancel: () => setProps((prev) => ({
412
+ ...prev,
413
+ open: false
414
+ })),
415
+ ...props
416
+ }),
417
+ modalProps: props,
418
+ setModalProps
419
+ };
187
420
  }
188
- var AppContext = createSplittingContext([
189
- "message",
190
- "notification",
191
- "modalProps",
192
- "setModalProps",
193
- "drawerProps",
194
- "setDrawerProps"
421
+ //#endregion
422
+ //#region src/useApp.ts
423
+ /**
424
+ * Shared context storing message, notification, modal, and drawer helpers.
425
+ */
426
+ const AppContext = createSplittingContext([
427
+ "message",
428
+ "notification",
429
+ "modalProps",
430
+ "setModalProps",
431
+ "drawerProps",
432
+ "setDrawerProps"
195
433
  ]);
434
+ /**
435
+ * Read app-level services exposed by the root `App` component.
436
+ *
437
+ * @template NewT - Narrowed app context shape to read from `AppContext`.
438
+ * @returns Read-only app context value.
439
+ *
440
+ * @example
441
+ * ```tsx
442
+ * import { App, useApp } from '@faasjs/ant-design'
443
+ * import { Button } from 'antd'
444
+ *
445
+ * function Page() {
446
+ * const { message, setModalProps } = useApp()
447
+ *
448
+ * return (
449
+ * <Button
450
+ * onClick={() => {
451
+ * message.success('Saved')
452
+ * setModalProps({ open: true, title: 'Done', children: 'Profile updated.' })
453
+ * }}
454
+ * >
455
+ * Save
456
+ * </Button>
457
+ * )
458
+ * }
459
+ *
460
+ * export function Root() {
461
+ * return (
462
+ * <App>
463
+ * <Page />
464
+ * </App>
465
+ * )
466
+ * }
467
+ * ```
468
+ */
469
+ function useApp() {
470
+ return AppContext.use();
471
+ }
472
+ //#endregion
473
+ //#region src/App.tsx
196
474
  function RoutesApp(props) {
197
- const location = useLocation();
198
- const { drawerProps, setDrawerProps, modalProps, setModalProps } = useApp();
199
- useEqualEffect(() => {
200
- console.debug("location", location);
201
- if (drawerProps.open) setDrawerProps({ open: false });
202
- if (modalProps.open) setModalProps({ open: false });
203
- }, [location]);
204
- return /* @__PURE__ */ jsx(Fragment, { children: props.children });
475
+ const location = useLocation();
476
+ const { drawerProps, setDrawerProps, modalProps, setModalProps } = useApp();
477
+ useEqualEffect(() => {
478
+ console.debug("location", location);
479
+ if (drawerProps.open) setDrawerProps({ open: false });
480
+ if (modalProps.open) setModalProps({ open: false });
481
+ }, [location]);
482
+ return /* @__PURE__ */ jsx(Fragment, { children: props.children });
205
483
  }
484
+ /**
485
+ * Render the root provider shell for a FaasJS Ant Design application.
486
+ *
487
+ * `App` initializes Ant Design message and notification APIs, exposes hook-managed modal and
488
+ * drawer state through {@link AppContext}, wraps descendants with {@link ErrorBoundary}, and
489
+ * optionally mounts React Router's `BrowserRouter`.
490
+ *
491
+ * @param {AppProps} props - App shell props including providers, routing, and error handling options.
492
+ *
493
+ * @example
494
+ * ```tsx
495
+ * import { App } from '@faasjs/ant-design'
496
+ *
497
+ * export default function Page() {
498
+ * return (
499
+ * <App
500
+ * configProviderProps={{}}
501
+ * browserRouterProps={{}}
502
+ * errorBoundaryProps={{}}
503
+ * faasConfigProviderProps={{}}
504
+ * >
505
+ * <div>content</div>
506
+ * </App>
507
+ * )
508
+ * }
509
+ * ```
510
+ */
206
511
  function App(props) {
207
- const [messageApi, messageContextHolder] = message.useMessage();
208
- const [notificationApi, notificationContextHolder] = notification.useNotification();
209
- const { modal, modalProps, setModalProps } = useModal();
210
- const { drawer, drawerProps, setDrawerProps } = useDrawer();
211
- return /* @__PURE__ */ jsx(
212
- OptionalWrapper,
213
- {
214
- condition: !!props.configProviderProps,
215
- Wrapper: ConfigProvider$1,
216
- wrapperProps: props.configProviderProps,
217
- children: /* @__PURE__ */ jsx(
218
- AppContext.Provider,
219
- {
220
- value: {
221
- message: messageApi,
222
- notification: notificationApi,
223
- drawerProps,
224
- setDrawerProps,
225
- modalProps,
226
- setModalProps
227
- },
228
- children: /* @__PURE__ */ jsx(
229
- ConfigProvider,
230
- {
231
- ...props.faasConfigProviderProps,
232
- faasClientOptions: {
233
- onError: (action) => async (res) => {
234
- if ("message" in res && res.toString().includes("AbortError"))
235
- return;
236
- console.error(`[FaasJS][${action}]`, res);
237
- messageApi.error("message" in res ? res.message : "Unknown error");
238
- },
239
- ...props.faasConfigProviderProps ? props.faasConfigProviderProps.faasClientOptions : {}
240
- },
241
- children: /* @__PURE__ */ jsx(ErrorBoundary, { ...props.errorBoundaryProps, children: /* @__PURE__ */ jsxs(
242
- OptionalWrapper,
243
- {
244
- condition: typeof document !== "undefined" && props.browserRouterProps !== false,
245
- Wrapper: BrowserRouter,
246
- wrapperProps: props.browserRouterProps,
247
- children: [
248
- messageContextHolder,
249
- notificationContextHolder,
250
- modal,
251
- drawer,
252
- props.browserRouterProps !== false ? /* @__PURE__ */ jsx(RoutesApp, { children: props.children }) : props.children
253
- ]
254
- }
255
- ) })
256
- }
257
- )
258
- }
259
- )
260
- }
261
- );
512
+ const [messageApi, messageContextHolder] = message.useMessage();
513
+ const [notificationApi, notificationContextHolder] = notification.useNotification();
514
+ const { modal, modalProps, setModalProps } = useModal({ destroyOnHidden: true });
515
+ const { drawer, drawerProps, setDrawerProps } = useDrawer({ destroyOnHidden: true });
516
+ return /* @__PURE__ */ jsx(OptionalWrapper, {
517
+ condition: !!props.configProviderProps,
518
+ Wrapper: ConfigProvider$1,
519
+ wrapperProps: props.configProviderProps,
520
+ children: /* @__PURE__ */ jsx(AppContext.Provider, {
521
+ value: {
522
+ message: messageApi,
523
+ notification: notificationApi,
524
+ drawerProps,
525
+ setDrawerProps,
526
+ modalProps,
527
+ setModalProps
528
+ },
529
+ children: /* @__PURE__ */ jsx(ConfigProvider, {
530
+ ...props.faasConfigProviderProps,
531
+ faasClientOptions: {
532
+ onError: (action) => async (res) => {
533
+ if ("message" in res && res.toString().includes("AbortError")) return;
534
+ console.error(`[FaasJS][${action}]`, res);
535
+ messageApi.error("message" in res ? res.message : "Unknown error");
536
+ },
537
+ ...props.faasConfigProviderProps ? props.faasConfigProviderProps.faasClientOptions : {}
538
+ },
539
+ children: /* @__PURE__ */ jsx(ErrorBoundary, {
540
+ ...props.errorBoundaryProps,
541
+ children: /* @__PURE__ */ jsxs(OptionalWrapper, {
542
+ condition: typeof document !== "undefined" && props.browserRouterProps !== false,
543
+ Wrapper: BrowserRouter,
544
+ wrapperProps: props.browserRouterProps,
545
+ children: [
546
+ messageContextHolder,
547
+ notificationContextHolder,
548
+ modal,
549
+ drawer,
550
+ props.browserRouterProps !== false ? /* @__PURE__ */ jsx(RoutesApp, { children: props.children }) : props.children
551
+ ]
552
+ })
553
+ })
554
+ })
555
+ })
556
+ });
262
557
  }
263
- var useApp = AppContext.use;
264
- App.useApp = useApp;
265
- App.whyDidYouRender = true;
558
+ //#endregion
559
+ //#region src/Blank.tsx
560
+ /**
561
+ * Render a disabled placeholder when a value is empty.
562
+ *
563
+ * Empty values include `undefined`, `null`, empty strings, and empty arrays.
564
+ *
565
+ * @param {BlankProps} [options] - Placeholder text and value to render.
566
+ * @returns Rendered value or the configured placeholder text.
567
+ *
568
+ * @example
569
+ * ```tsx
570
+ * import { Blank } from '@faasjs/ant-design'
571
+ *
572
+ * export function FieldPreview() {
573
+ * return <Blank value={undefined} text="Empty" />
574
+ * }
575
+ * ```
576
+ */
266
577
  function Blank(options) {
267
- const { theme: theme2 } = useConfigContext();
268
- return !options || options.value === void 0 || options.value === null || Array.isArray(options.value) && !options.value.length || options.value === "" ? /* @__PURE__ */ jsx(Typography.Text, { disabled: true, children: options?.text || theme2.Blank.text }) : options.value;
578
+ const { theme } = useConfigContext();
579
+ return !options || options.value === void 0 || options.value === null || Array.isArray(options.value) && !options.value.length || options.value === "" ? /* @__PURE__ */ jsx(Typography.Text, {
580
+ disabled: true,
581
+ children: options?.text || theme.Blank.text
582
+ }) : options.value;
269
583
  }
270
- Blank.whyDidYouRender = true;
584
+ //#endregion
585
+ //#region src/data.ts
586
+ /**
587
+ * Convert a snake_case, kebab-case, or spaced identifier into a title-style label.
588
+ *
589
+ * @param {string | number} id - Identifier to convert.
590
+ * @returns Generated label string.
591
+ *
592
+ * @example
593
+ * ```ts
594
+ * idToTitle('example_id') // 'Example Id'
595
+ * ```
596
+ */
271
597
  function idToTitle(id) {
272
- if (typeof id === "number") return id.toString();
273
- const splitted = id.split(/(\s|_|-)/).filter((word) => !/(\s|_|-)/.test(word)).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
274
- return splitted.charAt(0).toUpperCase() + splitted.slice(1);
598
+ if (typeof id === "number") return id.toString();
599
+ const splitted = id.split(/(\s|_|-)/).filter((word) => !/(\s|_|-)/.test(word)).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
600
+ return splitted.charAt(0).toUpperCase() + splitted.slice(1);
275
601
  }
602
+ /**
603
+ * Normalize primitive options into explicit `{ label, value }` objects.
604
+ *
605
+ * String and number options are converted with {@link idToTitle}, while pre-shaped option objects
606
+ * are returned as-is.
607
+ *
608
+ * @param {BaseOption[]} options - Raw option list to normalize.
609
+ * @returns Normalized option list.
610
+ *
611
+ * @example
612
+ * ```ts
613
+ * import { transferOptions } from '@faasjs/ant-design'
614
+ *
615
+ * transferOptions(['draft', { label: 'Published', value: 'published' }])
616
+ * // [
617
+ * // { label: 'Draft', value: 'draft' },
618
+ * // { label: 'Published', value: 'published' },
619
+ * // ]
620
+ * ```
621
+ */
276
622
  function transferOptions(options) {
277
- if (!options) return [];
278
- return options.map(
279
- (item) => typeof item === "object" ? item : {
280
- label: idToTitle(item.toString()),
281
- value: item
282
- }
283
- );
623
+ if (!options) return [];
624
+ return options.map((item) => typeof item === "object" ? item : {
625
+ label: idToTitle(item.toString()),
626
+ value: item
627
+ });
284
628
  }
629
+ /**
630
+ * Normalize raw values into the runtime shape expected by FaasJS Ant Design components.
631
+ *
632
+ * Primitive strings such as `'null'` and `'undefined'` become `null`, comma-delimited array
633
+ * strings are split into arrays, and date or time values are converted to `dayjs` objects.
634
+ *
635
+ * @param {FaasItemType | null | undefined} type - Target field type.
636
+ * @param {any} value - Raw value to normalize.
637
+ * @returns Normalized value for rendering or form initialization.
638
+ *
639
+ * @example
640
+ * ```ts
641
+ * import { transferValue } from '@faasjs/ant-design'
642
+ *
643
+ * transferValue('number', '42') // 42
644
+ * transferValue('boolean', 'true') // true
645
+ * transferValue('string[]', 'a,b') // ['a', 'b']
646
+ * ```
647
+ */
285
648
  function transferValue(type, value) {
286
- if (!type) type = "string";
287
- if (!type.endsWith("[]") && (typeof value === "undefined" || value === null || value === "" || value === "null" || value === "undefined"))
288
- return null;
289
- if (type.endsWith("[]")) {
290
- if (!value) value = [];
291
- if (typeof value === "string") value = value.split(",").filter(Boolean);
292
- if (!Array.isArray(value)) value = [value];
293
- value = value.map(
294
- (item) => transferValue(type.replace("[]", ""), item)
295
- );
296
- }
297
- switch (type) {
298
- case "boolean":
299
- if (typeof value === "string") value = value === "true";
300
- break;
301
- case "number":
302
- if (typeof value === "string") value = Number(value);
303
- break;
304
- case "date":
305
- case "time":
306
- if (typeof value === "number" && value.toString().length === 10)
307
- value = value * 1e3;
308
- if (!dayjs2.isDayjs(value)) value = dayjs2(value);
309
- break;
310
- }
311
- return value;
649
+ if (!type) type = "string";
650
+ if (!type.endsWith("[]") && (typeof value === "undefined" || value === null || value === "" || value === "null" || value === "undefined")) return null;
651
+ if (type.endsWith("[]")) {
652
+ if (!value) value = [];
653
+ if (typeof value === "string") value = value.split(",").filter(Boolean);
654
+ if (!Array.isArray(value)) value = [value];
655
+ value = value.map((item) => transferValue(type.replace("[]", ""), item));
656
+ }
657
+ switch (type) {
658
+ case "boolean":
659
+ if (typeof value === "string") value = value === "true";
660
+ break;
661
+ case "number":
662
+ if (typeof value === "string") value = Number(value);
663
+ break;
664
+ case "date":
665
+ case "time":
666
+ if (typeof value === "number" && value.toString().length === 10) value = value * 1e3;
667
+ if (!dayjs.isDayjs(value)) value = dayjs(value);
668
+ break;
669
+ }
670
+ return value;
312
671
  }
672
+ /**
673
+ * Clone a {@link UnionFaasItemElement} with FaasJS injection props.
674
+ *
675
+ * React elements are cloned directly, while component references are first wrapped with
676
+ * `createElement`.
677
+ *
678
+ * @param {UnionFaasItemElement} element - Element or component to clone.
679
+ * @param {any} props - Injection props such as `scene`, `value`, `values`, and `index`.
680
+ * @returns Cloned React element ready for rendering.
681
+ *
682
+ * @example
683
+ * ```tsx
684
+ * import { cloneUnionFaasItemElement, type UnionFaasItemElement } from '@faasjs/ant-design'
685
+ *
686
+ * const Cell: UnionFaasItemElement<string> = ({ value }) => <span>{value}</span>
687
+ *
688
+ * const element = cloneUnionFaasItemElement(Cell, {
689
+ * scene: 'table',
690
+ * value: 'Hello',
691
+ * index: 0,
692
+ * })
693
+ * ```
694
+ */
313
695
  function cloneUnionFaasItemElement(element, props) {
314
- return cloneElement(
315
- isValidElement(element) ? element : createElement(element),
316
- props
317
- );
696
+ return cloneElement(isValidElement(element) ? element : createElement(element), props);
318
697
  }
698
+ //#endregion
699
+ //#region src/Description.tsx
319
700
  function DescriptionItemContent(props) {
320
- const [computedProps, setComputedProps] = useState();
321
- useEffect(() => {
322
- const propsCopy = { ...props };
323
- propsCopy.item.title = propsCopy.item.title ?? idToTitle(propsCopy.item.id);
324
- if (!propsCopy.item.type) propsCopy.item.type = "string";
325
- if (propsCopy.item.options?.length) {
326
- propsCopy.item.options = transferOptions(propsCopy.item.options);
327
- }
328
- propsCopy.value = transferValue(propsCopy.item.type, propsCopy.value);
329
- if (propsCopy.item.options && propsCopy.value !== null) {
330
- if (propsCopy.item.type.endsWith("[]"))
331
- propsCopy.value = propsCopy.value.map(
332
- (v) => propsCopy.item.options.find((option) => option.value === v)?.label || v
333
- );
334
- else if (["string", "number", "boolean"].includes(propsCopy.item.type))
335
- propsCopy.value = props.item.options.find((option) => option.value === props.value)?.label || props.value;
336
- }
337
- setComputedProps(propsCopy);
338
- }, [props]);
339
- if (!computedProps) return null;
340
- if (computedProps.item.descriptionChildren === null || computedProps.item.children === null || computedProps.item.descriptionRender === null || computedProps.item.render === null)
341
- return null;
342
- const children = computedProps.item.descriptionChildren || computedProps.item.children;
343
- if (children)
344
- return cloneUnionFaasItemElement(children, {
345
- scene: "description",
346
- value: computedProps.value,
347
- values: computedProps.values,
348
- index: 0
349
- });
350
- const render = computedProps.item.descriptionRender || computedProps.item.render;
351
- if (render)
352
- return /* @__PURE__ */ jsx(Fragment, { children: render(computedProps.value, computedProps.values, 0, "description") });
353
- if (computedProps.extendTypes?.[computedProps.item.type]) {
354
- if (computedProps.extendTypes[computedProps.item.type].children)
355
- return cloneUnionFaasItemElement(
356
- computedProps.extendTypes[computedProps.item.type].children,
357
- {
358
- scene: "description",
359
- value: computedProps.value,
360
- values: computedProps.values
361
- }
362
- );
363
- if (computedProps.extendTypes[computedProps.item.type].render)
364
- return /* @__PURE__ */ jsx(Fragment, { children: computedProps.extendTypes[computedProps.item.type].render(
365
- computedProps.value,
366
- computedProps.values,
367
- 0,
368
- "description"
369
- ) });
370
- throw Error(`${computedProps.item.type} requires children or render`);
371
- }
372
- if (computedProps.value === null || Array.isArray(computedProps.value) && !computedProps.value.length)
373
- return /* @__PURE__ */ jsx(Blank, {});
374
- switch (computedProps.item.type) {
375
- case "string[]":
376
- return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.join(", ") });
377
- case "number":
378
- return computedProps.value || null;
379
- case "number[]":
380
- return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.join(", ") });
381
- case "boolean":
382
- return computedProps.value ? /* @__PURE__ */ jsx(
383
- CheckOutlined,
384
- {
385
- style: {
386
- marginTop: "4px",
387
- color: "#52c41a"
388
- }
389
- }
390
- ) : /* @__PURE__ */ jsx(
391
- CloseOutlined,
392
- {
393
- style: {
394
- marginTop: "4px",
395
- color: "#ff4d4f"
396
- }
397
- }
398
- );
399
- case "time":
400
- return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.format("YYYY-MM-DD HH:mm:ss") });
401
- case "date":
402
- return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.format("YYYY-MM-DD") });
403
- case "object":
404
- if (!computedProps.value) return /* @__PURE__ */ jsx(Blank, {});
405
- return /* @__PURE__ */ jsx(
406
- Description,
407
- {
408
- items: computedProps.item.object,
409
- dataSource: computedProps.value,
410
- column: 1
411
- }
412
- );
413
- case "object[]":
414
- if (!computedProps.value?.length)
415
- return /* @__PURE__ */ jsx(Blank, {});
416
- return /* @__PURE__ */ jsx(Space, { direction: "vertical", children: computedProps.value.map(
417
- (value, index) => /* @__PURE__ */ jsx(
418
- Description,
419
- {
420
- items: computedProps.item.object,
421
- dataSource: value,
422
- column: 1
423
- },
424
- index
425
- )
426
- ) });
427
- default:
428
- return computedProps.value || null;
429
- }
701
+ const [computedProps, setComputedProps] = useState();
702
+ useEqualEffect(() => {
703
+ const propsCopy = { ...props };
704
+ propsCopy.item.title = propsCopy.item.title ?? idToTitle(propsCopy.item.id);
705
+ if (!propsCopy.item.type) propsCopy.item.type = "string";
706
+ if (propsCopy.item.options?.length) propsCopy.item.options = transferOptions(propsCopy.item.options);
707
+ propsCopy.value = transferValue(propsCopy.item.type, propsCopy.value);
708
+ if (propsCopy.item.options && propsCopy.value !== null) {
709
+ if (propsCopy.item.type.endsWith("[]")) propsCopy.value = propsCopy.value.map((v) => propsCopy.item.options.find((option) => option.value === v)?.label || v);
710
+ else if ([
711
+ "string",
712
+ "number",
713
+ "boolean"
714
+ ].includes(propsCopy.item.type)) propsCopy.value = props.item.options.find((option) => option.value === props.value)?.label || props.value;
715
+ }
716
+ setComputedProps(propsCopy);
717
+ }, [props]);
718
+ if (!computedProps) return null;
719
+ const itemType = computedProps.item.type ?? "string";
720
+ if (computedProps.item.descriptionChildren === null || computedProps.item.children === null || computedProps.item.descriptionRender === null || computedProps.item.render === null) return null;
721
+ const children = computedProps.item.descriptionChildren || computedProps.item.children;
722
+ if (children) return cloneUnionFaasItemElement(children, {
723
+ scene: "description",
724
+ value: computedProps.value,
725
+ values: computedProps.values,
726
+ index: 0
727
+ });
728
+ const render = computedProps.item.descriptionRender || computedProps.item.render;
729
+ if (render) return /* @__PURE__ */ jsx(Fragment, { children: render(computedProps.value, computedProps.values, 0, "description") });
730
+ if (computedProps.extendTypes?.[itemType]) {
731
+ const extendType = computedProps.extendTypes[itemType];
732
+ if (extendType.children) return cloneUnionFaasItemElement(extendType.children, {
733
+ scene: "description",
734
+ value: computedProps.value,
735
+ values: computedProps.values
736
+ });
737
+ if (extendType.render) return /* @__PURE__ */ jsx(Fragment, { children: extendType.render(computedProps.value, computedProps.values, 0, "description") });
738
+ throw Error(`${itemType} requires children or render`);
739
+ }
740
+ if (computedProps.value === null || Array.isArray(computedProps.value) && !computedProps.value.length) return /* @__PURE__ */ jsx(Blank, {});
741
+ switch (itemType) {
742
+ case "string[]": return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.join(", ") });
743
+ case "number": return computedProps.value || null;
744
+ case "number[]": return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.join(", ") });
745
+ case "boolean": return computedProps.value ? /* @__PURE__ */ jsx(CheckOutlined, { style: {
746
+ marginTop: "4px",
747
+ color: "#52c41a"
748
+ } }) : /* @__PURE__ */ jsx(CloseOutlined, { style: {
749
+ marginTop: "4px",
750
+ color: "#ff4d4f"
751
+ } });
752
+ case "time": return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.format("YYYY-MM-DD HH:mm:ss") });
753
+ case "date": return /* @__PURE__ */ jsx(Fragment, { children: computedProps.value.format("YYYY-MM-DD") });
754
+ case "object":
755
+ if (!computedProps.value) return /* @__PURE__ */ jsx(Blank, {});
756
+ return /* @__PURE__ */ jsx(Description, {
757
+ items: computedProps.item.object || [],
758
+ dataSource: computedProps.value,
759
+ column: 1
760
+ });
761
+ case "object[]":
762
+ if (!computedProps.value?.length) return /* @__PURE__ */ jsx(Blank, {});
763
+ return /* @__PURE__ */ jsx(Space, {
764
+ direction: "vertical",
765
+ children: computedProps.value.map((value, index) => /* @__PURE__ */ jsx(Description, {
766
+ items: computedProps.item.object || [],
767
+ dataSource: value,
768
+ column: 1
769
+ }, index))
770
+ });
771
+ default: return computedProps.value || null;
772
+ }
430
773
  }
431
774
  DescriptionItemContent.displayName = "DescriptionItemContent";
432
- DescriptionItemContent.whyDidYouRender = true;
433
- function Description({
434
- faasData,
435
- dataSource,
436
- renderTitle,
437
- extendTypes,
438
- ...props
439
- }) {
440
- if (faasData && !dataSource)
441
- return /* @__PURE__ */ jsx(
442
- FaasDataWrapper,
443
- {
444
- render: ({ data }) => /* @__PURE__ */ jsx(
445
- Description,
446
- {
447
- ...props,
448
- dataSource: data,
449
- renderTitle,
450
- extendTypes
451
- }
452
- ),
453
- ...faasData
454
- }
455
- );
456
- return /* @__PURE__ */ jsx(
457
- Descriptions,
458
- {
459
- ...props,
460
- title: typeof renderTitle === "function" ? renderTitle(dataSource) : props.title,
461
- items: props.items.filter(
462
- (item) => item && !(item.descriptionChildren === null || item.children === null || item.descriptionRender === null || item.render === null) && (!item.if || item.if(dataSource))
463
- ).map((item) => ({
464
- ...item,
465
- key: item.id,
466
- label: item.title ?? idToTitle(item.id),
467
- children: /* @__PURE__ */ jsx(
468
- DescriptionItemContent,
469
- {
470
- item,
471
- value: dataSource ? dataSource[item.id] : null,
472
- values: dataSource,
473
- extendTypes
474
- }
475
- )
476
- }))
477
- }
478
- );
775
+ /**
776
+ * Render an Ant Design description list from FaasJS item metadata.
777
+ *
778
+ * The component can render a local `dataSource` directly or resolve one through `faasData`, and
779
+ * it applies the same item type normalization helpers used by the form and table components.
780
+ *
781
+ * @template T - Data record shape rendered by the component.
782
+ * @param {DescriptionProps<T>} props - Description props including items, data source, and optional Faas data config.
783
+ * @throws {Error} When an entry in `extendTypes` omits both `children` and `render`.
784
+ *
785
+ * @example
786
+ * ```tsx
787
+ * import { Description } from '@faasjs/ant-design'
788
+ *
789
+ * export function Detail() {
790
+ * return (
791
+ * <Description
792
+ * title="Title"
793
+ * items={[
794
+ * {
795
+ * id: 'id',
796
+ * title: 'Title',
797
+ * type: 'string',
798
+ * },
799
+ * ]}
800
+ * dataSource={{ id: 'value' }}
801
+ * />
802
+ * )
803
+ * }
804
+ * ```
805
+ */
806
+ function Description(props) {
807
+ const { faasData, dataSource, renderTitle, extendTypes, ...descriptionProps } = props;
808
+ if (faasData && !dataSource) {
809
+ const faasDataProps = {
810
+ action: faasData.action,
811
+ render: ({ data }) => /* @__PURE__ */ jsx(Description, {
812
+ ...descriptionProps,
813
+ dataSource: data,
814
+ ...renderTitle ? { renderTitle } : {},
815
+ ...extendTypes ? { extendTypes } : {}
816
+ })
817
+ };
818
+ if (faasData.baseUrl !== void 0) faasDataProps.baseUrl = faasData.baseUrl;
819
+ if (faasData.data !== void 0) faasDataProps.data = faasData.data;
820
+ if (faasData.fallback !== void 0) faasDataProps.fallback = faasData.fallback;
821
+ if (faasData.onDataChange !== void 0) faasDataProps.onDataChange = (args) => faasData.onDataChange?.(args);
822
+ if (faasData.params !== void 0) faasDataProps.params = faasData.params;
823
+ if (faasData.ref !== void 0) faasDataProps.ref = faasData.ref;
824
+ if (faasData.setData !== void 0) faasDataProps.setData = faasData.setData;
825
+ return /* @__PURE__ */ jsx(FaasDataWrapper, { ...faasDataProps });
826
+ }
827
+ return /* @__PURE__ */ jsx(Descriptions, {
828
+ ...descriptionProps,
829
+ title: typeof renderTitle === "function" ? renderTitle(dataSource) : descriptionProps.title,
830
+ items: descriptionProps.items.filter((item) => item && !(item.descriptionChildren === null || item.children === null || item.descriptionRender === null || item.render === null) && (!item.if || item.if(dataSource))).map((item) => ({
831
+ ...item,
832
+ key: item.id,
833
+ label: item.title ?? idToTitle(item.id),
834
+ children: /* @__PURE__ */ jsx(DescriptionItemContent, {
835
+ item,
836
+ value: dataSource ? dataSource[item.id] : null,
837
+ ...dataSource ? { values: dataSource } : {},
838
+ ...extendTypes ? { extendTypes } : {}
839
+ })
840
+ }))
841
+ });
479
842
  }
480
843
  Description.displayName = "Description";
481
- Description.whyDidYouRender = true;
844
+ //#endregion
845
+ //#region src/FormItem.tsx
482
846
  function isOptionsProps(item) {
483
- return item && Array.isArray(item.options);
847
+ return item && Array.isArray(item.options);
484
848
  }
485
849
  function processProps(propsCopy, config) {
486
- propsCopy.title = propsCopy.title ?? idToTitle(propsCopy.id);
487
- if (!propsCopy.label && propsCopy.label !== false)
488
- propsCopy.label = propsCopy.title;
489
- if (!propsCopy.name) propsCopy.name = propsCopy.id;
490
- if (!propsCopy.type) propsCopy.type = "string";
491
- if (!propsCopy.rules) propsCopy.rules = [];
492
- if (propsCopy.required) {
493
- if (propsCopy.type.endsWith("[]"))
494
- propsCopy.rules.push({
495
- required: true,
496
- validator: async (_, values) => {
497
- if (!values || values.length < 1)
498
- return Promise.reject(
499
- Error(`${propsCopy.label || propsCopy.title} ${config.required}`)
500
- );
501
- }
502
- });
503
- else
504
- propsCopy.rules.push({
505
- required: true,
506
- message: `${propsCopy.label || propsCopy.title} ${config.required}`
507
- });
508
- }
509
- if (!propsCopy.input) propsCopy.input = {};
510
- if (isOptionsProps(propsCopy))
511
- propsCopy.input.options = transferOptions(propsCopy.options);
512
- switch (propsCopy.type) {
513
- case "boolean":
514
- propsCopy.valuePropName = "checked";
515
- break;
516
- case "object":
517
- if (!Array.isArray(propsCopy.name)) propsCopy.name = [propsCopy.name];
518
- for (const sub of propsCopy.object) {
519
- if (!sub.name) sub.name = propsCopy.name.concat(sub.id);
520
- processProps(sub, config);
521
- }
522
- break;
523
- }
524
- return propsCopy;
850
+ propsCopy.title = propsCopy.title ?? idToTitle(propsCopy.id);
851
+ if (!propsCopy.label && propsCopy.label !== false) propsCopy.label = propsCopy.title;
852
+ if (!propsCopy.name) propsCopy.name = propsCopy.id;
853
+ if (!propsCopy.type) propsCopy.type = "string";
854
+ if (!propsCopy.rules) propsCopy.rules = [];
855
+ if (propsCopy.required) if (propsCopy.type.endsWith("[]")) propsCopy.rules.push({
856
+ required: true,
857
+ validator: async (_, values) => {
858
+ if (!values || values.length < 1) return Promise.reject(Error(`${propsCopy.label || propsCopy.title} ${config.required}`));
859
+ }
860
+ });
861
+ else propsCopy.rules.push({
862
+ required: true,
863
+ message: `${propsCopy.label || propsCopy.title} ${config.required}`
864
+ });
865
+ if (!propsCopy.input) propsCopy.input = {};
866
+ if (isOptionsProps(propsCopy)) propsCopy.input.options = transferOptions(propsCopy.options);
867
+ switch (propsCopy.type) {
868
+ case "boolean":
869
+ propsCopy.valuePropName = "checked";
870
+ break;
871
+ case "object":
872
+ if (!Array.isArray(propsCopy.name)) propsCopy.name = [propsCopy.name];
873
+ for (const sub of propsCopy.object || []) {
874
+ if (!sub.name) sub.name = propsCopy.name.concat(sub.id);
875
+ processProps(sub, config);
876
+ }
877
+ break;
878
+ }
879
+ return propsCopy;
525
880
  }
881
+ /**
882
+ * Render a FaasJS-aware Ant Design form field or nested field group.
883
+ *
884
+ * The component derives default labels from `id`, applies required validation messages from the
885
+ * active theme, supports surface-specific union renderers, and can render nested `object` or
886
+ * `object[]` field structures.
887
+ *
888
+ * @template T - Value type rendered or edited by the form item.
889
+ * @param {FormItemProps<T>} props - Form item props including field metadata, rules, and custom renderers.
890
+ *
891
+ * @example
892
+ * ```tsx
893
+ * import { FormItem } from '@faasjs/ant-design'
894
+ * import { Input } from 'antd'
895
+ *
896
+ * export function AccountFields() {
897
+ * return (
898
+ * <>
899
+ * <FormItem id="name" type="string" />
900
+ * <FormItem id="password">
901
+ * <Input.Password />
902
+ * </FormItem>
903
+ * </>
904
+ * )
905
+ * }
906
+ * ```
907
+ */
526
908
  function FormItem(props) {
527
- const [computedProps, setComputedProps] = useState();
528
- const [extendTypes, setExtendTypes] = useState();
529
- const { theme: theme2 } = useConfigContext();
530
- const [hidden, setHidden] = useState(props.hidden || false);
531
- useEffect(() => {
532
- const { extendTypes: extendTypes2, ...propsCopy } = { ...props };
533
- if (extendTypes2) {
534
- setExtendTypes(extendTypes2);
535
- }
536
- if (propsCopy.if) {
537
- const condition = propsCopy.if;
538
- const originShouldUpdate = propsCopy.shouldUpdate;
539
- propsCopy.shouldUpdate = (prev, cur) => {
540
- const show = condition(cur);
541
- const shouldUpdate = hidden !== show;
542
- setHidden(!show);
543
- const origin = originShouldUpdate ? typeof originShouldUpdate === "boolean" ? originShouldUpdate : originShouldUpdate(prev, cur, {}) : true;
544
- return shouldUpdate || origin;
545
- };
546
- delete propsCopy.if;
547
- delete propsCopy.hidden;
548
- }
549
- setComputedProps(processProps(propsCopy, theme2.common));
550
- }, [props]);
551
- if (!computedProps) return null;
552
- if (hidden)
553
- return /* @__PURE__ */ jsx(
554
- Form$1.Item,
555
- {
556
- ...computedProps,
557
- id: computedProps.id.toString(),
558
- noStyle: true,
559
- rules: [],
560
- children: /* @__PURE__ */ jsx(Input, { type: "hidden", hidden: true })
561
- }
562
- );
563
- if (computedProps.formChildren === null || computedProps.children === null || computedProps.formRender === null || computedProps.render === null)
564
- return null;
565
- const children = computedProps.formChildren || computedProps.children;
566
- if (children)
567
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: cloneUnionFaasItemElement(children, { scene: "form" }) });
568
- const render = computedProps.formRender || computedProps.render;
569
- if (render)
570
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: render(null, null, 0, "form") });
571
- if (extendTypes?.[computedProps.type])
572
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: cloneUnionFaasItemElement(extendTypes[computedProps.type].children, {
573
- scene: "form"
574
- }) });
575
- switch (computedProps.type) {
576
- case "string":
577
- if (isOptionsProps(computedProps))
578
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: computedProps.options.length > 10 ? /* @__PURE__ */ jsx(Select, { ...computedProps.input }) : /* @__PURE__ */ jsx(Radio.Group, { ...computedProps.input }) });
579
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: /* @__PURE__ */ jsx(Input, { ...computedProps.input }) });
580
- case "string[]":
581
- if (isOptionsProps(computedProps))
582
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: /* @__PURE__ */ jsx(Select, { mode: "multiple", ...computedProps.input }) });
583
- return /* @__PURE__ */ jsx(
584
- Form$1.List,
585
- {
586
- name: computedProps.name,
587
- rules: computedProps.rules,
588
- children: (fields, { add, remove }, { errors }) => /* @__PURE__ */ jsxs(Fragment, { children: [
589
- computedProps.label && /* @__PURE__ */ jsx("div", { className: "ant-form-item-label", children: /* @__PURE__ */ jsx(
590
- "label",
591
- {
592
- className: computedProps.rules.find((r) => r.required) && "ant-form-item-required",
593
- children: computedProps.label
594
- }
595
- ) }),
596
- fields.map((field) => {
597
- const { key, ...fieldProps } = field;
598
- return /* @__PURE__ */ jsx(Form$1.Item, { id: key.toString(), children: /* @__PURE__ */ jsxs(Row, { gutter: 24, style: { flexFlow: "row nowrap" }, children: [
599
- /* @__PURE__ */ jsx(Col, { span: 23, children: /* @__PURE__ */ jsx(Form$1.Item, { ...fieldProps, noStyle: true, children: /* @__PURE__ */ jsx(Input, { ...computedProps.input }) }) }),
600
- /* @__PURE__ */ jsx(Col, { span: 1, children: !computedProps.input?.disabled && (!computedProps.rules.find((r) => r.required) || key > 0) && /* @__PURE__ */ jsx(
601
- Button,
602
- {
603
- danger: true,
604
- type: "link",
605
- style: { float: "right" },
606
- icon: /* @__PURE__ */ jsx(MinusCircleOutlined, {}),
607
- onClick: () => remove(field.name)
608
- }
609
- ) })
610
- ] }) }, key);
611
- }),
612
- /* @__PURE__ */ jsxs(Form$1.Item, { children: [
613
- !computedProps.input?.disabled && (!computedProps.maxCount || computedProps.maxCount > fields.length) && /* @__PURE__ */ jsx(
614
- Button,
615
- {
616
- type: "dashed",
617
- block: true,
618
- onClick: () => add(),
619
- icon: /* @__PURE__ */ jsx(PlusOutlined, {})
620
- }
621
- ),
622
- computedProps.extra && /* @__PURE__ */ jsx("div", { className: "ant-form-item-extra", children: computedProps.extra }),
623
- /* @__PURE__ */ jsx(Form$1.ErrorList, { errors })
624
- ] })
625
- ] })
626
- }
627
- );
628
- case "number":
629
- if (computedProps.options)
630
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: computedProps.options.length > 10 ? /* @__PURE__ */ jsx(Select, { ...computedProps.input }) : /* @__PURE__ */ jsx(Radio.Group, { ...computedProps.input }) });
631
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: /* @__PURE__ */ jsx(
632
- InputNumber,
633
- {
634
- style: { width: "100%" },
635
- ...computedProps.input
636
- }
637
- ) });
638
- case "number[]":
639
- if (computedProps.options)
640
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: /* @__PURE__ */ jsx(Select, { mode: "multiple", ...computedProps.input }) });
641
- return /* @__PURE__ */ jsx(
642
- Form$1.List,
643
- {
644
- name: computedProps.name,
645
- rules: computedProps.rules,
646
- children: (fields, { add, remove }, { errors }) => /* @__PURE__ */ jsxs(Fragment, { children: [
647
- computedProps.label && /* @__PURE__ */ jsx("div", { className: "ant-form-item-label", children: /* @__PURE__ */ jsx(
648
- "label",
649
- {
650
- className: computedProps.rules?.find(
651
- (r) => r.required
652
- ) && "ant-form-item-required",
653
- children: computedProps.label
654
- }
655
- ) }),
656
- fields.map((field) => {
657
- const { key, ...fieldProps } = field;
658
- return /* @__PURE__ */ jsx(Form$1.Item, { id: key.toString(), children: /* @__PURE__ */ jsxs(Row, { gutter: 24, style: { flexFlow: "row nowrap" }, children: [
659
- /* @__PURE__ */ jsx(Col, { span: 23, children: /* @__PURE__ */ jsx(Form$1.Item, { ...fieldProps, noStyle: true, children: /* @__PURE__ */ jsx(
660
- InputNumber,
661
- {
662
- style: { width: "100%" },
663
- ...computedProps.input
664
- }
665
- ) }) }),
666
- /* @__PURE__ */ jsx(Col, { span: 1, children: !computedProps.input?.disabled && (!computedProps.rules.find((r) => r.required) || key > 0) && /* @__PURE__ */ jsx(
667
- Button,
668
- {
669
- danger: true,
670
- type: "link",
671
- style: { float: "right" },
672
- icon: /* @__PURE__ */ jsx(MinusCircleOutlined, {}),
673
- onClick: () => remove(field.name)
674
- }
675
- ) })
676
- ] }) }, key);
677
- }),
678
- /* @__PURE__ */ jsxs(Form$1.Item, { children: [
679
- !computedProps.input?.disabled && (!computedProps.maxCount || computedProps.maxCount > fields.length) && /* @__PURE__ */ jsx(
680
- Button,
681
- {
682
- type: "dashed",
683
- block: true,
684
- onClick: () => add(),
685
- icon: /* @__PURE__ */ jsx(PlusOutlined, {})
686
- }
687
- ),
688
- computedProps.extra && /* @__PURE__ */ jsx("div", { className: "ant-form-item-extra", children: computedProps.extra }),
689
- /* @__PURE__ */ jsx(Form$1.ErrorList, { errors })
690
- ] })
691
- ] })
692
- }
693
- );
694
- case "boolean":
695
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: /* @__PURE__ */ jsx(Switch, { ...computedProps.input }) });
696
- case "date":
697
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: /* @__PURE__ */ jsx(DatePicker, { ...computedProps.input }) });
698
- case "time":
699
- return /* @__PURE__ */ jsx(Form$1.Item, { ...computedProps, id: computedProps.id.toString(), children: /* @__PURE__ */ jsx(
700
- DatePicker,
701
- {
702
- ...{ ...computedProps.input, showTime: true }
703
- }
704
- ) });
705
- case "object":
706
- return /* @__PURE__ */ jsxs(Fragment, { children: [
707
- computedProps.label && /* @__PURE__ */ jsx("div", { className: "ant-form-item-label", children: /* @__PURE__ */ jsx(
708
- "label",
709
- {
710
- className: computedProps.rules?.find((r) => r.required) && "ant-form-item-required",
711
- children: computedProps.label
712
- }
713
- ) }),
714
- computedProps.object.map((o) => /* @__PURE__ */ jsx(FormItem, { ...o }, o.id))
715
- ] });
716
- case "object[]":
717
- return /* @__PURE__ */ jsx(
718
- Form$1.List,
719
- {
720
- name: computedProps.name,
721
- rules: computedProps.rules,
722
- children: (fields, { add, remove }, { errors }) => /* @__PURE__ */ jsxs(Fragment, { children: [
723
- fields.map((field) => /* @__PURE__ */ jsxs(
724
- Form$1.Item,
725
- {
726
- id: field.key.toString(),
727
- style: { marginBottom: 0 },
728
- children: [
729
- /* @__PURE__ */ jsx("div", { className: "ant-form-item-label", children: /* @__PURE__ */ jsxs("label", { children: [
730
- computedProps.label,
731
- " ",
732
- field.name + 1,
733
- !computedProps.disabled && (!computedProps.rules.find((r) => r.required) || field.key > 0) && /* @__PURE__ */ jsx(
734
- Button,
735
- {
736
- danger: true,
737
- type: "link",
738
- onClick: () => remove(field.name),
739
- children: theme2.common.delete
740
- }
741
- )
742
- ] }) }),
743
- /* @__PURE__ */ jsx(Row, { gutter: 24, children: computedProps.object.map((o) => /* @__PURE__ */ jsx(Col, { span: o.col || 24, children: /* @__PURE__ */ jsx(FormItem, { ...o, name: [field.name, o.id] }) }, o.id)) })
744
- ]
745
- },
746
- field.key
747
- )),
748
- /* @__PURE__ */ jsxs(Form$1.Item, { children: [
749
- !computedProps.disabled && (!computedProps.maxCount || computedProps.maxCount > fields.length) && /* @__PURE__ */ jsxs(
750
- Button,
751
- {
752
- type: "dashed",
753
- block: true,
754
- onClick: () => add(),
755
- icon: /* @__PURE__ */ jsx(PlusOutlined, {}),
756
- children: [
757
- theme2.common.add,
758
- " ",
759
- computedProps.label
760
- ]
761
- }
762
- ),
763
- computedProps.extra && /* @__PURE__ */ jsx("div", { className: "ant-form-item-extra", children: computedProps.extra }),
764
- /* @__PURE__ */ jsx(Form$1.ErrorList, { errors })
765
- ] })
766
- ] })
767
- }
768
- );
769
- default:
770
- return null;
771
- }
909
+ const [computedProps, setComputedProps] = useState();
910
+ const [extendTypes, setExtendTypes] = useState();
911
+ const { theme } = useConfigContext();
912
+ const [hidden, setHidden] = useState(props.hidden || false);
913
+ useEqualEffect(() => {
914
+ const { extendTypes, ...propsCopy } = { ...props };
915
+ if (extendTypes) setExtendTypes(extendTypes);
916
+ if (propsCopy.if) {
917
+ const condition = propsCopy.if;
918
+ const originShouldUpdate = propsCopy.shouldUpdate;
919
+ propsCopy.shouldUpdate = (prev, cur) => {
920
+ const show = condition(cur);
921
+ const shouldUpdate = hidden !== show;
922
+ setHidden(!show);
923
+ const origin = originShouldUpdate ? typeof originShouldUpdate === "boolean" ? originShouldUpdate : originShouldUpdate(prev, cur, {}) : true;
924
+ return shouldUpdate || origin;
925
+ };
926
+ delete propsCopy.if;
927
+ delete propsCopy.hidden;
928
+ }
929
+ setComputedProps(processProps(propsCopy, theme.common));
930
+ }, [
931
+ hidden,
932
+ props,
933
+ theme.common
934
+ ]);
935
+ if (!computedProps) return null;
936
+ const itemType = computedProps.type ?? "string";
937
+ if (hidden) return /* @__PURE__ */ jsx(Form$1.Item, {
938
+ ...computedProps,
939
+ id: computedProps.id.toString(),
940
+ noStyle: true,
941
+ rules: [],
942
+ children: /* @__PURE__ */ jsx(Input, {
943
+ type: "hidden",
944
+ hidden: true
945
+ })
946
+ });
947
+ if (computedProps.formChildren === null || computedProps.children === null || computedProps.formRender === null || computedProps.render === null) return null;
948
+ const children = computedProps.formChildren || computedProps.children;
949
+ if (children) return /* @__PURE__ */ jsx(Form$1.Item, {
950
+ ...computedProps,
951
+ id: computedProps.id.toString(),
952
+ children: cloneUnionFaasItemElement(children, { scene: "form" })
953
+ });
954
+ const render = computedProps.formRender || computedProps.render;
955
+ if (render) return /* @__PURE__ */ jsx(Form$1.Item, {
956
+ ...computedProps,
957
+ id: computedProps.id.toString(),
958
+ children: render(void 0, Object.create(null), 0, "form")
959
+ });
960
+ const extendType = extendTypes?.[itemType];
961
+ if (extendType?.children) return /* @__PURE__ */ jsx(Form$1.Item, {
962
+ ...computedProps,
963
+ id: computedProps.id.toString(),
964
+ children: cloneUnionFaasItemElement(extendType.children, { scene: "form" })
965
+ });
966
+ const renderFormItemList = (inputElement) => /* @__PURE__ */ jsx(Form$1.List, {
967
+ name: computedProps.name,
968
+ rules: computedProps.rules,
969
+ children: (fields, { add, remove }, { errors }) => /* @__PURE__ */ jsxs(Fragment, { children: [
970
+ computedProps.label && /* @__PURE__ */ jsx("div", {
971
+ className: "ant-form-item-label",
972
+ children: /* @__PURE__ */ jsx("label", {
973
+ className: (computedProps.rules || []).find((r) => r.required) && "ant-form-item-required",
974
+ children: computedProps.label
975
+ })
976
+ }),
977
+ fields.map((field) => {
978
+ const { key, ...fieldProps } = field;
979
+ return /* @__PURE__ */ jsx(Form$1.Item, {
980
+ id: key.toString(),
981
+ children: /* @__PURE__ */ jsxs(Row, {
982
+ gutter: 24,
983
+ style: { flexFlow: "row nowrap" },
984
+ children: [/* @__PURE__ */ jsx(Col, {
985
+ span: 23,
986
+ children: /* @__PURE__ */ jsx(Form$1.Item, {
987
+ ...fieldProps,
988
+ noStyle: true,
989
+ children: inputElement
990
+ })
991
+ }), /* @__PURE__ */ jsx(Col, {
992
+ span: 1,
993
+ children: !computedProps.input?.disabled && (!(computedProps.rules || []).find((r) => r.required) || key > 0) && /* @__PURE__ */ jsx(Button, {
994
+ danger: true,
995
+ type: "link",
996
+ style: { float: "right" },
997
+ icon: /* @__PURE__ */ jsx(MinusCircleOutlined, {}),
998
+ onClick: () => remove(field.name)
999
+ })
1000
+ })]
1001
+ })
1002
+ }, key);
1003
+ }),
1004
+ /* @__PURE__ */ jsxs(Form$1.Item, { children: [
1005
+ !computedProps.input?.disabled && (!computedProps.maxCount || computedProps.maxCount > fields.length) && /* @__PURE__ */ jsx(Button, {
1006
+ type: "dashed",
1007
+ block: true,
1008
+ onClick: () => add(),
1009
+ icon: /* @__PURE__ */ jsx(PlusOutlined, {})
1010
+ }),
1011
+ computedProps.extra && /* @__PURE__ */ jsx("div", {
1012
+ className: "ant-form-item-extra",
1013
+ children: computedProps.extra
1014
+ }),
1015
+ /* @__PURE__ */ jsx(Form$1.ErrorList, { errors })
1016
+ ] })
1017
+ ] })
1018
+ });
1019
+ switch (itemType) {
1020
+ case "string":
1021
+ if (isOptionsProps(computedProps)) return /* @__PURE__ */ jsx(Form$1.Item, {
1022
+ ...computedProps,
1023
+ id: computedProps.id.toString(),
1024
+ children: computedProps.options.length > 10 ? /* @__PURE__ */ jsx(Select, { ...computedProps.input }) : /* @__PURE__ */ jsx(Radio.Group, { ...computedProps.input })
1025
+ });
1026
+ return /* @__PURE__ */ jsx(Form$1.Item, {
1027
+ ...computedProps,
1028
+ id: computedProps.id.toString(),
1029
+ children: /* @__PURE__ */ jsx(Input, { ...computedProps.input })
1030
+ });
1031
+ case "string[]":
1032
+ if (isOptionsProps(computedProps)) return /* @__PURE__ */ jsx(Form$1.Item, {
1033
+ ...computedProps,
1034
+ id: computedProps.id.toString(),
1035
+ children: /* @__PURE__ */ jsx(Select, {
1036
+ mode: "multiple",
1037
+ ...computedProps.input
1038
+ })
1039
+ });
1040
+ return renderFormItemList(/* @__PURE__ */ jsx(Input, { ...computedProps.input }));
1041
+ case "number":
1042
+ if (isOptionsProps(computedProps)) return /* @__PURE__ */ jsx(Form$1.Item, {
1043
+ ...computedProps,
1044
+ id: computedProps.id.toString(),
1045
+ children: computedProps.options.length > 10 ? /* @__PURE__ */ jsx(Select, { ...computedProps.input }) : /* @__PURE__ */ jsx(Radio.Group, { ...computedProps.input })
1046
+ });
1047
+ return /* @__PURE__ */ jsx(Form$1.Item, {
1048
+ ...computedProps,
1049
+ id: computedProps.id.toString(),
1050
+ children: /* @__PURE__ */ jsx(InputNumber, {
1051
+ style: { width: "100%" },
1052
+ ...computedProps.input
1053
+ })
1054
+ });
1055
+ case "number[]":
1056
+ if (isOptionsProps(computedProps)) return /* @__PURE__ */ jsx(Form$1.Item, {
1057
+ ...computedProps,
1058
+ id: computedProps.id.toString(),
1059
+ children: /* @__PURE__ */ jsx(Select, {
1060
+ mode: "multiple",
1061
+ ...computedProps.input
1062
+ })
1063
+ });
1064
+ return renderFormItemList(/* @__PURE__ */ jsx(InputNumber, {
1065
+ style: { width: "100%" },
1066
+ ...computedProps.input
1067
+ }));
1068
+ case "boolean": return /* @__PURE__ */ jsx(Form$1.Item, {
1069
+ ...computedProps,
1070
+ id: computedProps.id.toString(),
1071
+ children: /* @__PURE__ */ jsx(Switch, { ...computedProps.input })
1072
+ });
1073
+ case "date": return /* @__PURE__ */ jsx(Form$1.Item, {
1074
+ ...computedProps,
1075
+ id: computedProps.id.toString(),
1076
+ children: /* @__PURE__ */ jsx(DatePicker, { ...computedProps.input })
1077
+ });
1078
+ case "time": return /* @__PURE__ */ jsx(Form$1.Item, {
1079
+ ...computedProps,
1080
+ id: computedProps.id.toString(),
1081
+ children: /* @__PURE__ */ jsx(DatePicker, {
1082
+ ...computedProps.input,
1083
+ showTime: true
1084
+ })
1085
+ });
1086
+ case "object": {
1087
+ const objectItems = computedProps.object || [];
1088
+ return /* @__PURE__ */ jsxs(Fragment, { children: [computedProps.label && /* @__PURE__ */ jsx("div", {
1089
+ className: "ant-form-item-label",
1090
+ children: /* @__PURE__ */ jsx("label", {
1091
+ className: computedProps.rules?.find((r) => r.required) && "ant-form-item-required",
1092
+ children: computedProps.label
1093
+ })
1094
+ }), objectItems.map((o) => /* @__PURE__ */ jsx(FormItem, { ...o }, o.id))] });
1095
+ }
1096
+ case "object[]": return /* @__PURE__ */ jsx(Form$1.List, {
1097
+ name: computedProps.name,
1098
+ rules: computedProps.rules,
1099
+ children: (fields, { add, remove }, { errors }) => /* @__PURE__ */ jsxs(Fragment, { children: [fields.map((field) => /* @__PURE__ */ jsxs(Form$1.Item, {
1100
+ id: field.key.toString(),
1101
+ style: { marginBottom: 0 },
1102
+ children: [/* @__PURE__ */ jsx("div", {
1103
+ className: "ant-form-item-label",
1104
+ children: /* @__PURE__ */ jsxs("label", { children: [
1105
+ computedProps.label,
1106
+ " ",
1107
+ field.name + 1,
1108
+ !computedProps.disabled && (!(computedProps.rules || []).find((r) => r.required) || field.key > 0) && /* @__PURE__ */ jsx(Button, {
1109
+ danger: true,
1110
+ type: "link",
1111
+ onClick: () => remove(field.name),
1112
+ children: theme.common.delete
1113
+ })
1114
+ ] })
1115
+ }), /* @__PURE__ */ jsx(Row, {
1116
+ gutter: 24,
1117
+ children: (computedProps.object || []).map((o) => /* @__PURE__ */ jsx(Col, {
1118
+ span: o.col || 24,
1119
+ children: /* @__PURE__ */ jsx(FormItem, {
1120
+ ...o,
1121
+ name: [field.name, o.id]
1122
+ })
1123
+ }, o.id))
1124
+ })]
1125
+ }, field.key)), /* @__PURE__ */ jsxs(Form$1.Item, { children: [
1126
+ !computedProps.disabled && (!computedProps.maxCount || computedProps.maxCount > fields.length) && /* @__PURE__ */ jsxs(Button, {
1127
+ type: "dashed",
1128
+ block: true,
1129
+ onClick: () => add(),
1130
+ icon: /* @__PURE__ */ jsx(PlusOutlined, {}),
1131
+ children: [
1132
+ theme.common.add,
1133
+ " ",
1134
+ computedProps.label
1135
+ ]
1136
+ }),
1137
+ computedProps.extra && /* @__PURE__ */ jsx("div", {
1138
+ className: "ant-form-item-extra",
1139
+ children: computedProps.extra
1140
+ }),
1141
+ /* @__PURE__ */ jsx(Form$1.ErrorList, { errors })
1142
+ ] })] })
1143
+ });
1144
+ default: return null;
1145
+ }
772
1146
  }
773
- FormItem.whyDidYouRender = true;
774
1147
  FormItem.useStatus = Form$1.Item.useStatus;
1148
+ //#endregion
1149
+ //#region src/Form.tsx
775
1150
  function isFormItemProps(item) {
776
- return item.id !== void 0;
1151
+ return item.id !== void 0;
777
1152
  }
1153
+ /**
1154
+ * Render a data-aware Ant Design form with optional FaasJS submission helpers.
1155
+ *
1156
+ * The component normalizes `initialValues` with {@link transferValue}, renders item definitions
1157
+ * through {@link FormItem}, and can either delegate submission to a custom `onFinish` handler or
1158
+ * the built-in FaasJS request flow configured by `faas`.
1159
+ *
1160
+ * @template Values - Form values shape.
1161
+ * @param {FormProps<Values>} props - Form props including items, submit behavior, and FaasJS integration.
1162
+ *
1163
+ * @example
1164
+ * ```tsx
1165
+ * import { Form } from '@faasjs/ant-design'
1166
+ *
1167
+ * export function ProfileForm() {
1168
+ * return (
1169
+ * <Form
1170
+ * items={[
1171
+ * { id: 'name', required: true },
1172
+ * { id: 'email', required: true },
1173
+ * ]}
1174
+ * onFinish={async (values) => {
1175
+ * console.log(values)
1176
+ * }}
1177
+ * />
1178
+ * )
1179
+ * }
1180
+ * ```
1181
+ *
1182
+ * @example
1183
+ * ```tsx
1184
+ * import { Form } from '@faasjs/ant-design'
1185
+ *
1186
+ * export function CreateUserForm() {
1187
+ * return (
1188
+ * <Form
1189
+ * initialValues={{ role: 'user' }}
1190
+ * items={[
1191
+ * { id: 'name', required: true },
1192
+ * { id: 'role', options: ['user', 'admin'] },
1193
+ * ]}
1194
+ * faas={{
1195
+ * action: 'user/create',
1196
+ * params: (values) => ({
1197
+ * role: values.role || 'user',
1198
+ * }),
1199
+ * }}
1200
+ * />
1201
+ * )
1202
+ * }
1203
+ * ```
1204
+ */
778
1205
  function Form(props) {
779
- const [loading, setLoading] = useState(false);
780
- const [computedProps, setComputedProps] = useState();
781
- const [submit, setSubmit] = useState();
782
- const config = useConfigContext();
783
- const [extendTypes, setExtendTypes] = useState();
784
- const [form] = Form$1.useForm(props.form);
785
- const [initialValues, setInitialValues] = useState(
786
- props.initialValues || /* @__PURE__ */ Object.create(null)
787
- );
788
- useEffect(() => {
789
- const { submit: submit2, ...propsCopy } = {
790
- ...props,
791
- form
792
- };
793
- if (typeof submit2 !== "undefined") setSubmit(submit2);
794
- if (propsCopy.initialValues && propsCopy.items?.length) {
795
- for (const key in propsCopy.initialValues) {
796
- propsCopy.initialValues[key] = transferValue(
797
- propsCopy.items.find((item) => isFormItemProps(item) && item.id === key)?.type,
798
- propsCopy.initialValues[key]
799
- );
800
- }
801
- setInitialValues(propsCopy.initialValues);
802
- delete propsCopy.initialValues;
803
- }
804
- if (propsCopy.items?.length)
805
- for (const item of propsCopy.items) {
806
- if (isFormItemProps(item) && item.if)
807
- item.hidden = !item.if(initialValues || /* @__PURE__ */ Object.create(null));
808
- }
809
- if (propsCopy.onFinish) {
810
- propsCopy.onFinish = async (values) => {
811
- setLoading(true);
812
- try {
813
- if (submit2?.to?.action) {
814
- await props.onFinish(
815
- values,
816
- async (values2) => faas(
817
- submit2.to.action,
818
- submit2.to.params ? {
819
- ...values2,
820
- ...submit2.to.params
821
- } : values2
822
- )
823
- );
824
- } else await props.onFinish(values);
825
- } catch (error) {
826
- console.error(error);
827
- }
828
- setLoading(false);
829
- };
830
- } else if (submit2 && submit2.to?.action) {
831
- propsCopy.onFinish = async (values) => {
832
- setLoading(true);
833
- return faas(
834
- submit2.to.action,
835
- submit2.to.params ? {
836
- ...values,
837
- ...submit2.to.params
838
- } : values
839
- ).then((result) => {
840
- if (submit2.to.then)
841
- submit2.to.then(result);
842
- return result;
843
- }).catch((error) => {
844
- if (submit2.to.catch)
845
- submit2.to.catch(error);
846
- return Promise.reject(error);
847
- }).finally(() => {
848
- if (submit2.to.finally)
849
- submit2.to.finally();
850
- setLoading(false);
851
- });
852
- };
853
- }
854
- if (propsCopy.extendTypes) {
855
- setExtendTypes(propsCopy.extendTypes);
856
- delete propsCopy.extendTypes;
857
- }
858
- setComputedProps(propsCopy);
859
- }, [props]);
860
- const onValuesChange = useEqualCallback(
861
- (changedValues, allValues) => {
862
- console.debug("Form:onValuesChange", changedValues, allValues);
863
- if (props.onValuesChange) {
864
- props.onValuesChange(changedValues, allValues);
865
- }
866
- if (!props.items) return;
867
- for (const key in changedValues) {
868
- const item = computedProps.items.find(
869
- (i) => isFormItemProps(i) && i.id === key
870
- );
871
- if (item?.onValueChange)
872
- item.onValueChange(changedValues[key], allValues, form);
873
- }
874
- },
875
- [computedProps]
876
- );
877
- useEffect(() => {
878
- if (!initialValues) return;
879
- console.debug("Form:initialValues", initialValues);
880
- form.setFieldsValue(initialValues);
881
- setInitialValues(null);
882
- }, [computedProps]);
883
- if (!computedProps) return null;
884
- return /* @__PURE__ */ jsxs(Form$1, { ...computedProps, onValuesChange, children: [
885
- computedProps.beforeItems,
886
- computedProps.items?.map((item) => {
887
- if (isFormItemProps(item))
888
- return /* @__PURE__ */ jsx(FormItem, { ...item, extendTypes }, item.id);
889
- return item;
890
- }),
891
- computedProps.children,
892
- typeof submit !== "boolean" && /* @__PURE__ */ jsx(Button, { htmlType: "submit", type: "primary", loading, children: submit?.text || config.theme.Form.submit.text }),
893
- computedProps.footer
894
- ] });
1206
+ const [loading, setLoading] = useState(false);
1207
+ const [antdProps, setAntdProps] = useState();
1208
+ const [submit, setSubmit] = useState(props.submit === false ? false : {});
1209
+ const [items, setItems] = useState([]);
1210
+ const config = useConfigContext();
1211
+ const [extendTypes, setExtendTypes] = useState();
1212
+ const [form] = Form$1.useForm(props.form);
1213
+ const [initialValues, setInitialValues] = useState(props.initialValues || Object.create(null));
1214
+ const [onFinish, setOnFinish] = useState();
1215
+ useEqualEffect(() => {
1216
+ if (props.onFinish) {
1217
+ setOnFinish(() => async (values) => {
1218
+ if (!props.onFinish) return;
1219
+ setLoading(true);
1220
+ try {
1221
+ return await props.onFinish(values);
1222
+ } finally {
1223
+ setLoading(false);
1224
+ }
1225
+ });
1226
+ return;
1227
+ }
1228
+ if (props.faas?.action) {
1229
+ setOnFinish(() => async (values) => {
1230
+ if (!props.faas?.action) return;
1231
+ setLoading(true);
1232
+ let submitValues = values;
1233
+ try {
1234
+ if (props.faas?.transformValues) submitValues = await props.faas.transformValues(values);
1235
+ const extraParams = typeof props.faas?.params === "function" ? props.faas.params(submitValues) : props.faas.params;
1236
+ if (extraParams) submitValues = {
1237
+ ...submitValues,
1238
+ ...extraParams
1239
+ };
1240
+ const result = await faas(props.faas.action, submitValues);
1241
+ props.faas.onSuccess?.(result, submitValues);
1242
+ return result;
1243
+ } catch (error) {
1244
+ props.faas.onError?.(error, submitValues);
1245
+ throw error;
1246
+ } finally {
1247
+ props.faas.onFinally?.();
1248
+ setLoading(false);
1249
+ }
1250
+ });
1251
+ return;
1252
+ }
1253
+ setOnFinish(void 0);
1254
+ }, [props.onFinish, props.faas]);
1255
+ useEqualEffect(() => {
1256
+ setExtendTypes(props.extendTypes);
1257
+ }, [props.extendTypes]);
1258
+ useEqualEffect(() => {
1259
+ setSubmit(props.submit === false ? false : props.submit || {});
1260
+ }, [props.submit]);
1261
+ useEqualEffect(() => {
1262
+ const nextInitialValues = props.initialValues ? JSON.parse(JSON.stringify(props.initialValues)) : Object.create(null);
1263
+ for (const key in nextInitialValues) nextInitialValues[key] = transferValue(props.items?.find((item) => isFormItemProps(item) && item.id === key)?.type, nextInitialValues[key]);
1264
+ if (props.items?.length) setItems(props.items.map((item) => {
1265
+ if (!isFormItemProps(item) || !item.if) return item;
1266
+ return {
1267
+ ...item,
1268
+ hidden: !item.if(nextInitialValues)
1269
+ };
1270
+ }));
1271
+ else setItems([]);
1272
+ if (props.initialValues) {
1273
+ setInitialValues(nextInitialValues);
1274
+ return;
1275
+ }
1276
+ setInitialValues(null);
1277
+ }, [props.initialValues, props.items]);
1278
+ useEqualEffect(() => {
1279
+ const propsCopy = { ...props };
1280
+ delete propsCopy.onFinish;
1281
+ delete propsCopy.faas;
1282
+ delete propsCopy.extendTypes;
1283
+ delete propsCopy.submit;
1284
+ delete propsCopy.items;
1285
+ delete propsCopy.initialValues;
1286
+ setAntdProps(propsCopy);
1287
+ }, [props]);
1288
+ const onValuesChange = useEqualCallback((changedValues, allValues) => {
1289
+ console.debug("Form:onValuesChange", changedValues, allValues);
1290
+ if (props.onValuesChange) props.onValuesChange(changedValues, allValues);
1291
+ if (!items.length) return;
1292
+ for (const key in changedValues) {
1293
+ const item = items.find((i) => isFormItemProps(i) && i.id === key);
1294
+ if (item?.onValueChange) item.onValueChange(changedValues[key], allValues, form);
1295
+ }
1296
+ }, [
1297
+ items,
1298
+ props.onValuesChange,
1299
+ form
1300
+ ]);
1301
+ useEqualEffect(() => {
1302
+ if (!initialValues) return;
1303
+ console.debug("Form:initialValues", initialValues);
1304
+ form.setFieldsValue(initialValues);
1305
+ setInitialValues(null);
1306
+ }, [
1307
+ form,
1308
+ initialValues,
1309
+ items
1310
+ ]);
1311
+ if (!antdProps) return null;
1312
+ const submitButtonProps = typeof submit === "object" ? submit.buttonProps : void 0;
1313
+ const submitButtonLoading = loading ? true : submitButtonProps?.loading ?? false;
1314
+ return /* @__PURE__ */ jsxs(Form$1, {
1315
+ ...antdProps,
1316
+ form,
1317
+ onFinish,
1318
+ onValuesChange,
1319
+ children: [
1320
+ props.beforeItems,
1321
+ items.map((item) => {
1322
+ if (isFormItemProps(item)) return /* @__PURE__ */ jsx(FormItem, {
1323
+ ...item,
1324
+ ...extendTypes ? { extendTypes } : {}
1325
+ }, item.id);
1326
+ return item;
1327
+ }),
1328
+ props.children,
1329
+ typeof submit !== "boolean" && /* @__PURE__ */ jsx(Button, {
1330
+ ...submitButtonProps,
1331
+ htmlType: "submit",
1332
+ type: submitButtonProps?.type || "primary",
1333
+ loading: submitButtonLoading,
1334
+ children: submit?.text || config.theme.Form.submit.text
1335
+ }),
1336
+ props.footer
1337
+ ]
1338
+ });
895
1339
  }
896
- Form.whyDidYouRender = true;
897
1340
  Form.useForm = Form$1.useForm;
898
1341
  Form.useFormInstance = Form$1.useFormInstance;
899
1342
  Form.useWatch = Form$1.useWatch;
@@ -901,689 +1344,693 @@ Form.Item = FormItem;
901
1344
  Form.List = Form$1.List;
902
1345
  Form.ErrorList = Form$1.ErrorList;
903
1346
  Form.Provider = Form$1.Provider;
1347
+ //#endregion
1348
+ //#region src/Link.tsx
1349
+ /**
1350
+ * Render a navigation-aware link or button.
1351
+ *
1352
+ * Internal links are pushed through React Router, while links with `_blank` targets are opened
1353
+ * with `window.open`.
1354
+ *
1355
+ * @param {LinkProps} props - Link props controlling navigation target, rendering mode, and button behavior.
1356
+ *
1357
+ * @example
1358
+ * ```tsx
1359
+ * import { Link } from '@faasjs/ant-design'
1360
+ *
1361
+ * export function Navigation() {
1362
+ * return (
1363
+ * <>
1364
+ * <Link href="/">Home</Link>
1365
+ * <Link href="/users/new" button={{ type: 'primary' }}>
1366
+ * Create User
1367
+ * </Link>
1368
+ * </>
1369
+ * )
1370
+ * }
1371
+ * ```
1372
+ */
904
1373
  function Link(props) {
905
- const { theme: theme2 } = useConfigContext();
906
- const navigate = useNavigate();
907
- const target = props.target || theme2.Link?.target || (props.href.startsWith("http") ? "_blank" : void 0);
908
- let computedStyle = {
909
- ...theme2.Link.style || {},
910
- cursor: "pointer",
911
- ...props.style
912
- };
913
- if (props.block)
914
- computedStyle = Object.assign(
915
- {
916
- display: "block",
917
- width: "100%"
918
- },
919
- computedStyle
920
- );
921
- if (props.button)
922
- return /* @__PURE__ */ jsx(
923
- Button,
924
- {
925
- ...props.button || {},
926
- style: computedStyle,
927
- onClick: (e) => {
928
- props.onClick ? props.onClick(e) : target === "_blank" ? window.open(props.href) : navigate(props.href);
929
- e.preventDefault();
930
- },
931
- children: props.children ?? props.text
932
- }
933
- );
934
- return /* @__PURE__ */ jsx(
935
- Typography.Link,
936
- {
937
- href: props.href,
938
- target,
939
- style: computedStyle,
940
- copyable: props.copyable,
941
- onClick: (e) => {
942
- e.preventDefault();
943
- if (props.onClick) {
944
- props.onClick(e);
945
- return;
946
- }
947
- if (target === "_blank") {
948
- window.open(props.href);
949
- return;
950
- }
951
- navigate(props.href);
952
- },
953
- children: props.children ?? props.text
954
- }
955
- );
1374
+ const { theme } = useConfigContext();
1375
+ const navigate = useNavigate();
1376
+ const target = props.target || theme.Link?.target || (props.href.startsWith("http") ? "_blank" : void 0);
1377
+ let computedStyle = {
1378
+ ...theme.Link.style,
1379
+ cursor: "pointer",
1380
+ ...props.style
1381
+ };
1382
+ if (props.block) computedStyle = Object.assign({
1383
+ display: "block",
1384
+ width: "100%"
1385
+ }, computedStyle);
1386
+ const buttonProps = props.button && typeof props.button === "object" ? props.button : void 0;
1387
+ const copyableProps = typeof props.copyable === "undefined" ? {} : { copyable: props.copyable };
1388
+ if (props.button) return /* @__PURE__ */ jsx(Button, {
1389
+ ...buttonProps,
1390
+ style: computedStyle,
1391
+ onClick: (e) => {
1392
+ e.preventDefault();
1393
+ if (props.onClick) {
1394
+ props.onClick(e);
1395
+ return;
1396
+ }
1397
+ if (target === "_blank") {
1398
+ window.open(props.href);
1399
+ return;
1400
+ }
1401
+ navigate(props.href);
1402
+ },
1403
+ children: props.children ?? props.text
1404
+ });
1405
+ return /* @__PURE__ */ jsx(Typography.Link, {
1406
+ href: props.href,
1407
+ target,
1408
+ style: computedStyle,
1409
+ ...copyableProps,
1410
+ onClick: (e) => {
1411
+ e.preventDefault();
1412
+ if (props.onClick) {
1413
+ props.onClick(e);
1414
+ return;
1415
+ }
1416
+ if (target === "_blank") {
1417
+ window.open(props.href);
1418
+ return;
1419
+ }
1420
+ navigate(props.href);
1421
+ },
1422
+ children: props.children ?? props.text
1423
+ });
956
1424
  }
957
- Link.whyDidYouRender = true;
1425
+ //#endregion
1426
+ //#region src/Routers.tsx
1427
+ /**
1428
+ * Default 404 route element that uses the configured localized title.
1429
+ *
1430
+ * @example
1431
+ * ```tsx
1432
+ * import { PageNotFound, Routes } from '@faasjs/ant-design'
1433
+ *
1434
+ * export function AppRoutes() {
1435
+ * return (
1436
+ * <Routes
1437
+ * routes={[{ path: '/', element: <div>Home</div> }]}
1438
+ * notFound={<PageNotFound />}
1439
+ * />
1440
+ * )
1441
+ * }
1442
+ * ```
1443
+ */
958
1444
  function PageNotFound() {
959
- const { theme: theme2 } = useConfigContext();
960
- return /* @__PURE__ */ jsx(Result, { status: "404", title: theme2.common.pageNotFound });
1445
+ const { theme } = useConfigContext();
1446
+ return /* @__PURE__ */ jsx(Result, {
1447
+ status: "404",
1448
+ title: theme.common.pageNotFound
1449
+ });
961
1450
  }
1451
+ /**
1452
+ * Render React Router routes with lazy-page support and a default 404 route.
1453
+ *
1454
+ * The wrapper adds a catch-all route automatically and uses an Ant Design `Skeleton` fallback when
1455
+ * `fallback` is not provided.
1456
+ *
1457
+ * @param {RoutesProps} props - Route definitions and optional fallback or 404 elements.
1458
+ *
1459
+ * @example
1460
+ * ```tsx
1461
+ * import { Routes, lazy } from '@faasjs/ant-design'
1462
+ * import { BrowserRouter } from 'react-router-dom'
1463
+ *
1464
+ * export function App() {
1465
+ * return (
1466
+ * <BrowserRouter>
1467
+ * <Routes
1468
+ * routes={[
1469
+ * {
1470
+ * path: '/',
1471
+ * page: lazy(() => import('./pages/home')),
1472
+ * },
1473
+ * ]}
1474
+ * />
1475
+ * </BrowserRouter>
1476
+ * )
1477
+ * }
1478
+ * ```
1479
+ */
962
1480
  function Routes(props) {
963
- return /* @__PURE__ */ jsxs(Routes$1, { children: [
964
- props.routes.map((r) => /* @__PURE__ */ jsx(
965
- Route,
966
- {
967
- ...r,
968
- element: r.element || /* @__PURE__ */ jsx(
969
- Suspense,
970
- {
971
- fallback: props.fallback || /* @__PURE__ */ jsx("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx(Skeleton, { active: true }) }),
972
- children: /* @__PURE__ */ jsx(r.page, {})
973
- }
974
- )
975
- },
976
- r.path
977
- )),
978
- /* @__PURE__ */ jsx(Route, { path: "*", element: props.notFound || /* @__PURE__ */ jsx(PageNotFound, {}) }, "*")
979
- ] });
1481
+ return /* @__PURE__ */ jsxs(Routes$1, { children: [props.routes.map((r) => {
1482
+ const Page = r.page;
1483
+ return /* @__PURE__ */ jsx(Route, {
1484
+ ...r,
1485
+ element: r.element || (Page ? /* @__PURE__ */ jsx(Suspense, {
1486
+ fallback: props.fallback || /* @__PURE__ */ jsx("div", {
1487
+ style: { padding: "24px" },
1488
+ children: /* @__PURE__ */ jsx(Skeleton, { active: true })
1489
+ }),
1490
+ children: /* @__PURE__ */ jsx(Page, {})
1491
+ }) : void 0)
1492
+ }, r.path);
1493
+ }), /* @__PURE__ */ jsx(Route, {
1494
+ path: "*",
1495
+ element: props.notFound || /* @__PURE__ */ jsx(PageNotFound, {})
1496
+ }, "*")] });
980
1497
  }
981
- Routes.whyDidYouRender = true;
1498
+ //#endregion
1499
+ //#region src/Table.tsx
982
1500
  function processValue(item, value) {
983
- const transferred = transferValue(item.type, value);
984
- if (transferred === null || Array.isArray(transferred) && transferred.length === 0)
985
- return /* @__PURE__ */ jsx(Blank, {});
986
- if (item.options) {
987
- if (item.type.endsWith("[]"))
988
- return transferred.map(
989
- (v) => item.options.find((option) => option.value === v)?.label || v
990
- ).join(", ");
991
- if (["string", "number", "boolean"].includes(item.type))
992
- return item.options.find((option) => option.value === transferred)?.label || transferred;
993
- }
994
- if (item.type.endsWith("[]")) return transferred.join(", ");
995
- if (["date", "time"].includes(item.type))
996
- return transferred.format(
997
- item.type === "date" ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss"
998
- );
999
- return value;
1501
+ const itemType = item.type ?? "string";
1502
+ const transferred = transferValue(itemType, value);
1503
+ if (transferred === null || Array.isArray(transferred) && transferred.length === 0) return /* @__PURE__ */ jsx(Blank, {});
1504
+ if (item.options) {
1505
+ if (itemType.endsWith("[]")) return transferred.map((v) => item.options.find((option) => option.value === v)?.label || v).join(", ");
1506
+ if ([
1507
+ "string",
1508
+ "number",
1509
+ "boolean"
1510
+ ].includes(itemType)) return item.options.find((option) => option.value === transferred)?.label || transferred;
1511
+ }
1512
+ if (itemType.endsWith("[]")) return transferred.join(", ");
1513
+ if (["date", "time"].includes(itemType)) return transferred.format(itemType === "date" ? "YYYY-MM-DD" : "YYYY-MM-DD HH:mm:ss");
1514
+ return transferred;
1000
1515
  }
1516
+ /**
1517
+ * Render an Ant Design table from FaasJS item metadata.
1518
+ *
1519
+ * The component can render local `dataSource` rows or resolve remote rows through `faasData`. It
1520
+ * also generates default filters and sorters for built-in item types unless you disable them with
1521
+ * the corresponding Ant Design column props.
1522
+ *
1523
+ * @template T - Row record type rendered by the table.
1524
+ * @template ExtendTypes - Additional item prop shape accepted by `items`.
1525
+ * @param {TableProps<T, ExtendTypes>} props - Table props including columns, data source, and optional Faas data config.
1526
+ * @throws {Error} When an entry in `extendTypes` omits both `children` and `render`.
1527
+ *
1528
+ * @example
1529
+ * ```tsx
1530
+ * import { Table } from '@faasjs/ant-design'
1531
+ *
1532
+ * const rows = [
1533
+ * { id: 1, name: 'Alice', active: true },
1534
+ * { id: 2, name: 'Bob', active: false },
1535
+ * ]
1536
+ *
1537
+ * export function UserTable() {
1538
+ * return (
1539
+ * <Table
1540
+ * rowKey="id"
1541
+ * dataSource={rows}
1542
+ * items={[
1543
+ * { id: 'name', title: 'Name' },
1544
+ * { id: 'active', type: 'boolean', title: 'Active' },
1545
+ * ]}
1546
+ * />
1547
+ * )
1548
+ * }
1549
+ * ```
1550
+ */
1001
1551
  function Table(props) {
1002
- const [columns, setColumns] = useState();
1003
- const { theme: theme2 } = useConfigContext();
1004
- const generateFilterDropdown = (item) => {
1005
- if (item.filterDropdown && item.filterDropdown !== true) return;
1006
- if (item.options.length < 11) {
1007
- if (!item.filters)
1008
- item.filters = item.options.map((o) => ({
1009
- text: o.label,
1010
- value: o.value
1011
- }));
1012
- return;
1013
- }
1014
- item.filterDropdown = ({ setSelectedKeys, selectedKeys, confirm }) => /* @__PURE__ */ jsx(
1015
- "div",
1016
- {
1017
- style: {
1018
- padding: 8,
1019
- width: "200px"
1020
- },
1021
- onKeyDown: (e) => e.stopPropagation(),
1022
- children: /* @__PURE__ */ jsx(
1023
- Select,
1024
- {
1025
- options: item.options,
1026
- allowClear: true,
1027
- showSearch: true,
1028
- style: { width: "100%" },
1029
- placeholder: `${theme2.common.search} ${item.title}`,
1030
- value: selectedKeys,
1031
- onChange: (v) => {
1032
- setSelectedKeys(v?.length ? v : []);
1033
- confirm();
1034
- },
1035
- mode: "multiple",
1036
- filterOption: (input, option) => {
1037
- if (!input || !option || !option.label) return true;
1038
- input = input.trim();
1039
- return option.value === input || option.label.toString().toLowerCase().includes(input.toLowerCase());
1040
- }
1041
- }
1042
- )
1043
- }
1044
- );
1045
- return item;
1046
- };
1047
- useEffect(() => {
1048
- const items = cloneDeep(props.items).filter(
1049
- (item) => !(item.tableChildren === null || item.children === null || item.tableRender === null || item.render === null)
1050
- );
1051
- for (const item of items) {
1052
- if (!item.key) item.key = item.id;
1053
- if (!item.dataIndex) item.dataIndex = item.id;
1054
- item.title = item.title ?? idToTitle(item.id);
1055
- if (!item.type) item.type = "string";
1056
- if (item.options?.length) {
1057
- item.options = transferOptions(item.options);
1058
- item.filters = item.options.map((o) => ({
1059
- text: o.label,
1060
- value: o.value
1061
- })).concat({
1062
- text: /* @__PURE__ */ jsx(Blank, {}),
1063
- value: null
1064
- });
1065
- generateFilterDropdown(item);
1066
- }
1067
- const children = item.tableChildren || item.children;
1068
- if (children) {
1069
- item.render = (value, values) => cloneUnionFaasItemElement(children, {
1070
- scene: "table",
1071
- value,
1072
- values,
1073
- index: 0
1074
- });
1075
- delete item.children;
1076
- delete item.tableChildren;
1077
- continue;
1078
- }
1079
- const render = item.tableRender || item.render;
1080
- if (render) {
1081
- item.render = (value, values) => render(value, values, 0, "table");
1082
- delete item.tableRender;
1083
- continue;
1084
- }
1085
- if (props.extendTypes?.[item.type]) {
1086
- if (props.extendTypes[item.type].children) {
1087
- item.render = (value, values) => cloneUnionFaasItemElement(props.extendTypes[item.type].children, {
1088
- scene: "table",
1089
- value,
1090
- values,
1091
- index: 0
1092
- });
1093
- } else if (props.extendTypes[item.type].render)
1094
- item.render = (value, values) => props.extendTypes[item.type].render(value, values, 0, "table");
1095
- else throw Error(`${item.type} requires children or render`);
1096
- continue;
1097
- }
1098
- switch (item.type) {
1099
- case "string":
1100
- if (!item.render) item.render = (value) => processValue(item, value);
1101
- if (item.filterDropdown !== false) {
1102
- if (!item.onFilter && !props.faasData)
1103
- item.onFilter = (value, row) => {
1104
- if (!value || isNil(value)) return true;
1105
- if (isNil(row[item.id])) return false;
1106
- return row[item.id].trim().toLowerCase().includes(value.trim().toLowerCase());
1107
- };
1108
- if (typeof item.filterDropdown === "undefined" && !item.filters && item.optionsType !== "auto")
1109
- item.filterDropdown = ({
1110
- setSelectedKeys,
1111
- confirm,
1112
- clearFilters
1113
- }) => /* @__PURE__ */ jsx(
1114
- Input.Search,
1115
- {
1116
- placeholder: `${theme2.common.search} ${item.title}`,
1117
- allowClear: true,
1118
- onSearch: (v) => {
1119
- if (v) {
1120
- setSelectedKeys([v]);
1121
- } else {
1122
- setSelectedKeys([]);
1123
- clearFilters();
1124
- }
1125
- confirm();
1126
- }
1127
- }
1128
- );
1129
- }
1130
- break;
1131
- case "string[]":
1132
- if (!item.render) item.render = (value) => processValue(item, value);
1133
- if (item.filterDropdown !== false) {
1134
- if (!item.onFilter && !props.faasData)
1135
- item.onFilter = (value, row) => {
1136
- if (value === null && (!row[item.id] || !row[item.id].length))
1137
- return true;
1138
- if (!row[item.id] || !row[item.id].length || !value)
1139
- return false;
1140
- return row[item.id].some(
1141
- (v) => v.trim().toLowerCase().includes(value.trim().toLowerCase())
1142
- );
1143
- };
1144
- if (typeof item.filterDropdown === "undefined" && !item.filters && item.optionsType !== "auto")
1145
- item.filterDropdown = ({
1146
- setSelectedKeys,
1147
- confirm,
1148
- clearFilters
1149
- }) => /* @__PURE__ */ jsx(
1150
- Input.Search,
1151
- {
1152
- placeholder: `${theme2.common.search} ${item.title}`,
1153
- allowClear: true,
1154
- onSearch: (v) => {
1155
- if (v) {
1156
- setSelectedKeys([v]);
1157
- } else {
1158
- setSelectedKeys([]);
1159
- clearFilters();
1160
- }
1161
- confirm();
1162
- }
1163
- }
1164
- );
1165
- }
1166
- break;
1167
- case "number":
1168
- if (!item.render) item.render = (value) => processValue(item, value);
1169
- if (typeof item.sorter === "undefined")
1170
- item.sorter = (a, b) => a[item.id] - b[item.id];
1171
- if (item.filterDropdown !== false) {
1172
- if (!item.onFilter && !props.faasData)
1173
- item.onFilter = (value, row) => {
1174
- if (value === null) return true;
1175
- if (isNil(row[item.id])) return false;
1176
- return value == row[item.id];
1177
- };
1178
- if (typeof item.filterDropdown === "undefined" && !item.filters)
1179
- item.filterDropdown = ({
1180
- setSelectedKeys,
1181
- confirm,
1182
- clearFilters
1183
- }) => /* @__PURE__ */ jsx(
1184
- Input.Search,
1185
- {
1186
- placeholder: `${theme2.common.search} ${item.title}`,
1187
- allowClear: true,
1188
- onSearch: (v) => {
1189
- if (v) {
1190
- setSelectedKeys([Number(v)]);
1191
- } else {
1192
- setSelectedKeys([]);
1193
- clearFilters();
1194
- }
1195
- confirm();
1196
- }
1197
- }
1198
- );
1199
- }
1200
- break;
1201
- case "number[]":
1202
- if (!item.render)
1203
- item.render = (value) => processValue(item, value).join(", ");
1204
- if (item.filterDropdown !== false) {
1205
- if (!item.onFilter && !props.faasData)
1206
- item.onFilter = (value, row) => {
1207
- if (value === null && (!row[item.id] || !row[item.id].length))
1208
- return true;
1209
- if (!row[item.id] || !row[item.id].length) return false;
1210
- return row[item.id].includes(Number(value));
1211
- };
1212
- if (typeof item.filterDropdown === "undefined" && !item.filters)
1213
- item.filterDropdown = ({
1214
- setSelectedKeys,
1215
- confirm,
1216
- clearFilters
1217
- }) => /* @__PURE__ */ jsx(
1218
- Input.Search,
1219
- {
1220
- placeholder: `${theme2.common.search} ${item.title}`,
1221
- allowClear: true,
1222
- onSearch: (v) => {
1223
- if (v) {
1224
- setSelectedKeys([Number(v)]);
1225
- } else {
1226
- setSelectedKeys([]);
1227
- clearFilters();
1228
- }
1229
- confirm();
1230
- }
1231
- }
1232
- );
1233
- }
1234
- break;
1235
- case "boolean":
1236
- if (!item.render)
1237
- item.render = (value) => isNil(value) ? /* @__PURE__ */ jsx(Blank, {}) : value ? /* @__PURE__ */ jsx(
1238
- CheckOutlined,
1239
- {
1240
- style: {
1241
- marginTop: "4px",
1242
- color: "#52c41a"
1243
- }
1244
- }
1245
- ) : /* @__PURE__ */ jsx(
1246
- CloseOutlined,
1247
- {
1248
- style: {
1249
- marginTop: "4px",
1250
- color: "#ff4d4f"
1251
- }
1252
- }
1253
- );
1254
- if (item.filterDropdown !== false) {
1255
- if (typeof item.filterDropdown === "undefined")
1256
- item.filterDropdown = ({
1257
- setSelectedKeys,
1258
- selectedKeys,
1259
- confirm
1260
- }) => /* @__PURE__ */ jsxs(
1261
- Radio.Group,
1262
- {
1263
- style: { padding: 8 },
1264
- buttonStyle: "solid",
1265
- value: JSON.stringify(selectedKeys[0]),
1266
- onChange: (e) => {
1267
- const Values = {
1268
- true: true,
1269
- false: false,
1270
- null: null
1271
- };
1272
- setSelectedKeys(
1273
- e.target.value ? [Values[e.target.value]] : []
1274
- );
1275
- confirm();
1276
- },
1277
- children: [
1278
- /* @__PURE__ */ jsx(Radio.Button, { children: theme2.common.all }),
1279
- /* @__PURE__ */ jsx(Radio.Button, { value: "true", children: /* @__PURE__ */ jsx(
1280
- CheckOutlined,
1281
- {
1282
- style: {
1283
- color: "#52c41a",
1284
- verticalAlign: "middle"
1285
- }
1286
- }
1287
- ) }),
1288
- /* @__PURE__ */ jsx(Radio.Button, { value: "false", children: /* @__PURE__ */ jsx(
1289
- CloseOutlined,
1290
- {
1291
- style: {
1292
- verticalAlign: "middle",
1293
- color: "#ff4d4f"
1294
- }
1295
- }
1296
- ) }),
1297
- /* @__PURE__ */ jsx(Radio.Button, { value: "null", children: theme2.common.blank })
1298
- ]
1299
- }
1300
- );
1301
- if (!item.onFilter && !props.faasData)
1302
- item.onFilter = (value, row) => {
1303
- switch (value) {
1304
- case true:
1305
- return !isNil(row[item.id]) && row[item.id] !== false;
1306
- case false:
1307
- return !isNil(row[item.id]) && !row[item.id];
1308
- default:
1309
- return isNil(row[item.id]);
1310
- }
1311
- };
1312
- }
1313
- break;
1314
- case "date":
1315
- if (!item.render) item.render = (value) => processValue(item, value);
1316
- if (typeof item.sorter === "undefined")
1317
- item.sorter = (a, b, order) => {
1318
- if (isNil(a[item.id])) return order === "ascend" ? 1 : -1;
1319
- if (isNil(b[item.id])) return order === "ascend" ? -1 : 1;
1320
- return new Date(a[item.id]).getTime() < new Date(b[item.id]).getTime() ? -1 : 1;
1321
- };
1322
- if (item.filterDropdown !== false) {
1323
- if (typeof item.filterDropdown === "undefined")
1324
- item.filterDropdown = ({ setSelectedKeys, confirm }) => /* @__PURE__ */ jsx(
1325
- DatePicker.RangePicker,
1326
- {
1327
- onChange: (dates) => {
1328
- setSelectedKeys(
1329
- dates?.[0] && dates[1] ? [
1330
- [
1331
- dates[0].startOf("day").toISOString(),
1332
- dates[1].endOf("day").toISOString()
1333
- ]
1334
- ] : []
1335
- );
1336
- confirm();
1337
- }
1338
- }
1339
- );
1340
- if (!item.onFilter && !props.faasData)
1341
- item.onFilter = (value, row) => {
1342
- if (isNil(value[0])) return true;
1343
- if (isNil(row[item.id])) return false;
1344
- return dayjs2(row[item.id]) >= dayjs2(value[0]) && dayjs2(row[item.id]) <= dayjs2(value[1]);
1345
- };
1346
- }
1347
- break;
1348
- case "time":
1349
- item.width = item.width ?? 200;
1350
- if (!item.render) item.render = (value) => processValue(item, value);
1351
- if (typeof item.sorter === "undefined")
1352
- item.sorter = (a, b, order) => {
1353
- if (isNil(a[item.id])) return order === "ascend" ? 1 : -1;
1354
- if (isNil(b[item.id])) return order === "ascend" ? -1 : 1;
1355
- return new Date(a[item.id]).getTime() < new Date(b[item.id]).getTime() ? -1 : 1;
1356
- };
1357
- if (item.filterDropdown !== false) {
1358
- if (typeof item.filterDropdown === "undefined")
1359
- item.filterDropdown = ({ setSelectedKeys, confirm }) => /* @__PURE__ */ jsx(
1360
- DatePicker.RangePicker,
1361
- {
1362
- onChange: (dates) => {
1363
- setSelectedKeys(
1364
- dates?.[0] && dates[1] ? [
1365
- [
1366
- dates[0].startOf("day").toISOString(),
1367
- dates[1].endOf("day").toISOString()
1368
- ]
1369
- ] : []
1370
- );
1371
- confirm();
1372
- }
1373
- }
1374
- );
1375
- if (!item.onFilter && !props.faasData)
1376
- item.onFilter = (value, row) => {
1377
- if (isNil(value[0])) return true;
1378
- if (isNil(row[item.id])) return false;
1379
- return dayjs2(row[item.id]) >= dayjs2(value[0]) && dayjs2(row[item.id]) <= dayjs2(value[1]);
1380
- };
1381
- }
1382
- break;
1383
- case "object":
1384
- if (!item.render)
1385
- item.render = (value) => /* @__PURE__ */ jsx(
1386
- Description,
1387
- {
1388
- items: item.object,
1389
- dataSource: value || {},
1390
- column: 1
1391
- }
1392
- );
1393
- break;
1394
- case "object[]":
1395
- if (!item.render)
1396
- item.render = (value) => /* @__PURE__ */ jsx(Fragment, { children: value.map((v, i) => /* @__PURE__ */ jsx(
1397
- Description,
1398
- {
1399
- items: item.object,
1400
- dataSource: v || [],
1401
- column: 1
1402
- },
1403
- i
1404
- )) });
1405
- break;
1406
- default:
1407
- if (!item.render) item.render = (value) => processValue(item, value);
1408
- if (item.filterDropdown !== false && !item.onFilter && !props.faasData)
1409
- item.onFilter = (value, row) => {
1410
- if (value === null && isNil(row[item.id])) return true;
1411
- return value === row[item.id];
1412
- };
1413
- break;
1414
- }
1415
- }
1416
- setColumns(items);
1417
- }, [props.items]);
1418
- useEffect(() => {
1419
- if (!props.dataSource || !columns) return;
1420
- for (const column of columns) {
1421
- if (column.optionsType === "auto" && !column.options && !column.filters) {
1422
- const options = uniqBy(props.dataSource, column.id).map(
1423
- (v) => ({
1424
- label: v[column.id],
1425
- value: v[column.id]
1426
- })
1427
- );
1428
- if (options.length)
1429
- setColumns((prev) => {
1430
- const newColumns = [...prev];
1431
- const index = newColumns.findIndex((item) => item.id === column.id);
1432
- newColumns[index].options = options;
1433
- generateFilterDropdown(newColumns[index]);
1434
- return newColumns;
1435
- });
1436
- }
1437
- }
1438
- }, [props.dataSource, columns]);
1439
- if (!columns) return null;
1440
- if (props.dataSource)
1441
- return /* @__PURE__ */ jsx(
1442
- Table$1,
1443
- {
1444
- ...props,
1445
- rowKey: props.rowKey || "id",
1446
- columns,
1447
- dataSource: props.dataSource
1448
- }
1449
- );
1450
- return /* @__PURE__ */ jsx(FaasDataWrapper, { ...props.faasData, children: /* @__PURE__ */ jsx(FaasDataTable, { props, columns }) });
1552
+ const [columns, setColumns] = useState();
1553
+ const { theme } = useConfigContext();
1554
+ const { all, blank, search } = theme.common;
1555
+ const generateFilterDropdown = useCallback((item) => {
1556
+ if (item.filterDropdown && item.filterDropdown !== true) return;
1557
+ if (!item.options?.length) return;
1558
+ if (item.options.length < 11) {
1559
+ if (!item.filters) item.filters = item.options.map((o) => ({
1560
+ text: o.label,
1561
+ value: o.value
1562
+ }));
1563
+ return;
1564
+ }
1565
+ item.filterDropdown = ({ setSelectedKeys, selectedKeys, confirm }) => /* @__PURE__ */ jsx("div", {
1566
+ style: {
1567
+ padding: 8,
1568
+ width: "200px"
1569
+ },
1570
+ onKeyDown: (e) => e.stopPropagation(),
1571
+ children: /* @__PURE__ */ jsx(Select, {
1572
+ options: item.options,
1573
+ allowClear: true,
1574
+ showSearch: true,
1575
+ style: { width: "100%" },
1576
+ placeholder: `${search} ${item.title}`,
1577
+ value: selectedKeys,
1578
+ onChange: (v) => {
1579
+ setSelectedKeys(v?.length ? v : []);
1580
+ confirm();
1581
+ },
1582
+ mode: "multiple",
1583
+ filterOption: (input, option) => {
1584
+ if (!input || !option) return true;
1585
+ if (typeof option.label !== "string") return option.value === input;
1586
+ input = input.trim();
1587
+ return option.value === input || option.label.toLowerCase().includes(input.toLowerCase());
1588
+ }
1589
+ })
1590
+ });
1591
+ return item;
1592
+ }, [search]);
1593
+ useEqualEffect(() => {
1594
+ const items = cloneDeep(props.items).filter((item) => !(item.tableChildren === null || item.children === null || item.tableRender === null || item.render === null));
1595
+ const createTextSearchFilterDropdown = (item, transformValue) => ({ setSelectedKeys, confirm, clearFilters }) => /* @__PURE__ */ jsx(Input.Search, {
1596
+ placeholder: `${search} ${item.title}`,
1597
+ allowClear: true,
1598
+ onSearch: (v) => {
1599
+ if (v) setSelectedKeys(transformValue ? [transformValue(v)] : [v]);
1600
+ else {
1601
+ setSelectedKeys([]);
1602
+ clearFilters?.();
1603
+ }
1604
+ confirm();
1605
+ }
1606
+ });
1607
+ for (const item of items) {
1608
+ if (!item.key) item.key = item.id;
1609
+ if (!item.dataIndex) item.dataIndex = item.id;
1610
+ const itemType = item.type ?? "string";
1611
+ item.title = item.title ?? idToTitle(item.id);
1612
+ item.type = itemType;
1613
+ if (item.options?.length) {
1614
+ item.options = transferOptions(item.options);
1615
+ item.filters = item.options.map((o) => ({
1616
+ text: o.label,
1617
+ value: o.value
1618
+ })).concat({
1619
+ text: /* @__PURE__ */ jsx(Blank, {}),
1620
+ value: null
1621
+ });
1622
+ generateFilterDropdown(item);
1623
+ }
1624
+ const children = item.tableChildren || item.children;
1625
+ if (children) {
1626
+ item.render = (value, values) => cloneUnionFaasItemElement(children, {
1627
+ scene: "table",
1628
+ value,
1629
+ values,
1630
+ index: 0
1631
+ });
1632
+ delete item.children;
1633
+ delete item.tableChildren;
1634
+ continue;
1635
+ }
1636
+ const render = item.tableRender || item.render;
1637
+ if (render) {
1638
+ item.render = (value, values) => render(value, values, 0, "table");
1639
+ delete item.tableRender;
1640
+ continue;
1641
+ }
1642
+ const extendType = props.extendTypes?.[itemType];
1643
+ if (extendType) {
1644
+ const extendChildren = extendType.children;
1645
+ if (extendChildren) item.render = (value, values) => cloneUnionFaasItemElement(extendChildren, {
1646
+ scene: "table",
1647
+ value,
1648
+ values,
1649
+ index: 0
1650
+ });
1651
+ else {
1652
+ const extendRender = extendType.render;
1653
+ if (extendRender) item.render = (value, values) => extendRender(value, values, 0, "table");
1654
+ else throw Error(`${itemType} requires children or render`);
1655
+ }
1656
+ continue;
1657
+ }
1658
+ switch (itemType) {
1659
+ case "string":
1660
+ if (!item.render) item.render = (value) => processValue(item, value);
1661
+ if (item.filterDropdown !== false) {
1662
+ if (!item.onFilter && !props.faasData) item.onFilter = (value, row) => {
1663
+ if (!value || isNil(value)) return true;
1664
+ if (isNil(row[item.id])) return false;
1665
+ return row[item.id].trim().toLowerCase().includes(value.trim().toLowerCase());
1666
+ };
1667
+ if (typeof item.filterDropdown === "undefined" && !item.filters && item.optionsType !== "auto") item.filterDropdown = createTextSearchFilterDropdown(item);
1668
+ }
1669
+ break;
1670
+ case "string[]":
1671
+ if (!item.render) item.render = (value) => processValue(item, value);
1672
+ if (item.filterDropdown !== false) {
1673
+ if (!item.onFilter && !props.faasData) item.onFilter = (value, row) => {
1674
+ if (value === null && (!row[item.id] || !row[item.id].length)) return true;
1675
+ if (!row[item.id] || !row[item.id].length || !value) return false;
1676
+ return row[item.id].some((v) => v.trim().toLowerCase().includes(value.trim().toLowerCase()));
1677
+ };
1678
+ if (typeof item.filterDropdown === "undefined" && !item.filters && item.optionsType !== "auto") item.filterDropdown = createTextSearchFilterDropdown(item);
1679
+ }
1680
+ break;
1681
+ case "number":
1682
+ if (!item.render) item.render = (value) => processValue(item, value);
1683
+ if (typeof item.sorter === "undefined") item.sorter = (a, b) => a[item.id] - b[item.id];
1684
+ if (item.filterDropdown !== false) {
1685
+ if (!item.onFilter && !props.faasData) item.onFilter = (value, row) => {
1686
+ if (value === null) return true;
1687
+ if (isNil(row[item.id])) return false;
1688
+ return value == row[item.id];
1689
+ };
1690
+ if (typeof item.filterDropdown === "undefined" && !item.filters) item.filterDropdown = createTextSearchFilterDropdown(item, Number);
1691
+ }
1692
+ break;
1693
+ case "number[]":
1694
+ if (!item.render) item.render = (value) => processValue(item, value);
1695
+ if (item.filterDropdown !== false) {
1696
+ if (!item.onFilter && !props.faasData) item.onFilter = (value, row) => {
1697
+ if (value === null && (!row[item.id] || !row[item.id].length)) return true;
1698
+ if (!row[item.id] || !row[item.id].length) return false;
1699
+ return row[item.id].includes(Number(value));
1700
+ };
1701
+ if (typeof item.filterDropdown === "undefined" && !item.filters) item.filterDropdown = createTextSearchFilterDropdown(item, Number);
1702
+ }
1703
+ break;
1704
+ case "boolean":
1705
+ if (!item.render) item.render = (value) => isNil(value) ? /* @__PURE__ */ jsx(Blank, {}) : value ? /* @__PURE__ */ jsx(CheckOutlined, { style: {
1706
+ marginTop: "4px",
1707
+ color: "#52c41a"
1708
+ } }) : /* @__PURE__ */ jsx(CloseOutlined, { style: {
1709
+ marginTop: "4px",
1710
+ color: "#ff4d4f"
1711
+ } });
1712
+ if (item.filterDropdown !== false) {
1713
+ if (typeof item.filterDropdown === "undefined") item.filterDropdown = ({ setSelectedKeys, selectedKeys, confirm: confirmFilter }) => /* @__PURE__ */ jsxs(Radio.Group, {
1714
+ style: { padding: 8 },
1715
+ buttonStyle: "solid",
1716
+ value: JSON.stringify(selectedKeys[0]),
1717
+ onChange: (e) => {
1718
+ setSelectedKeys(e.target.value ? [{
1719
+ true: true,
1720
+ false: false,
1721
+ null: null
1722
+ }[e.target.value]] : []);
1723
+ confirmFilter();
1724
+ },
1725
+ children: [
1726
+ /* @__PURE__ */ jsx(Radio.Button, { children: all }),
1727
+ /* @__PURE__ */ jsx(Radio.Button, {
1728
+ value: "true",
1729
+ children: /* @__PURE__ */ jsx(CheckOutlined, { style: {
1730
+ color: "#52c41a",
1731
+ verticalAlign: "middle"
1732
+ } })
1733
+ }),
1734
+ /* @__PURE__ */ jsx(Radio.Button, {
1735
+ value: "false",
1736
+ children: /* @__PURE__ */ jsx(CloseOutlined, { style: {
1737
+ verticalAlign: "middle",
1738
+ color: "#ff4d4f"
1739
+ } })
1740
+ }),
1741
+ /* @__PURE__ */ jsx(Radio.Button, {
1742
+ value: "null",
1743
+ children: blank
1744
+ })
1745
+ ]
1746
+ });
1747
+ if (!item.onFilter && !props.faasData) item.onFilter = (value, row) => {
1748
+ switch (value) {
1749
+ case true: return !isNil(row[item.id]) && row[item.id] !== false;
1750
+ case false: return !isNil(row[item.id]) && !row[item.id];
1751
+ default: return isNil(row[item.id]);
1752
+ }
1753
+ };
1754
+ }
1755
+ break;
1756
+ case "date":
1757
+ case "time":
1758
+ if (itemType === "time") item.width = item.width ?? 200;
1759
+ if (!item.render) item.render = (value) => processValue(item, value);
1760
+ if (typeof item.sorter === "undefined") item.sorter = (a, b, order) => {
1761
+ if (isNil(a[item.id])) return order === "ascend" ? 1 : -1;
1762
+ if (isNil(b[item.id])) return order === "ascend" ? -1 : 1;
1763
+ return new Date(a[item.id]).getTime() < new Date(b[item.id]).getTime() ? -1 : 1;
1764
+ };
1765
+ if (item.filterDropdown !== false) {
1766
+ if (typeof item.filterDropdown === "undefined") item.filterDropdown = ({ setSelectedKeys, confirm }) => /* @__PURE__ */ jsx(DatePicker.RangePicker, { onChange: (dates) => {
1767
+ const start = dates?.[0];
1768
+ const end = dates?.[1];
1769
+ setSelectedKeys(start && end ? [[start.startOf("day").toISOString(), end.endOf("day").toISOString()]] : []);
1770
+ confirm();
1771
+ } });
1772
+ if (!item.onFilter && !props.faasData) item.onFilter = (value, row) => {
1773
+ if (isNil(value[0])) return true;
1774
+ if (isNil(row[item.id])) return false;
1775
+ return dayjs(row[item.id]) >= dayjs(value[0]) && dayjs(row[item.id]) <= dayjs(value[1]);
1776
+ };
1777
+ }
1778
+ break;
1779
+ case "object":
1780
+ if (!item.render) item.render = (value) => /* @__PURE__ */ jsx(Description, {
1781
+ items: item.object || [],
1782
+ dataSource: value || {},
1783
+ column: 1
1784
+ });
1785
+ break;
1786
+ case "object[]":
1787
+ if (!item.render) item.render = (value) => /* @__PURE__ */ jsx(Fragment, { children: value.map((v, i) => /* @__PURE__ */ jsx(Description, {
1788
+ items: item.object || [],
1789
+ dataSource: v || [],
1790
+ column: 1
1791
+ }, i)) });
1792
+ break;
1793
+ default:
1794
+ if (!item.render) item.render = (value) => processValue(item, value);
1795
+ if (item.filterDropdown !== false && !item.onFilter && !props.faasData) item.onFilter = (value, row) => {
1796
+ if (value === null && isNil(row[item.id])) return true;
1797
+ return value === row[item.id];
1798
+ };
1799
+ break;
1800
+ }
1801
+ }
1802
+ setColumns(items);
1803
+ }, [
1804
+ all,
1805
+ blank,
1806
+ generateFilterDropdown,
1807
+ props.extendTypes,
1808
+ props.faasData,
1809
+ props.items,
1810
+ search
1811
+ ]);
1812
+ useEqualEffect(() => {
1813
+ if (!props.dataSource || !columns) return;
1814
+ for (const column of columns) if (column.optionsType === "auto" && !column.options && !column.filters) {
1815
+ const options = uniqBy(props.dataSource, column.id).map((v) => ({
1816
+ label: v[column.id],
1817
+ value: v[column.id]
1818
+ }));
1819
+ if (options.length) setColumns((prev) => {
1820
+ const newColumns = [...prev || []];
1821
+ const index = newColumns.findIndex((item) => item.id === column.id);
1822
+ if (index < 0) return newColumns;
1823
+ newColumns[index].options = options;
1824
+ generateFilterDropdown(newColumns[index]);
1825
+ return newColumns;
1826
+ });
1827
+ }
1828
+ }, [
1829
+ props.dataSource,
1830
+ columns,
1831
+ generateFilterDropdown
1832
+ ]);
1833
+ if (!columns) return null;
1834
+ if (props.dataSource) return /* @__PURE__ */ jsx(Table$1, {
1835
+ ...props,
1836
+ rowKey: props.rowKey || "id",
1837
+ columns,
1838
+ dataSource: props.dataSource
1839
+ });
1840
+ if (!props.faasData) return /* @__PURE__ */ jsx(FaasDataTable, {
1841
+ props,
1842
+ columns
1843
+ });
1844
+ return /* @__PURE__ */ jsx(FaasDataWrapper, {
1845
+ ...props.faasData,
1846
+ children: /* @__PURE__ */ jsx(FaasDataTable, {
1847
+ props,
1848
+ columns
1849
+ })
1850
+ });
1451
1851
  }
1452
- function FaasDataTable({
1453
- props,
1454
- columns,
1455
- data,
1456
- params,
1457
- reload,
1458
- loading
1459
- }) {
1460
- const [currentColumns, setCurrentColumns] = useState(columns);
1461
- useEffect(() => {
1462
- if (!data || Array.isArray(data)) return;
1463
- setCurrentColumns((prev) => {
1464
- const newColumns = [...prev];
1465
- for (const column of newColumns) {
1466
- if (data.options?.[column.id]) {
1467
- column.options = transferOptions(data.options[column.id]);
1468
- column.filters = column.options.map((v) => ({
1469
- text: v.label,
1470
- value: v.value
1471
- })).concat({
1472
- text: /* @__PURE__ */ jsx(Blank, {}),
1473
- value: null
1474
- });
1475
- column.render = (value) => processValue(column, value);
1476
- if (column.filterDropdown) delete column.filterDropdown;
1477
- continue;
1478
- }
1479
- if (column.optionsType === "auto" && !column.options && !column.filters) {
1480
- const filters = uniqBy(props.dataSource, column.id).map(
1481
- (v) => ({
1482
- text: v[column.id],
1483
- value: v[column.id]
1484
- })
1485
- );
1486
- if (filters.length)
1487
- column.filters = filters.concat({
1488
- text: /* @__PURE__ */ jsx(Blank, {}),
1489
- value: null
1490
- });
1491
- }
1492
- }
1493
- return newColumns;
1494
- });
1495
- }, [data, props.dataSource]);
1496
- if (!data)
1497
- return /* @__PURE__ */ jsx(
1498
- Table$1,
1499
- {
1500
- ...props,
1501
- loading,
1502
- rowKey: props.rowKey || "id",
1503
- columns: currentColumns,
1504
- dataSource: []
1505
- }
1506
- );
1507
- if (Array.isArray(data))
1508
- return /* @__PURE__ */ jsx(
1509
- Table$1,
1510
- {
1511
- ...props,
1512
- loading,
1513
- rowKey: props.rowKey || "id",
1514
- columns: currentColumns,
1515
- dataSource: data
1516
- }
1517
- );
1518
- return /* @__PURE__ */ jsx(
1519
- Table$1,
1520
- {
1521
- ...props,
1522
- loading,
1523
- rowKey: props.rowKey || "id",
1524
- columns: currentColumns,
1525
- dataSource: data.rows,
1526
- pagination: props.pagination === false ? false : {
1527
- ...props.pagination || /* @__PURE__ */ Object.create(null),
1528
- ...data.pagination || /* @__PURE__ */ Object.create(null)
1529
- },
1530
- onChange: (pagination, filters, sorter, extra) => {
1531
- if (props.onChange) {
1532
- const processed = props.onChange(pagination, filters, sorter, extra);
1533
- reload({
1534
- ...params || /* @__PURE__ */ Object.create(null),
1535
- pagination: processed.pagination,
1536
- filters: processed.filters,
1537
- sorter: processed.sorter,
1538
- extra: processed.extra
1539
- });
1540
- return;
1541
- }
1542
- reload({
1543
- ...params || /* @__PURE__ */ Object.create(null),
1544
- pagination,
1545
- filters,
1546
- sorter
1547
- });
1548
- }
1549
- }
1550
- );
1852
+ function FaasDataTable({ props, columns, data, params, reload, loading }) {
1853
+ const [currentColumns, setCurrentColumns] = useState(columns);
1854
+ useEqualEffect(() => {
1855
+ if (!data || Array.isArray(data)) return;
1856
+ setCurrentColumns((prev) => {
1857
+ const newColumns = [...prev];
1858
+ for (const column of newColumns) {
1859
+ if (data.options?.[column.id]) {
1860
+ column.options = transferOptions(data.options[column.id]);
1861
+ column.filters = column.options.map((v) => ({
1862
+ text: v.label,
1863
+ value: v.value
1864
+ })).concat({
1865
+ text: /* @__PURE__ */ jsx(Blank, {}),
1866
+ value: null
1867
+ });
1868
+ column.render = (value) => processValue(column, value);
1869
+ if (column.filterDropdown) delete column.filterDropdown;
1870
+ continue;
1871
+ }
1872
+ if (column.optionsType === "auto" && !column.options && !column.filters) {
1873
+ const filters = uniqBy(props.dataSource, column.id).map((v) => ({
1874
+ text: v[column.id],
1875
+ value: v[column.id]
1876
+ }));
1877
+ if (filters.length) column.filters = filters.concat({
1878
+ text: /* @__PURE__ */ jsx(Blank, {}),
1879
+ value: null
1880
+ });
1881
+ }
1882
+ }
1883
+ return newColumns;
1884
+ });
1885
+ }, [data, props.dataSource]);
1886
+ if (!data) return /* @__PURE__ */ jsx(Table$1, {
1887
+ ...props,
1888
+ ...typeof loading === "undefined" ? {} : { loading },
1889
+ rowKey: props.rowKey || "id",
1890
+ columns: currentColumns,
1891
+ dataSource: []
1892
+ });
1893
+ if (Array.isArray(data)) return /* @__PURE__ */ jsx(Table$1, {
1894
+ ...props,
1895
+ ...typeof loading === "undefined" ? {} : { loading },
1896
+ rowKey: props.rowKey || "id",
1897
+ columns: currentColumns,
1898
+ dataSource: data
1899
+ });
1900
+ return /* @__PURE__ */ jsx(Table$1, {
1901
+ ...props,
1902
+ ...typeof loading === "undefined" ? {} : { loading },
1903
+ rowKey: props.rowKey || "id",
1904
+ columns: currentColumns,
1905
+ dataSource: data.rows,
1906
+ pagination: props.pagination === false ? false : {
1907
+ ...props.pagination || Object.create(null),
1908
+ ...data.pagination || Object.create(null)
1909
+ },
1910
+ onChange: (pagination, filters, sorter, extra) => {
1911
+ if (!reload) return;
1912
+ if (props.onChange) {
1913
+ const processed = props.onChange(pagination, filters, sorter, extra);
1914
+ reload({
1915
+ ...params || Object.create(null),
1916
+ pagination: processed.pagination,
1917
+ filters: processed.filters,
1918
+ sorter: processed.sorter,
1919
+ extra: processed.extra
1920
+ });
1921
+ return;
1922
+ }
1923
+ reload({
1924
+ ...params || Object.create(null),
1925
+ pagination,
1926
+ filters,
1927
+ sorter
1928
+ });
1929
+ }
1930
+ });
1551
1931
  }
1932
+ //#endregion
1933
+ //#region src/Tabs.tsx
1934
+ /**
1935
+ * Render an Ant Design tabs wrapper that accepts FaasJS-style tab definitions.
1936
+ *
1937
+ * Missing `key` and `label` values are derived from each tab's `id` and `title`.
1938
+ *
1939
+ * @param {TabsProps} props - Tabs props including tab items and Ant Design tab options.
1940
+ *
1941
+ * @example
1942
+ * ```tsx
1943
+ * import { Tabs } from '@faasjs/ant-design'
1944
+ *
1945
+ * export function Page() {
1946
+ * return (
1947
+ * <Tabs
1948
+ * items={[
1949
+ * {
1950
+ * id: 'id',
1951
+ * children: 'content',
1952
+ * },
1953
+ * 1 === 0 && {
1954
+ * id: 'hidden',
1955
+ * children: 'content',
1956
+ * },
1957
+ * ]}
1958
+ * />
1959
+ * )
1960
+ * }
1961
+ * ```
1962
+ */
1552
1963
  function Tabs(props) {
1553
- return /* @__PURE__ */ jsx(
1554
- Tabs$1,
1555
- {
1556
- ...props,
1557
- items: props.items.filter(Boolean).map((i) => ({
1558
- ...i,
1559
- key: i.key ?? i.id,
1560
- label: i.label ?? i.title ?? i.id
1561
- }))
1562
- }
1563
- );
1964
+ return /* @__PURE__ */ jsx(Tabs$1, {
1965
+ ...props,
1966
+ items: props.items.filter(Boolean).map((i) => ({
1967
+ ...i,
1968
+ key: i.key ?? i.id,
1969
+ label: i.label ?? i.title ?? i.id
1970
+ }))
1971
+ });
1564
1972
  }
1565
- Tabs.whyDidYouRender = true;
1973
+ //#endregion
1974
+ //#region src/Title.tsx
1975
+ /**
1976
+ * Update `document.title` and optionally render the title inline.
1977
+ *
1978
+ * The component returns `null` by default and is often used only for its side effect.
1979
+ *
1980
+ * @param {TitleProps} props - Title props controlling document title updates and optional inline rendering.
1981
+ *
1982
+ * @example
1983
+ * ```tsx
1984
+ * import { Title } from '@faasjs/ant-design'
1985
+ *
1986
+ * export function DetailPage() {
1987
+ * return (
1988
+ * <>
1989
+ * <Title title={['Orders', 'Detail']} h1 />
1990
+ * <div>...</div>
1991
+ * </>
1992
+ * )
1993
+ * }
1994
+ * ```
1995
+ */
1566
1996
  function Title(props) {
1567
- const { theme: theme2 } = useConfigContext();
1568
- useEffect(() => {
1569
- const title = Array.isArray(props.title) ? props.title : [props.title];
1570
- document.title = title.concat(props.suffix || theme2.Title.suffix).filter((t) => !!t).join(props.separator || theme2.Title.separator);
1571
- }, [props, theme2.Title]);
1572
- if (props.h1) {
1573
- if (typeof props.h1 === "boolean")
1574
- return /* @__PURE__ */ jsx("h1", { children: Array.isArray(props.title) ? props.title[0] : props.title });
1575
- return /* @__PURE__ */ jsx("h1", { className: props.h1.className, style: props.h1.style, children: Array.isArray(props.title) ? props.title[0] : props.title });
1576
- }
1577
- if (props.plain)
1578
- return /* @__PURE__ */ jsx(Fragment, { children: Array.isArray(props.title) ? props.title[0] : props.title });
1579
- if (props.children)
1580
- return cloneElement(props.children, { title: props.title });
1581
- return null;
1997
+ const { theme } = useConfigContext();
1998
+ useEqualEffect(() => {
1999
+ const title = Array.isArray(props.title) ? props.title : [props.title];
2000
+ document.title = title.concat(props.suffix || theme.Title.suffix).filter((t) => !!t).join(props.separator || theme.Title.separator);
2001
+ }, [props, theme.Title]);
2002
+ if (props.h1) {
2003
+ if (typeof props.h1 === "boolean") return /* @__PURE__ */ jsx("h1", { children: Array.isArray(props.title) ? props.title[0] : props.title });
2004
+ return /* @__PURE__ */ jsx("h1", {
2005
+ className: props.h1.className,
2006
+ style: props.h1.style,
2007
+ children: Array.isArray(props.title) ? props.title[0] : props.title
2008
+ });
2009
+ }
2010
+ if (props.plain) return /* @__PURE__ */ jsx(Fragment, { children: Array.isArray(props.title) ? props.title[0] : props.title });
2011
+ if (props.children) return cloneElement(props.children, { title: props.title });
2012
+ return null;
1582
2013
  }
1583
- Title.whyDidYouRender = true;
2014
+ //#endregion
2015
+ //#region src/useThemeToken.ts
2016
+ /**
2017
+ * Read the current Ant Design theme token.
2018
+ *
2019
+ * @returns Ant Design global token object for the active theme.
2020
+ *
2021
+ * @example
2022
+ * ```tsx
2023
+ * import { useThemeToken } from '@faasjs/ant-design'
2024
+ *
2025
+ * function PrimarySwatch() {
2026
+ * const { colorPrimary } = useThemeToken()
2027
+ *
2028
+ * return <div style={{ width: 24, height: 24, background: colorPrimary }} />
2029
+ * }
2030
+ * ```
2031
+ */
1584
2032
  function useThemeToken() {
1585
- const config = theme.useToken();
1586
- return config.token;
2033
+ return theme.useToken().token;
1587
2034
  }
1588
-
1589
- export { App, Blank, ConfigContext, ConfigProvider, Description, Drawer, ErrorBoundary, FaasDataWrapper, Form, FormItem, Link, Loading, Modal, PageNotFound, Routes, Table, Tabs, Title, cloneUnionFaasItemElement, idToTitle, transferOptions, transferValue, useApp, useConfigContext, useDrawer, useModal, useThemeToken, withFaasData };
2035
+ //#endregion
2036
+ 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 };