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,78 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { Controller } from 'react-hook-form';
|
|
5
|
+
import { Input } from 'antd';
|
|
6
|
+
import { maskString } from '@/lib/utils.util';
|
|
7
|
+
import type { PhoneNumberProps } from '../../assets/interface/input-props.type';
|
|
8
|
+
|
|
9
|
+
export function PhoneNumber({
|
|
10
|
+
form, name, labelName, placeholder, required = false, disabled = false,
|
|
11
|
+
viewOnly = false, customMessage, onValueChange, hasPhone = false,
|
|
12
|
+
}: PhoneNumberProps) {
|
|
13
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'phone';
|
|
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
|
+
{viewOnly ? (
|
|
27
|
+
<div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
|
|
28
|
+
{hasPhone ? maskString(form.watch(fieldName) || '') : form.watch(fieldName) || ''}
|
|
29
|
+
</div>
|
|
30
|
+
) : (
|
|
31
|
+
<Controller
|
|
32
|
+
name={fieldName}
|
|
33
|
+
control={form.control}
|
|
34
|
+
render={({ field, fieldState }) => {
|
|
35
|
+
const isError = !!fieldState.error;
|
|
36
|
+
return (
|
|
37
|
+
<div>
|
|
38
|
+
<Input
|
|
39
|
+
{...field}
|
|
40
|
+
type="tel"
|
|
41
|
+
placeholder={placeholder || `Enter ${labelName || 'phone number'}`}
|
|
42
|
+
disabled={disabled}
|
|
43
|
+
status={isError ? 'error' : undefined}
|
|
44
|
+
maxLength={20}
|
|
45
|
+
onChange={(e) => {
|
|
46
|
+
field.onChange(e);
|
|
47
|
+
onValueChange?.(e.target.value);
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
{isError && (
|
|
51
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
52
|
+
{String(fieldState.error?.message || '')}
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
{!isError && customMessage && (
|
|
56
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div style={{ marginBottom: 16 }}>
|
|
69
|
+
{labelEl}
|
|
70
|
+
<Input
|
|
71
|
+
type="tel"
|
|
72
|
+
placeholder={placeholder || `Enter ${labelName || 'phone number'}`}
|
|
73
|
+
disabled={disabled}
|
|
74
|
+
/>
|
|
75
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
package/templates/ui/antd/src/components/common/fields/assets/components/radio-field.component.tsx
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Radio, Typography } from 'antd';
|
|
5
|
+
import type { RadioProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
const { Text } = Typography;
|
|
8
|
+
|
|
9
|
+
export function RadioField({
|
|
10
|
+
form, name, labelName, required = false, disabled = false, options = [],
|
|
11
|
+
}: RadioProps) {
|
|
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 radioOptions = (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
|
+
<Radio.Group
|
|
33
|
+
{...field}
|
|
34
|
+
disabled={disabled}
|
|
35
|
+
options={radioOptions}
|
|
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
|
+
<Radio.Group disabled={disabled} options={radioOptions} />
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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 { RangeDatePickerProps } from '../../assets/interface/input-props.type';
|
|
7
|
+
|
|
8
|
+
const { RangePicker } = DatePicker;
|
|
9
|
+
const { Text } = Typography;
|
|
10
|
+
|
|
11
|
+
export function RangeDatePicker({
|
|
12
|
+
form, name, labelName, required = false, disabled = false, customMessage,
|
|
13
|
+
}: RangeDatePickerProps) {
|
|
14
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'dateRange';
|
|
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
|
+
<Controller
|
|
28
|
+
name={fieldName}
|
|
29
|
+
control={form.control}
|
|
30
|
+
render={({ field, fieldState }) => {
|
|
31
|
+
const isError = !!fieldState.error;
|
|
32
|
+
return (
|
|
33
|
+
<div>
|
|
34
|
+
<RangePicker
|
|
35
|
+
style={{ width: '100%' }}
|
|
36
|
+
disabled={disabled}
|
|
37
|
+
status={isError ? 'error' : undefined}
|
|
38
|
+
value={field.value ? field.value.map((d: string | null) => d ? dayjs(d) : null) : null}
|
|
39
|
+
onChange={(dates) => {
|
|
40
|
+
field.onChange(dates ? dates.map((d) => d?.toISOString() || null) : [null, null]);
|
|
41
|
+
}}
|
|
42
|
+
/>
|
|
43
|
+
{isError && (
|
|
44
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
45
|
+
{String(fieldState.error?.message || '')}
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
{!isError && customMessage && (
|
|
49
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div style={{ marginBottom: 16 }}>
|
|
61
|
+
{labelEl}
|
|
62
|
+
<RangePicker style={{ width: '100%' }} disabled={disabled} />
|
|
63
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
package/templates/ui/antd/src/components/common/fields/assets/components/search-field.component.tsx
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Input } from 'antd';
|
|
4
|
+
import type { SearchFieldProps } from '../../assets/interface/input-props.type';
|
|
5
|
+
|
|
6
|
+
const { Search: AntSearch } = Input;
|
|
7
|
+
|
|
8
|
+
export function SearchField({
|
|
9
|
+
placeholder, onSearch, value, setValue, loading,
|
|
10
|
+
}: SearchFieldProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div style={{ marginBottom: 16 }}>
|
|
13
|
+
<AntSearch
|
|
14
|
+
placeholder={placeholder || 'Search...'}
|
|
15
|
+
value={value}
|
|
16
|
+
onChange={(e) => setValue?.(e.target.value)}
|
|
17
|
+
onSearch={(val) => onSearch?.(val)}
|
|
18
|
+
loading={loading}
|
|
19
|
+
allowClear
|
|
20
|
+
enterButton
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
package/templates/ui/antd/src/components/common/fields/assets/components/select-field.component.tsx
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Select } from 'antd';
|
|
5
|
+
import type { SelectFieldProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
export function SelectField({
|
|
8
|
+
form, name, labelName, required = false, disabled = false,
|
|
9
|
+
options = [], placeholder, showSearch = true, type = 'single',
|
|
10
|
+
viewOnly = false, onValueChange, isLoading = false, onSearch, customMessage,
|
|
11
|
+
}: SelectFieldProps) {
|
|
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 normalizedOptions = (options || []).map((opt) =>
|
|
20
|
+
typeof opt === 'string' ? { label: opt, value: opt } : opt
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (viewOnly) {
|
|
24
|
+
return (
|
|
25
|
+
<div style={{ marginBottom: 16 }}>
|
|
26
|
+
{labelEl}
|
|
27
|
+
<div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
|
|
28
|
+
{form?.watch(name) ? (
|
|
29
|
+
type === 'multiple'
|
|
30
|
+
? (form.watch(name) || []).join(', ')
|
|
31
|
+
: normalizedOptions.find((o) => o.value === form.watch(name))?.label || form.watch(name)
|
|
32
|
+
) : ''}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div style={{ marginBottom: 16 }}>
|
|
40
|
+
{labelEl}
|
|
41
|
+
<Controller
|
|
42
|
+
name={name}
|
|
43
|
+
control={form?.control}
|
|
44
|
+
defaultValue={type === 'multiple' ? [] : undefined}
|
|
45
|
+
render={({ field, fieldState }) => {
|
|
46
|
+
const isError = !!fieldState?.error;
|
|
47
|
+
return (
|
|
48
|
+
<div>
|
|
49
|
+
<Select
|
|
50
|
+
{...field}
|
|
51
|
+
style={{ width: '100%' }}
|
|
52
|
+
placeholder={placeholder || `Select ${labelName || 'option'}`}
|
|
53
|
+
disabled={disabled || isLoading}
|
|
54
|
+
status={isError ? 'error' : undefined}
|
|
55
|
+
loading={isLoading}
|
|
56
|
+
mode={type === 'multiple' ? 'multiple' : undefined}
|
|
57
|
+
showSearch={showSearch}
|
|
58
|
+
filterOption={showSearch ? (input: string, option?: any) =>
|
|
59
|
+
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
|
60
|
+
: undefined}
|
|
61
|
+
options={normalizedOptions}
|
|
62
|
+
onSearch={onSearch}
|
|
63
|
+
onChange={(val) => {
|
|
64
|
+
field.onChange(val);
|
|
65
|
+
onValueChange?.(val);
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
{isError && (
|
|
69
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
70
|
+
{String(fieldState.error?.message || '')}
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
{!isError && customMessage && (
|
|
74
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
75
|
+
)}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
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 SingleCheckField({
|
|
10
|
+
form, name, labelName, required = false, disabled = false,
|
|
11
|
+
}: CheckboxProps) {
|
|
12
|
+
if (form) {
|
|
13
|
+
return (
|
|
14
|
+
<div style={{ marginBottom: 16 }}>
|
|
15
|
+
<Controller
|
|
16
|
+
name={name}
|
|
17
|
+
control={form.control}
|
|
18
|
+
defaultValue={false}
|
|
19
|
+
render={({ field, fieldState }) => (
|
|
20
|
+
<div>
|
|
21
|
+
<Checkbox
|
|
22
|
+
{...field}
|
|
23
|
+
checked={!!field.value}
|
|
24
|
+
disabled={disabled}
|
|
25
|
+
onChange={(e) => field.onChange(e.target.checked)}
|
|
26
|
+
>
|
|
27
|
+
{labelName}
|
|
28
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
29
|
+
</Checkbox>
|
|
30
|
+
{fieldState.error && (
|
|
31
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
32
|
+
{String(fieldState.error.message || '')}
|
|
33
|
+
</div>
|
|
34
|
+
)}
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div style={{ marginBottom: 16 }}>
|
|
44
|
+
<Checkbox disabled={disabled}>
|
|
45
|
+
{labelName}
|
|
46
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
47
|
+
</Checkbox>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Select } from 'antd';
|
|
5
|
+
import type { SingleSelectProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
export function SingleSelectField({
|
|
8
|
+
form, name, labelName, placeholder, required = false, disabled = false,
|
|
9
|
+
options = [], viewOnly = false, isLoading = false, defaultValue,
|
|
10
|
+
onValueChange, customMessage,
|
|
11
|
+
}: SingleSelectProps) {
|
|
12
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'singleselect';
|
|
13
|
+
|
|
14
|
+
const labelEl = labelName ? (
|
|
15
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
16
|
+
{labelName}
|
|
17
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
18
|
+
</label>
|
|
19
|
+
) : null;
|
|
20
|
+
|
|
21
|
+
const normalizedOptions = options.map((opt) =>
|
|
22
|
+
typeof opt === 'string' ? { label: opt, value: opt } : opt
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (form) {
|
|
26
|
+
return (
|
|
27
|
+
<div style={{ marginBottom: 16 }}>
|
|
28
|
+
{labelEl}
|
|
29
|
+
{viewOnly ? (
|
|
30
|
+
<div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
|
|
31
|
+
{form.watch(fieldName) || ''}
|
|
32
|
+
</div>
|
|
33
|
+
) : (
|
|
34
|
+
<Controller
|
|
35
|
+
name={fieldName}
|
|
36
|
+
control={form.control}
|
|
37
|
+
defaultValue={defaultValue || ''}
|
|
38
|
+
render={({ field, fieldState }) => {
|
|
39
|
+
const isError = !!fieldState.error;
|
|
40
|
+
return (
|
|
41
|
+
<div>
|
|
42
|
+
<Select
|
|
43
|
+
{...field}
|
|
44
|
+
style={{ width: '100%' }}
|
|
45
|
+
placeholder={placeholder || `Select ${labelName || 'option'}`}
|
|
46
|
+
disabled={disabled || isLoading}
|
|
47
|
+
status={isError ? 'error' : undefined}
|
|
48
|
+
loading={isLoading}
|
|
49
|
+
options={normalizedOptions}
|
|
50
|
+
onChange={(val) => {
|
|
51
|
+
field.onChange(val);
|
|
52
|
+
onValueChange?.(val);
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
{isError && (
|
|
56
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
57
|
+
{String(fieldState.error?.message || '')}
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
{!isError && customMessage && (
|
|
61
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div style={{ marginBottom: 16 }}>
|
|
74
|
+
{labelEl}
|
|
75
|
+
<Select
|
|
76
|
+
style={{ width: '100%' }}
|
|
77
|
+
placeholder={placeholder || `Select ${labelName || 'option'}`}
|
|
78
|
+
disabled={disabled || isLoading}
|
|
79
|
+
loading={isLoading}
|
|
80
|
+
options={normalizedOptions}
|
|
81
|
+
defaultValue={defaultValue}
|
|
82
|
+
/>
|
|
83
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Input } from 'antd';
|
|
5
|
+
import type { NumberInputProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
export function StringNumber({
|
|
8
|
+
form, name, labelName, placeholder, required = false, disabled = false,
|
|
9
|
+
viewOnly = false, customMessage, prefix, suffix,
|
|
10
|
+
}: NumberInputProps) {
|
|
11
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'stringnumber';
|
|
12
|
+
|
|
13
|
+
const labelEl = labelName ? (
|
|
14
|
+
<label className="font-semibold leading-6 text-[14px] tracking-[0.02em]" style={{ display: 'block', marginBottom: 4 }}>
|
|
15
|
+
{labelName}
|
|
16
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
17
|
+
</label>
|
|
18
|
+
) : null;
|
|
19
|
+
|
|
20
|
+
const filterNumber = (val: string) => val.replace(/[^0-9.]/g, '');
|
|
21
|
+
|
|
22
|
+
if (form) {
|
|
23
|
+
return (
|
|
24
|
+
<div style={{ marginBottom: 16 }}>
|
|
25
|
+
{labelEl}
|
|
26
|
+
{viewOnly ? (
|
|
27
|
+
<div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa' }}>
|
|
28
|
+
{form.watch(fieldName) || ''}
|
|
29
|
+
</div>
|
|
30
|
+
) : (
|
|
31
|
+
<Controller
|
|
32
|
+
name={fieldName}
|
|
33
|
+
control={form.control}
|
|
34
|
+
render={({ field, fieldState }) => {
|
|
35
|
+
const isError = !!fieldState.error;
|
|
36
|
+
return (
|
|
37
|
+
<div>
|
|
38
|
+
<Input
|
|
39
|
+
{...field}
|
|
40
|
+
placeholder={placeholder || labelName}
|
|
41
|
+
disabled={disabled}
|
|
42
|
+
status={isError ? 'error' : undefined}
|
|
43
|
+
prefix={prefix}
|
|
44
|
+
suffix={suffix}
|
|
45
|
+
onChange={(e) => {
|
|
46
|
+
const filtered = filterNumber(e.target.value);
|
|
47
|
+
field.onChange({ target: { value: filtered } });
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
{isError && (
|
|
51
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
52
|
+
{String(fieldState.error?.message || '')}
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
{!isError && customMessage && (
|
|
56
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div style={{ marginBottom: 16 }}>
|
|
69
|
+
{labelEl}
|
|
70
|
+
<Input
|
|
71
|
+
placeholder={placeholder || labelName}
|
|
72
|
+
value={value}
|
|
73
|
+
onChange={(e) => setValue?.(filterNumber(e.target.value))}
|
|
74
|
+
prefix={prefix}
|
|
75
|
+
suffix={suffix}
|
|
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/switch-field.component.tsx
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Switch, Typography } from 'antd';
|
|
5
|
+
import type { SwitchProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
const { Text } = Typography;
|
|
8
|
+
|
|
9
|
+
export function SwitchField({
|
|
10
|
+
form, name, labelName, required = false, disabled = false,
|
|
11
|
+
viewOnly = false, customMessage, description, border = false,
|
|
12
|
+
value, setValue, onCheckedChange,
|
|
13
|
+
}: SwitchProps) {
|
|
14
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'switch';
|
|
15
|
+
|
|
16
|
+
const labelEl = labelName ? (
|
|
17
|
+
<div style={{ marginBottom: 4 }}>
|
|
18
|
+
<Text strong style={{ fontSize: 14 }}>
|
|
19
|
+
{labelName}
|
|
20
|
+
{required && <span style={{ color: '#ff4d4f', marginLeft: 2 }}>*</span>}
|
|
21
|
+
</Text>
|
|
22
|
+
{description && <Text type="secondary" style={{ display: 'block', fontSize: 12 }}>{description}</Text>}
|
|
23
|
+
</div>
|
|
24
|
+
) : null;
|
|
25
|
+
|
|
26
|
+
const renderSwitch = (checked: boolean, onChange: (v: boolean) => void) => (
|
|
27
|
+
<div style={{
|
|
28
|
+
display: 'flex', alignItems: 'center', gap: 8,
|
|
29
|
+
padding: border ? 12 : 0,
|
|
30
|
+
border: border ? '1px solid #d9d9d9' : 'none',
|
|
31
|
+
borderRadius: border ? 6 : 0,
|
|
32
|
+
}}>
|
|
33
|
+
<Switch
|
|
34
|
+
checked={checked}
|
|
35
|
+
onChange={(v) => { onChange(v); onCheckedChange?.(v); }}
|
|
36
|
+
disabled={disabled || viewOnly}
|
|
37
|
+
/>
|
|
38
|
+
{labelEl}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (form) {
|
|
43
|
+
return (
|
|
44
|
+
<div style={{ marginBottom: 16 }}>
|
|
45
|
+
<Controller
|
|
46
|
+
name={fieldName}
|
|
47
|
+
control={form.control}
|
|
48
|
+
defaultValue={false}
|
|
49
|
+
render={({ field }) => renderSwitch(!!field.value, (v) => { field.onChange(v); setValue?.(v); })}
|
|
50
|
+
/>
|
|
51
|
+
{customMessage && <Text type="secondary" style={{ fontSize: 12 }}>{customMessage}</Text>}
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div style={{ marginBottom: 16 }}>
|
|
58
|
+
{renderSwitch(!!value, (v) => setValue?.(v))}
|
|
59
|
+
{customMessage && <Text type="secondary" style={{ fontSize: 12 }}>{customMessage}</Text>}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Controller } from 'react-hook-form';
|
|
4
|
+
import { Input } from 'antd';
|
|
5
|
+
import type { TextAreaInputProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
const { TextArea: AntTextArea } = Input;
|
|
8
|
+
|
|
9
|
+
export function TextArea({
|
|
10
|
+
form, name, labelName, placeholder, required = false, disabled = false,
|
|
11
|
+
viewOnly = false, rows = 3, customMessage, value, setValue,
|
|
12
|
+
maxLength, showCount,
|
|
13
|
+
}: TextAreaInputProps) {
|
|
14
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || 'textarea';
|
|
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', whiteSpace: 'pre-wrap' }}>
|
|
29
|
+
{form.watch(fieldName) || ''}
|
|
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
|
+
<AntTextArea
|
|
40
|
+
{...field}
|
|
41
|
+
rows={rows}
|
|
42
|
+
placeholder={placeholder || labelName}
|
|
43
|
+
disabled={disabled}
|
|
44
|
+
status={isError ? 'error' : undefined}
|
|
45
|
+
maxLength={maxLength}
|
|
46
|
+
showCount={showCount}
|
|
47
|
+
/>
|
|
48
|
+
{isError && (
|
|
49
|
+
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
|
|
50
|
+
{String(fieldState.error?.message || '')}
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
{!isError && customMessage && (
|
|
54
|
+
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div style={{ marginBottom: 16 }}>
|
|
67
|
+
{labelEl}
|
|
68
|
+
{viewOnly ? (
|
|
69
|
+
<div style={{ padding: '4px 11px', border: '1px solid #d9d9d9', borderRadius: 6, minHeight: 32, background: '#fafafa', whiteSpace: 'pre-wrap' }}>
|
|
70
|
+
{value || ''}
|
|
71
|
+
</div>
|
|
72
|
+
) : (
|
|
73
|
+
<AntTextArea
|
|
74
|
+
rows={rows}
|
|
75
|
+
placeholder={placeholder || labelName}
|
|
76
|
+
value={value}
|
|
77
|
+
onChange={(e) => setValue?.(e.target.value)}
|
|
78
|
+
maxLength={maxLength}
|
|
79
|
+
showCount={showCount}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
{customMessage && <div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>{customMessage}</div>}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|