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

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