@faasjs/ant-design 8.0.0-beta.3 → 8.0.0-beta.30

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