nexstruct 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +122 -0
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/package.json +99 -0
- package/scaffold/generator.js +409 -0
- package/scaffold/index.js +20 -0
- package/scaffold/prompts.js +108 -0
- package/templates/api/axios/src/api/axios/client.api.ts +30 -0
- package/templates/api/axios/src/api/axios/users.api.ts +15 -0
- package/templates/api/fetch/src/api/fetch/client.api.ts +68 -0
- package/templates/api/fetch/src/api/fetch/users.api.ts +15 -0
- package/templates/api/trpc/src/api/trpc/client.api.ts +4 -0
- package/templates/api/trpc/src/api/trpc/router.api.ts +15 -0
- package/templates/api/trpc/src/api/trpc/server.client.api.ts +4 -0
- package/templates/api/trpc/src/providers/trpc.provider.tsx +24 -0
- package/templates/auth/clerk/src/auth/clerk/auth.service.ts +4 -0
- package/templates/auth/clerk/src/hooks/use-auth.hook.ts +13 -0
- package/templates/auth/clerk/src/middleware.ts +7 -0
- package/templates/auth/clerk/src/providers/auth.provider.tsx +6 -0
- package/templates/auth/next-auth/src/app/api/auth/[...nextauth]/route.ts +5 -0
- package/templates/auth/next-auth/src/auth/next-auth/auth.service.ts +45 -0
- package/templates/auth/next-auth/src/hooks/use-session.hook.ts +13 -0
- package/templates/auth/next-auth/src/providers/session.provider.tsx +6 -0
- package/templates/forms/formik/src/components/forms/login-form.component.tsx +30 -0
- package/templates/forms/formik/src/forms/formik/hooks/use-form-config.hook.ts +7 -0
- package/templates/forms/formik/src/forms/formik/schemas/example.schema.ts +8 -0
- package/templates/forms/react-hook-form/src/components/forms/login-form.component.tsx +27 -0
- package/templates/forms/react-hook-form/src/forms/react-hook-form/hooks/use-form.hook.ts +13 -0
- package/templates/forms/react-hook-form/src/forms/react-hook-form/schemas/example.schema.ts +15 -0
- package/templates/nextjs-base/next.config.ts +5 -0
- package/templates/nextjs-base/postcss.config.mjs +9 -0
- package/templates/nextjs-base/src/app/_components/navbar.tsx +88 -0
- package/templates/nextjs-base/src/app/_components/sidebar.tsx +223 -0
- package/templates/nextjs-base/src/app/error.tsx +39 -0
- package/templates/nextjs-base/src/app/globals.css +71 -0
- package/templates/nextjs-base/src/app/layout.tsx +21 -0
- package/templates/nextjs-base/src/app/loading.tsx +13 -0
- package/templates/nextjs-base/src/app/not-found.tsx +22 -0
- package/templates/nextjs-base/src/app/page.tsx +10 -0
- package/templates/nextjs-base/tailwind.config.ts +69 -0
- package/templates/shared/src/components/common/theme-toggle.component.tsx +31 -0
- package/templates/shared/src/components/common/toast/custom-message.component.tsx +18 -0
- package/templates/shared/src/components/common/toast/index.ts +8 -0
- package/templates/shared/src/components/common/toast/toast-message.component.tsx +112 -0
- package/templates/shared/src/hooks/use-debounce.hook.ts +12 -0
- package/templates/shared/src/hooks/use-fetch.hook.ts +42 -0
- package/templates/shared/src/hooks/use-intersection-observer.hook.ts +39 -0
- package/templates/shared/src/hooks/use-local-storage.hook.ts +30 -0
- package/templates/shared/src/hooks/use-media-query.hook.ts +26 -0
- package/templates/shared/src/hooks/use-toggle.hook.ts +12 -0
- package/templates/shared/src/lib/utils.util.ts +361 -0
- package/templates/shared/src/providers/theme.provider.tsx +17 -0
- package/templates/shared/src/providers/toast.provider.tsx +32 -0
- package/templates/shared/src/types/common.type.ts +34 -0
- package/templates/state/context/src/store/context/auth.context.tsx +47 -0
- package/templates/state/context/src/store/context/counter.context.tsx +41 -0
- package/templates/state/context/src/store/context/index.ts +2 -0
- package/templates/state/redux/src/providers/redux.provider.tsx +7 -0
- package/templates/state/redux/src/store/redux/hooks.store.ts +5 -0
- package/templates/state/redux/src/store/redux/index.ts +4 -0
- package/templates/state/redux/src/store/redux/slices/api.slice.ts +8 -0
- package/templates/state/redux/src/store/redux/slices/counter.slice.ts +24 -0
- package/templates/state/redux/src/store/redux/store.store.ts +13 -0
- package/templates/state/zustand/src/store/zustand/counter.store.ts +15 -0
- package/templates/state/zustand/src/store/zustand/index.ts +2 -0
- package/templates/state/zustand/src/store/zustand/user.store.ts +32 -0
- package/templates/ui/antd/COMPONENT_GUIDE.md +326 -0
- package/templates/ui/antd/src/app/examples/dialog/page.tsx +205 -0
- package/templates/ui/antd/src/app/examples/form/page.tsx +160 -0
- package/templates/ui/antd/src/app/examples/layout.tsx +125 -0
- package/templates/ui/antd/src/app/examples/page.tsx +64 -0
- package/templates/ui/antd/src/app/examples/table/page.tsx +118 -0
- package/templates/ui/antd/src/app/page.tsx +283 -0
- package/templates/ui/antd/src/components/common/DynamicTable/dynamic-table.component.tsx +79 -0
- package/templates/ui/antd/src/components/common/button/action-button.component.tsx +63 -0
- package/templates/ui/antd/src/components/common/dialog/dialog-wrapper.component.tsx +63 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/check-field.component.tsx +55 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/date-picker-field.component.tsx +80 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/limit-field.component.tsx +26 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/multi-check-field.component.tsx +56 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/number-field.component.tsx +100 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/otp-field.component.tsx +63 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/password-field.component.tsx +106 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/phone-number-field.component.tsx +78 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/radio-field.component.tsx +55 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/range-date-picker.component.tsx +66 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/search-field.component.tsx +24 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/select-field.component.tsx +82 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/single-check-field.component.tsx +50 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/single-select-field.component.tsx +86 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/string-number-field.component.tsx +80 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/switch-field.component.tsx +62 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/text-area-field.component.tsx +85 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/text-field.component.tsx +88 -0
- package/templates/ui/antd/src/components/common/fields/assets/interface/input-props.type.ts +233 -0
- package/templates/ui/antd/src/components/common/fields/cusInputField.component.tsx +40 -0
- package/templates/ui/antd/src/components/common/pagination/pagination.component.tsx +27 -0
- package/templates/ui/antd/src/components/ui/avatar.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/badge.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/button.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/card.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/checkbox.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/dialog.component.tsx +9 -0
- package/templates/ui/antd/src/components/ui/dropdown-menu.component.tsx +10 -0
- package/templates/ui/antd/src/components/ui/form.component.tsx +12 -0
- package/templates/ui/antd/src/components/ui/input.component.tsx +13 -0
- package/templates/ui/antd/src/components/ui/label.component.tsx +18 -0
- package/templates/ui/antd/src/components/ui/popover.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/progress.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/radio-group.component.tsx +10 -0
- package/templates/ui/antd/src/components/ui/scroll-area.component.tsx +25 -0
- package/templates/ui/antd/src/components/ui/select.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/separator.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/sheet.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/switch.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/table.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/tabs.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/textarea.component.tsx +9 -0
- package/templates/ui/antd/src/components/ui/tooltip.component.tsx +8 -0
- package/templates/ui/antd/src/lib/theme.util.ts +40 -0
- package/templates/ui/antd/src/providers/antd.provider.tsx +13 -0
- package/templates/ui/mui/src/app/examples/layout.tsx +113 -0
- package/templates/ui/mui/src/app/examples/page.tsx +716 -0
- package/templates/ui/mui/src/app/page.tsx +298 -0
- package/templates/ui/mui/src/components/common/DynamicTable/dynamic-table.component.tsx +131 -0
- package/templates/ui/mui/src/components/common/button/action-button.component.tsx +57 -0
- package/templates/ui/mui/src/components/common/dialog/dialog-wrapper.component.tsx +55 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/check-field.component.tsx +51 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/date-picker-field.component.tsx +50 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/multi-check-field.component.tsx +14 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/number-field.component.tsx +59 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/password-field.component.tsx +87 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/phone-number-field.component.tsx +48 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/radio-field.component.tsx +37 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/search-field.component.tsx +41 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/select-field.component.tsx +77 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/single-check-field.component.tsx +39 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/single-select-field.component.tsx +56 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/string-number-field.component.tsx +52 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/switch-field.component.tsx +35 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/text-area-field.component.tsx +46 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/text-field.component.tsx +51 -0
- package/templates/ui/mui/src/components/common/fields/assets/interface/input-props.type.ts +193 -0
- package/templates/ui/mui/src/components/common/fields/cusInputField.component.tsx +34 -0
- package/templates/ui/mui/src/components/common/pagination/pagination.component.tsx +59 -0
- package/templates/ui/mui/src/components/ui/avatar.component.tsx +19 -0
- package/templates/ui/mui/src/components/ui/badge.component.tsx +18 -0
- package/templates/ui/mui/src/components/ui/button.component.tsx +22 -0
- package/templates/ui/mui/src/components/ui/card.component.tsx +39 -0
- package/templates/ui/mui/src/components/ui/checkbox.component.tsx +21 -0
- package/templates/ui/mui/src/components/ui/dialog.component.tsx +38 -0
- package/templates/ui/mui/src/components/ui/dropdown-menu.component.tsx +43 -0
- package/templates/ui/mui/src/components/ui/form.component.tsx +98 -0
- package/templates/ui/mui/src/components/ui/input.component.tsx +15 -0
- package/templates/ui/mui/src/components/ui/label.component.tsx +15 -0
- package/templates/ui/mui/src/components/ui/popover.component.tsx +20 -0
- package/templates/ui/mui/src/components/ui/progress.component.tsx +19 -0
- package/templates/ui/mui/src/components/ui/radio-group.component.tsx +25 -0
- package/templates/ui/mui/src/components/ui/scroll-area.component.tsx +27 -0
- package/templates/ui/mui/src/components/ui/select.component.tsx +26 -0
- package/templates/ui/mui/src/components/ui/separator.component.tsx +11 -0
- package/templates/ui/mui/src/components/ui/sheet.component.tsx +44 -0
- package/templates/ui/mui/src/components/ui/switch.component.tsx +23 -0
- package/templates/ui/mui/src/components/ui/table.component.tsx +34 -0
- package/templates/ui/mui/src/components/ui/tabs.component.tsx +38 -0
- package/templates/ui/mui/src/components/ui/textarea.component.tsx +18 -0
- package/templates/ui/mui/src/components/ui/tooltip.component.tsx +24 -0
- package/templates/ui/mui/src/lib/theme.util.ts +73 -0
- package/templates/ui/mui/src/providers/mui.provider.tsx +13 -0
- package/templates/ui/shadcn/COMPONENT_GUIDE.md +306 -0
- package/templates/ui/shadcn/src/app/examples/dialog/page.tsx +122 -0
- package/templates/ui/shadcn/src/app/examples/form/page.tsx +107 -0
- package/templates/ui/shadcn/src/app/examples/layout.tsx +24 -0
- package/templates/ui/shadcn/src/app/examples/page.tsx +30 -0
- package/templates/ui/shadcn/src/app/examples/table/page.tsx +77 -0
- package/templates/ui/shadcn/src/app/page.tsx +20 -0
- package/templates/ui/shadcn/src/components/common/DynamicTable/dynamic-table.component.tsx +136 -0
- package/templates/ui/shadcn/src/components/common/button/action-button.component.tsx +68 -0
- package/templates/ui/shadcn/src/components/common/dialog/dialog-wrapper.component.tsx +58 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/check-field.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/date-picker-field.component.tsx +62 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/dynamic-file-upload-field.component.tsx +152 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/limit-field.component.tsx +73 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/multi-check-field.component.tsx +46 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/number-field.component.tsx +124 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/otp-field.component.tsx +61 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/password-field.component.tsx +110 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/phone-number-field.component.tsx +90 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/radio-field.component.tsx +41 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/range-date-picker.component.tsx +71 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/rich-text-editor.component.tsx +91 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/search-field.component.tsx +34 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/select-field.component.tsx +231 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/single-check-field.component.tsx +42 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/single-select-field.component.tsx +82 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/string-number-field.component.tsx +68 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/switch-field.component.tsx +61 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/text-area-field.component.tsx +62 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/text-area-with-file.component.tsx +142 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/text-field.component.tsx +80 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/tiny-editor.component.tsx +51 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/upload-profile-picture.component.tsx +103 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/upload-video-file.component.tsx +86 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/interface/input-props.type.ts +198 -0
- package/templates/ui/shadcn/src/components/common/fields/cusInputField.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/common/pagination/pagination.component.tsx +68 -0
- package/templates/ui/shadcn/src/components/ui/avatar.component.tsx +37 -0
- package/templates/ui/shadcn/src/components/ui/badge.component.tsx +28 -0
- package/templates/ui/shadcn/src/components/ui/button.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/ui/card.component.tsx +46 -0
- package/templates/ui/shadcn/src/components/ui/checkbox.component.tsx +25 -0
- package/templates/ui/shadcn/src/components/ui/dialog.component.tsx +98 -0
- package/templates/ui/shadcn/src/components/ui/dropdown-menu.component.tsx +163 -0
- package/templates/ui/shadcn/src/components/ui/form.component.tsx +110 -0
- package/templates/ui/shadcn/src/components/ui/input-otp.component.tsx +64 -0
- package/templates/ui/shadcn/src/components/ui/input.component.tsx +23 -0
- package/templates/ui/shadcn/src/components/ui/label.component.tsx +23 -0
- package/templates/ui/shadcn/src/components/ui/popover.component.tsx +27 -0
- package/templates/ui/shadcn/src/components/ui/progress.component.tsx +22 -0
- package/templates/ui/shadcn/src/components/ui/radio-group.component.tsx +33 -0
- package/templates/ui/shadcn/src/components/ui/scroll-area.component.tsx +37 -0
- package/templates/ui/shadcn/src/components/ui/select.component.tsx +139 -0
- package/templates/ui/shadcn/src/components/ui/separator.component.tsx +23 -0
- package/templates/ui/shadcn/src/components/ui/sheet.component.tsx +89 -0
- package/templates/ui/shadcn/src/components/ui/switch.component.tsx +26 -0
- package/templates/ui/shadcn/src/components/ui/table.component.tsx +71 -0
- package/templates/ui/shadcn/src/components/ui/tabs.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/ui/textarea.component.tsx +20 -0
- package/templates/ui/shadcn/src/components/ui/tooltip.component.tsx +25 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Button, Tooltip } from 'antd';
|
|
4
|
+
import type { ButtonProps as AntButtonProps } from 'antd';
|
|
5
|
+
import { forwardRef } from 'react';
|
|
6
|
+
|
|
7
|
+
interface ActionButtonProps {
|
|
8
|
+
/** HTML button type: submit / button / reset */
|
|
9
|
+
htmlType?: AntButtonProps['htmlType'];
|
|
10
|
+
/** Visual variant — maps to antd Button `type` */
|
|
11
|
+
variant?: 'primary' | 'dashed' | 'link' | 'text' | 'default';
|
|
12
|
+
btnSize?: 'small' | 'middle' | 'large';
|
|
13
|
+
btnStyle?: React.CSSProperties;
|
|
14
|
+
danger?: boolean;
|
|
15
|
+
tooltipContent?: string;
|
|
16
|
+
buttonContent?: React.ReactNode;
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
lastIcon?: React.ReactNode;
|
|
19
|
+
isPending?: boolean;
|
|
20
|
+
handleOpen?: (e?: React.MouseEvent<HTMLButtonElement>) => void;
|
|
21
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
22
|
+
loadingContent?: React.ReactNode;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
ghost?: boolean;
|
|
25
|
+
block?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>(
|
|
29
|
+
({ htmlType, btnSize = 'middle', btnStyle, variant = 'primary',
|
|
30
|
+
danger = false, tooltipContent, buttonContent, icon, lastIcon,
|
|
31
|
+
isPending = false, handleOpen, side = 'top', loadingContent,
|
|
32
|
+
disabled = false, ghost, block }, ref) => {
|
|
33
|
+
const buttonEl = (
|
|
34
|
+
<Button
|
|
35
|
+
ref={ref}
|
|
36
|
+
type={variant as any}
|
|
37
|
+
size={btnSize}
|
|
38
|
+
onClick={handleOpen}
|
|
39
|
+
danger={danger}
|
|
40
|
+
disabled={isPending || disabled}
|
|
41
|
+
loading={isPending}
|
|
42
|
+
icon={!isPending ? icon : undefined}
|
|
43
|
+
htmlType={htmlType}
|
|
44
|
+
ghost={ghost}
|
|
45
|
+
block={block}
|
|
46
|
+
style={{ textTransform: 'none', ...btnStyle }}
|
|
47
|
+
>
|
|
48
|
+
{isPending ? (loadingContent ?? buttonContent) : buttonContent}
|
|
49
|
+
{lastIcon && !isPending && <span style={{ marginLeft: 8 }}>{lastIcon}</span>}
|
|
50
|
+
</Button>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (!tooltipContent) return buttonEl;
|
|
54
|
+
return (
|
|
55
|
+
<Tooltip title={tooltipContent} placement={side}>
|
|
56
|
+
<span>{buttonEl}</span>
|
|
57
|
+
</Tooltip>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
ActionButton.displayName = 'ActionButton';
|
|
62
|
+
|
|
63
|
+
export { ActionButton };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Modal, Typography } from 'antd';
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
const { Text } = Typography;
|
|
7
|
+
|
|
8
|
+
interface DialogWrapperProps {
|
|
9
|
+
open: boolean;
|
|
10
|
+
setOpen: (open: boolean) => void;
|
|
11
|
+
triggerContent?: ReactNode;
|
|
12
|
+
title?: ReactNode;
|
|
13
|
+
description?: string;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
closer?: boolean;
|
|
16
|
+
footer?: ReactNode;
|
|
17
|
+
width?: number | string;
|
|
18
|
+
style?: React.CSSProperties;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function DialogWrapper({
|
|
22
|
+
open, setOpen, triggerContent, title, description,
|
|
23
|
+
children, closer = true, footer, width = 520, style,
|
|
24
|
+
}: DialogWrapperProps) {
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
{triggerContent}
|
|
28
|
+
<Modal
|
|
29
|
+
open={open}
|
|
30
|
+
onCancel={() => setOpen(false)}
|
|
31
|
+
title={
|
|
32
|
+
title || description ? (
|
|
33
|
+
<div>
|
|
34
|
+
<div style={{ fontWeight: 600, fontSize: 16 }}>
|
|
35
|
+
{typeof title === 'string' ? title : title}
|
|
36
|
+
</div>
|
|
37
|
+
{description && (
|
|
38
|
+
<Text type="secondary" style={{ fontSize: 13, marginTop: 2, display: 'block' }}>
|
|
39
|
+
{description}
|
|
40
|
+
</Text>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
) : undefined
|
|
44
|
+
}
|
|
45
|
+
closable={closer}
|
|
46
|
+
footer={footer}
|
|
47
|
+
width={width}
|
|
48
|
+
destroyOnClose
|
|
49
|
+
styles={{
|
|
50
|
+
body: {
|
|
51
|
+
maxHeight: 'calc(90vh - 200px)',
|
|
52
|
+
overflowY: 'auto',
|
|
53
|
+
paddingTop: 16,
|
|
54
|
+
paddingBottom: 16,
|
|
55
|
+
},
|
|
56
|
+
...(style ? { ...style } : {}),
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</Modal>
|
|
61
|
+
</>
|
|
62
|
+
);
|
|
63
|
+
}
|
package/templates/ui/antd/src/components/common/fields/assets/components/check-field.component.tsx
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Checkbox, Typography } from 'antd';
|
|
5
|
+
import type { CheckboxProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
const { Text } = Typography;
|
|
8
|
+
|
|
9
|
+
export function CheckField({
|
|
10
|
+
form, name, labelName, required = false, disabled = false, options = [],
|
|
11
|
+
}: CheckboxProps) {
|
|
12
|
+
const labelEl = labelName ? (
|
|
13
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
14
|
+
{labelName}
|
|
15
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
16
|
+
</label>
|
|
17
|
+
) : null;
|
|
18
|
+
|
|
19
|
+
const checkOptions = (options || []).map((opt) =>
|
|
20
|
+
typeof opt === 'string' ? { label: opt, value: opt } : opt
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (form) {
|
|
24
|
+
return (
|
|
25
|
+
<div style={{ marginBottom: 16 }}>
|
|
26
|
+
{labelEl}
|
|
27
|
+
<Controller
|
|
28
|
+
name={name}
|
|
29
|
+
control={form.control}
|
|
30
|
+
render={({ field, fieldState }) => (
|
|
31
|
+
<div>
|
|
32
|
+
<Checkbox.Group
|
|
33
|
+
{...field}
|
|
34
|
+
disabled={disabled}
|
|
35
|
+
options={checkOptions}
|
|
36
|
+
/>
|
|
37
|
+
{fieldState.error && (
|
|
38
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
39
|
+
{String(fieldState.error.message || '')}
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div style={{ marginBottom: 16 }}>
|
|
51
|
+
{labelEl}
|
|
52
|
+
<Checkbox.Group disabled={disabled} options={checkOptions} />
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { DatePicker, Typography } from 'antd';
|
|
5
|
+
import dayjs from 'dayjs';
|
|
6
|
+
import type { DatePickerProps } from '../../assets/interface/input-props.type';
|
|
7
|
+
|
|
8
|
+
const { Text } = Typography;
|
|
9
|
+
|
|
10
|
+
export function DatePickerField({
|
|
11
|
+
form, name, labelName, placeholder, required = false, disabled = false,
|
|
12
|
+
viewOnly = false, customMessage, picker = 'date',
|
|
13
|
+
}: DatePickerProps) {
|
|
14
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'date';
|
|
15
|
+
|
|
16
|
+
const labelEl = labelName ? (
|
|
17
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
18
|
+
{labelName}
|
|
19
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
20
|
+
</label>
|
|
21
|
+
) : null;
|
|
22
|
+
|
|
23
|
+
if (form) {
|
|
24
|
+
return (
|
|
25
|
+
<div style={{ marginBottom: 16 }}>
|
|
26
|
+
{labelEl}
|
|
27
|
+
{viewOnly ? (
|
|
28
|
+
<div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
|
|
29
|
+
{form.watch(fieldName) ? dayjs(form.watch(fieldName)).format('YYYY-MM-DD') : ''}
|
|
30
|
+
</div>
|
|
31
|
+
) : (
|
|
32
|
+
<Controller
|
|
33
|
+
name={fieldName}
|
|
34
|
+
control={form.control}
|
|
35
|
+
render={({ field, fieldState }) => {
|
|
36
|
+
const isError = !!fieldState.error;
|
|
37
|
+
return (
|
|
38
|
+
<div>
|
|
39
|
+
<DatePicker
|
|
40
|
+
{...field}
|
|
41
|
+
style={{ width: '100%' }}
|
|
42
|
+
placeholder={placeholder || `Select ${labelName || 'date'}`}
|
|
43
|
+
disabled={disabled}
|
|
44
|
+
status={isError ? 'error' : undefined}
|
|
45
|
+
picker={picker}
|
|
46
|
+
value={field.value ? dayjs(field.value) : null}
|
|
47
|
+
onChange={(date) => {
|
|
48
|
+
field.onChange(date ? date.toISOString() : null);
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
{isError && (
|
|
52
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
53
|
+
{String(fieldState.error?.message || '')}
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
{!isError && customMessage && (
|
|
57
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div style={{ marginBottom: 16 }}>
|
|
70
|
+
{labelEl}
|
|
71
|
+
<DatePicker
|
|
72
|
+
style={{ width: '100%' }}
|
|
73
|
+
placeholder={placeholder || `Select ${labelName || 'date'}`}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
picker={picker}
|
|
76
|
+
/>
|
|
77
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
package/templates/ui/antd/src/components/common/fields/assets/components/limit-field.component.tsx
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Select, Typography, Space } from 'antd';
|
|
4
|
+
import type { LimitFieldProps } from '../../assets/interface/input-props.type';
|
|
5
|
+
|
|
6
|
+
const { Text } = Typography;
|
|
7
|
+
|
|
8
|
+
export function LimitField({
|
|
9
|
+
limit, setLimit, totalItems, options = [5, 10, 20, 50, 100],
|
|
10
|
+
}: LimitFieldProps) {
|
|
11
|
+
return (
|
|
12
|
+
<Space>
|
|
13
|
+
<Text type="secondary" style={{ fontSize: 13 }}>Show</Text>
|
|
14
|
+
<Select
|
|
15
|
+
value={limit}
|
|
16
|
+
onChange={(val) => setLimit(val)}
|
|
17
|
+
style={{ width: 80 }}
|
|
18
|
+
options={options.map((o) => ({ label: String(o), value: String(o) }))}
|
|
19
|
+
size="small"
|
|
20
|
+
/>
|
|
21
|
+
<Text type="secondary" style={{ fontSize: 13 }}>
|
|
22
|
+
entries{totalItems !== undefined ? ` (${totalItems} total)` : ''}
|
|
23
|
+
</Text>
|
|
24
|
+
</Space>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Checkbox, Typography, Space } from 'antd';
|
|
5
|
+
import type { CheckboxProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
const { Text } = Typography;
|
|
8
|
+
|
|
9
|
+
export function MultiCheckField({
|
|
10
|
+
form, name, labelName, required = false, disabled = false, options = [],
|
|
11
|
+
}: CheckboxProps) {
|
|
12
|
+
const labelEl = labelName ? (
|
|
13
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
14
|
+
{labelName}
|
|
15
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
16
|
+
</label>
|
|
17
|
+
) : null;
|
|
18
|
+
|
|
19
|
+
const checkOptions = (options || []).map((opt) =>
|
|
20
|
+
typeof opt === 'string' ? { label: opt, value: opt } : opt
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (form) {
|
|
24
|
+
return (
|
|
25
|
+
<div style={{ marginBottom: 16 }}>
|
|
26
|
+
{labelEl}
|
|
27
|
+
<Controller
|
|
28
|
+
name={name}
|
|
29
|
+
control={form.control}
|
|
30
|
+
defaultValue={[]}
|
|
31
|
+
render={({ field, fieldState }) => (
|
|
32
|
+
<div>
|
|
33
|
+
<Checkbox.Group
|
|
34
|
+
{...field}
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
options={checkOptions}
|
|
37
|
+
/>
|
|
38
|
+
{fieldState.error && (
|
|
39
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
40
|
+
{String(fieldState.error.message || '')}
|
|
41
|
+
</div>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
)}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div style={{ marginBottom: 16 }}>
|
|
52
|
+
{labelEl}
|
|
53
|
+
<Checkbox.Group disabled={disabled} options={checkOptions} />
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
package/templates/ui/antd/src/components/common/fields/assets/components/number-field.component.tsx
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { Controller } from 'react-hook-form';
|
|
5
|
+
import { InputNumber } from 'antd';
|
|
6
|
+
import type { NumberInputProps } from '../../assets/interface/input-props.type';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Number — Ant Design numeric input with:
|
|
10
|
+
* - Integer or float mode
|
|
11
|
+
* - Scroll prevention
|
|
12
|
+
* - Minimum value of 0
|
|
13
|
+
* - Edge case handling
|
|
14
|
+
*/
|
|
15
|
+
export function Number({
|
|
16
|
+
form, name, labelName, placeholder, required = false, disabled = false,
|
|
17
|
+
viewOnly = false, numberType = 'float', customMessage,
|
|
18
|
+
min = 0, max, step, prefix, suffix,
|
|
19
|
+
}: NumberInputProps) {
|
|
20
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'number';
|
|
21
|
+
|
|
22
|
+
const labelEl = labelName ? (
|
|
23
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
24
|
+
{labelName}
|
|
25
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
26
|
+
</label>
|
|
27
|
+
) : null;
|
|
28
|
+
|
|
29
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
30
|
+
const allowed = ['Backspace', 'Delete', 'Tab', 'Escape', 'Enter', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
|
|
31
|
+
if (allowed.includes(e.key) || e.ctrlKey || e.metaKey) return;
|
|
32
|
+
if (numberType === 'integer' && !/^\d$/.test(e.key)) { e.preventDefault(); return; }
|
|
33
|
+
if (numberType === 'float') {
|
|
34
|
+
if (e.key === '.' && !(e.target as HTMLInputElement).value.includes('.')) return;
|
|
35
|
+
if (!/^\d$/.test(e.key)) { e.preventDefault(); }
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const commonProps = {
|
|
40
|
+
style: { width: '100%' as const },
|
|
41
|
+
placeholder: placeholder || labelName,
|
|
42
|
+
disabled,
|
|
43
|
+
min,
|
|
44
|
+
max,
|
|
45
|
+
step: step ?? (numberType === 'integer' ? 1 : 0.1),
|
|
46
|
+
precision: numberType === 'integer' ? 0 : undefined,
|
|
47
|
+
stringMode: false as const,
|
|
48
|
+
onKeyDown: handleKeyDown,
|
|
49
|
+
onWheel: (e: React.WheelEvent<HTMLInputElement>) => (e.target as HTMLInputElement).blur(),
|
|
50
|
+
prefix,
|
|
51
|
+
suffix,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (form) {
|
|
55
|
+
return (
|
|
56
|
+
<div style={{ marginBottom: 16 }}>
|
|
57
|
+
{labelEl}
|
|
58
|
+
{viewOnly ? (
|
|
59
|
+
<div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
|
|
60
|
+
{form.watch(fieldName) ?? ''}
|
|
61
|
+
</div>
|
|
62
|
+
) : (
|
|
63
|
+
<Controller
|
|
64
|
+
name={fieldName}
|
|
65
|
+
control={form.control}
|
|
66
|
+
render={({ field, fieldState }) => {
|
|
67
|
+
const isError = !!fieldState.error;
|
|
68
|
+
return (
|
|
69
|
+
<div>
|
|
70
|
+
<InputNumber
|
|
71
|
+
{...field}
|
|
72
|
+
{...commonProps}
|
|
73
|
+
status={isError ? 'error' : undefined}
|
|
74
|
+
onChange={(val) => field.onChange(val ?? '')}
|
|
75
|
+
/>
|
|
76
|
+
{isError && (
|
|
77
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
78
|
+
{String(fieldState.error?.message || '')}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
{!isError && customMessage && (
|
|
82
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div style={{ marginBottom: 16 }}>
|
|
95
|
+
{labelEl}
|
|
96
|
+
<InputNumber {...commonProps} />
|
|
97
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
package/templates/ui/antd/src/components/common/fields/assets/components/otp-field.component.tsx
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Input, Typography } from 'antd';
|
|
5
|
+
import type { OTPProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
const { Text } = Typography;
|
|
8
|
+
|
|
9
|
+
export function OTP({
|
|
10
|
+
form, name, labelName, required = false, disabled = false,
|
|
11
|
+
length = 6, customMessage,
|
|
12
|
+
}: OTPProps) {
|
|
13
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'otp';
|
|
14
|
+
|
|
15
|
+
const labelEl = labelName ? (
|
|
16
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
17
|
+
{labelName}
|
|
18
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
19
|
+
</label>
|
|
20
|
+
) : null;
|
|
21
|
+
|
|
22
|
+
if (form) {
|
|
23
|
+
return (
|
|
24
|
+
<div style={{ marginBottom: 16 }}>
|
|
25
|
+
{labelEl}
|
|
26
|
+
<Controller
|
|
27
|
+
name={fieldName}
|
|
28
|
+
control={form.control}
|
|
29
|
+
render={({ field, fieldState }) => {
|
|
30
|
+
const isError = !!fieldState.error;
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<Input.OTP
|
|
34
|
+
{...field}
|
|
35
|
+
length={length}
|
|
36
|
+
disabled={disabled}
|
|
37
|
+
status={isError ? 'error' : undefined}
|
|
38
|
+
onChange={(val) => field.onChange(val)}
|
|
39
|
+
/>
|
|
40
|
+
{isError && (
|
|
41
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
42
|
+
{String(fieldState.error?.message || '')}
|
|
43
|
+
</div>
|
|
44
|
+
)}
|
|
45
|
+
{!isError && customMessage && (
|
|
46
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div style={{ marginBottom: 16 }}>
|
|
58
|
+
{labelEl}
|
|
59
|
+
<Input.OTP length={length} disabled={disabled} />
|
|
60
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { Controller } from 'react-hook-form';
|
|
6
|
+
import { Input } from 'antd';
|
|
7
|
+
import type { PasswordInputProps } from '../../assets/interface/input-props.type';
|
|
8
|
+
|
|
9
|
+
/** Standard password strength rules */
|
|
10
|
+
const passwordRules = [
|
|
11
|
+
{ label: 'At least 8 characters', test: (val: string) => val.length >= 8 },
|
|
12
|
+
{ label: 'At least one uppercase letter', test: (val: string) => /[A-Z]/.test(val) },
|
|
13
|
+
{ label: 'At least one number', test: (val: string) => /\d/.test(val) },
|
|
14
|
+
{ label: 'At least one special character', test: (val: string) => /[!@#$%^&*(),.?":{}|<>_\-+=~`[\]\\;/]/.test(val) },
|
|
15
|
+
{ label: 'No sequential numbers (e.g. 1234)', test: (val: string) => !/012|123|234|345|456|567|678|789/.test(val) },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function Password({
|
|
19
|
+
form, name, labelName, placeholder, required = false, disabled = false,
|
|
20
|
+
mode = 'normal', customMessage,
|
|
21
|
+
}: PasswordInputProps) {
|
|
22
|
+
const [passwordValue, setPasswordValue] = useState('');
|
|
23
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'password';
|
|
24
|
+
const isValidate = mode === 'validate';
|
|
25
|
+
|
|
26
|
+
const labelEl = (label?: string, req?: boolean) => label ? (
|
|
27
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
28
|
+
{label}
|
|
29
|
+
{req && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
30
|
+
</label>
|
|
31
|
+
) : null;
|
|
32
|
+
|
|
33
|
+
const renderStrengthRules = () => {
|
|
34
|
+
if (!isValidate || !passwordValue || passwordRules.every(r => r.test(passwordValue))) return null;
|
|
35
|
+
return (
|
|
36
|
+
<ul style={{ margin: '8px 0 0', padding: 0, listStyle: 'none', fontSize: '0.8rem' }}>
|
|
37
|
+
{passwordRules.map((rule, i) => {
|
|
38
|
+
const passed = rule.test(passwordValue);
|
|
39
|
+
return (
|
|
40
|
+
<li key={i} style={{
|
|
41
|
+
color: passed ? '#16a34a' : '#94a3b8',
|
|
42
|
+
display: 'flex', alignItems: 'center', gap: 6, padding: '2px 0',
|
|
43
|
+
}}>
|
|
44
|
+
<span style={{
|
|
45
|
+
width: 8, height: 8, borderRadius: '50%', display: 'inline-block',
|
|
46
|
+
background: passed ? '#16a34a' : '#d9d9d9',
|
|
47
|
+
}} />
|
|
48
|
+
{rule.label}
|
|
49
|
+
</li>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</ul>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (form) {
|
|
57
|
+
return (
|
|
58
|
+
<div style={{ marginBottom: 16 }}>
|
|
59
|
+
{labelEl(labelName, required)}
|
|
60
|
+
<Controller
|
|
61
|
+
name={fieldName}
|
|
62
|
+
control={form.control}
|
|
63
|
+
render={({ field, fieldState }) => {
|
|
64
|
+
const isError = !!fieldState.error;
|
|
65
|
+
return (
|
|
66
|
+
<div>
|
|
67
|
+
<Input.Password
|
|
68
|
+
{...field}
|
|
69
|
+
placeholder={placeholder || `Enter ${labelName || 'password'}`}
|
|
70
|
+
disabled={disabled}
|
|
71
|
+
status={isError ? 'error' : undefined}
|
|
72
|
+
onChange={(e) => {
|
|
73
|
+
field.onChange(e);
|
|
74
|
+
setPasswordValue(e.target.value);
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
{renderStrengthRules()}
|
|
78
|
+
{isError && (
|
|
79
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
80
|
+
{String(fieldState.error.message || '')}
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
{!isError && customMessage && (
|
|
84
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
85
|
+
)}
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}}
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div style={{ marginBottom: 16 }}>
|
|
96
|
+
{labelEl(labelName, required)}
|
|
97
|
+
<Input.Password
|
|
98
|
+
placeholder={placeholder || `Enter ${labelName || 'password'}`}
|
|
99
|
+
disabled={disabled}
|
|
100
|
+
onChange={(e) => setPasswordValue(e.target.value)}
|
|
101
|
+
/>
|
|
102
|
+
{renderStrengthRules()}
|
|
103
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
}
|