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
package/templates/ui/mui/src/components/common/fields/assets/components/number-field.component.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext } from 'react-hook-form';
|
|
4
|
+
import TextField from '@mui/material/TextField';
|
|
5
|
+
import type { NumberInputProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Number — MUI numeric input with scroll prevention and key filtering.
|
|
9
|
+
*/
|
|
10
|
+
export function Number({ form, name, labelName, placeholder, required, disabled, viewOnly, numberType = 'float', customMessage }: NumberInputProps) {
|
|
11
|
+
const formContext = form ? useFormContext() : null;
|
|
12
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
13
|
+
|
|
14
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
15
|
+
const allowed = ['Backspace', 'Delete', 'Tab', 'Escape', 'Enter', 'ArrowLeft', 'ArrowRight', 'Home', 'End'];
|
|
16
|
+
if (allowed.includes(e.key) || e.ctrlKey || e.metaKey) return;
|
|
17
|
+
|
|
18
|
+
if (numberType === 'integer' && !/^\d$/.test(e.key)) { e.preventDefault(); return; }
|
|
19
|
+
if (numberType === 'float') {
|
|
20
|
+
if (e.key === '.' && !(e.target as HTMLInputElement).value.includes('.')) return;
|
|
21
|
+
if (!/^\d$/.test(e.key)) { e.preventDefault(); }
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const commonProps = {
|
|
26
|
+
fullWidth: true as const,
|
|
27
|
+
variant: 'outlined' as const,
|
|
28
|
+
label: labelName,
|
|
29
|
+
placeholder,
|
|
30
|
+
required,
|
|
31
|
+
disabled: disabled || viewOnly,
|
|
32
|
+
type: 'number' as const,
|
|
33
|
+
helperText: customMessage,
|
|
34
|
+
InputProps: {
|
|
35
|
+
readOnly: viewOnly,
|
|
36
|
+
onWheel: (e: React.WheelEvent<HTMLInputElement>) => (e.target as HTMLInputElement).blur(),
|
|
37
|
+
inputProps: { min: 0, step: numberType === 'float' ? 'any' : '0', onKeyDown: handleKeyDown },
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (form && formContext) {
|
|
42
|
+
return (
|
|
43
|
+
<TextField
|
|
44
|
+
{...commonProps}
|
|
45
|
+
error={!!formContext.getFieldState(fieldName, formContext.formState).error}
|
|
46
|
+
helperText={formContext.getFieldState(fieldName, formContext.formState).error?.message || customMessage}
|
|
47
|
+
{...formContext.register(fieldName, {
|
|
48
|
+
setValueAs: (v: string) => {
|
|
49
|
+
if (v === '') return '';
|
|
50
|
+
const parsed = numberType === 'float' ? parseFloat(v) : parseInt(v, 10);
|
|
51
|
+
return isNaN(parsed) || parsed < 0 ? '' : parsed;
|
|
52
|
+
},
|
|
53
|
+
})}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return <TextField {...commonProps} />;
|
|
59
|
+
}
|
package/templates/ui/mui/src/components/common/fields/assets/components/password-field.component.tsx
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { useFormContext } from 'react-hook-form';
|
|
5
|
+
import TextField from '@mui/material/TextField';
|
|
6
|
+
import IconButton from '@mui/material/IconButton';
|
|
7
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
8
|
+
import Visibility from '@mui/icons-material/Visibility';
|
|
9
|
+
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
|
10
|
+
import type { PasswordInputProps } from '../../assets/interface/input-props.type';
|
|
11
|
+
|
|
12
|
+
/** Standard password strength rules */
|
|
13
|
+
const passwordRules = [
|
|
14
|
+
{ label: 'At least 8 characters', test: (val: string) => val.length >= 8 },
|
|
15
|
+
{ label: 'At least one uppercase letter', test: (val: string) => /[A-Z]/.test(val) },
|
|
16
|
+
{ label: 'At least one number', test: (val: string) => /\d/.test(val) },
|
|
17
|
+
{ label: 'At least one special character', test: (val: string) => /[!@#$%^&*(),.?":{}|<>_\-+=~`[\]\\;/]/.test(val) },
|
|
18
|
+
{ label: 'No sequential numbers (e.g. 1234)', test: (val: string) => !/012|123|234|345|456|567|678|789/.test(val) },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function Password({ form, name, labelName, placeholder, required, disabled, mode = 'normal', customMessage }: PasswordInputProps) {
|
|
22
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
23
|
+
const [passwordValue, setPasswordValue] = useState('');
|
|
24
|
+
const formContext = form ? useFormContext() : null;
|
|
25
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
26
|
+
const isValidate = mode === 'validate';
|
|
27
|
+
|
|
28
|
+
const endAdornment = (
|
|
29
|
+
<InputAdornment position="end">
|
|
30
|
+
<IconButton onClick={() => setShowPassword(!showPassword)} edge="end" size="small">
|
|
31
|
+
{showPassword ? <VisibilityOff /> : <Visibility />}
|
|
32
|
+
</IconButton>
|
|
33
|
+
</InputAdornment>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const strengthRules = isValidate && passwordValue && !passwordRules.every(r => r.test(passwordValue)) ? (
|
|
37
|
+
<ul style={{ margin: '4px 0 0', padding: 0, listStyle: 'none', fontSize: '0.8rem' }}>
|
|
38
|
+
{passwordRules.map((rule, i) => {
|
|
39
|
+
const passed = rule.test(passwordValue);
|
|
40
|
+
return (
|
|
41
|
+
<li key={i} style={{ color: passed ? '#16a34a' : '#94a3b8', display: 'flex', alignItems: 'center', gap: 6, padding: '1px 0' }}>
|
|
42
|
+
<span style={{ width: 8, height: 8, borderRadius: '50%', display: 'inline-block', background: passed ? '#16a34a' : '#cbd5e1' }} />
|
|
43
|
+
{rule.label}
|
|
44
|
+
</li>
|
|
45
|
+
);
|
|
46
|
+
})}
|
|
47
|
+
</ul>
|
|
48
|
+
) : null;
|
|
49
|
+
|
|
50
|
+
const commonProps = {
|
|
51
|
+
fullWidth: true as const,
|
|
52
|
+
variant: 'outlined' as const,
|
|
53
|
+
label: labelName,
|
|
54
|
+
placeholder,
|
|
55
|
+
required,
|
|
56
|
+
disabled,
|
|
57
|
+
type: showPassword ? 'text' as const : 'password' as const,
|
|
58
|
+
InputProps: { endAdornment },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (form && formContext) {
|
|
62
|
+
return (
|
|
63
|
+
<div>
|
|
64
|
+
<TextField
|
|
65
|
+
{...commonProps}
|
|
66
|
+
error={!!formContext.getFieldState(fieldName, formContext.formState).error}
|
|
67
|
+
helperText={formContext.getFieldState(fieldName, formContext.formState).error?.message || customMessage}
|
|
68
|
+
{...formContext.register(fieldName, {
|
|
69
|
+
onChange: (e) => setPasswordValue(e.target.value),
|
|
70
|
+
})}
|
|
71
|
+
/>
|
|
72
|
+
{strengthRules}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div>
|
|
79
|
+
<TextField
|
|
80
|
+
{...commonProps}
|
|
81
|
+
helperText={customMessage}
|
|
82
|
+
onChange={(e) => setPasswordValue(e.target.value)}
|
|
83
|
+
/>
|
|
84
|
+
{strengthRules}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { useFormContext } from 'react-hook-form';
|
|
5
|
+
import TextField from '@mui/material/TextField';
|
|
6
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
7
|
+
import type { PhoneNumberProps } from '../../assets/interface/input-props.type';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* PhoneNumber — MUI phone input with country code prefix.
|
|
11
|
+
*/
|
|
12
|
+
export function PhoneNumber({
|
|
13
|
+
form, name, labelName, placeholder = '+1 (555) 000-0000', required, disabled,
|
|
14
|
+
viewOnly, customMessage, onValueChange,
|
|
15
|
+
}: PhoneNumberProps) {
|
|
16
|
+
const formContext = form ? useFormContext() : null;
|
|
17
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
18
|
+
|
|
19
|
+
const commonProps = {
|
|
20
|
+
fullWidth: true as const,
|
|
21
|
+
variant: 'outlined' as const,
|
|
22
|
+
label: labelName,
|
|
23
|
+
placeholder,
|
|
24
|
+
required,
|
|
25
|
+
disabled: disabled || viewOnly,
|
|
26
|
+
helperText: customMessage,
|
|
27
|
+
InputProps: {
|
|
28
|
+
startAdornment: <InputAdornment position="start">+</InputAdornment>,
|
|
29
|
+
readOnly: viewOnly,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (form && formContext) {
|
|
34
|
+
return (
|
|
35
|
+
<TextField
|
|
36
|
+
{...commonProps}
|
|
37
|
+
error={!!formContext.getFieldState(fieldName, formContext.formState).error}
|
|
38
|
+
helperText={formContext.getFieldState(fieldName, formContext.formState).error?.message || customMessage}
|
|
39
|
+
{...formContext.register(fieldName, {
|
|
40
|
+
onChange: (e) => { onValueChange?.(e.target.value); },
|
|
41
|
+
})}
|
|
42
|
+
type="tel"
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return <TextField {...commonProps} type="tel" />;
|
|
48
|
+
}
|
package/templates/ui/mui/src/components/common/fields/assets/components/radio-field.component.tsx
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext, Controller } from 'react-hook-form';
|
|
4
|
+
import FormControl from '@mui/material/FormControl';
|
|
5
|
+
import FormLabel from '@mui/material/FormLabel';
|
|
6
|
+
import RadioGroup from '@mui/material/RadioGroup';
|
|
7
|
+
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
8
|
+
import FormHelperText from '@mui/material/FormHelperText';
|
|
9
|
+
import Radio from '@mui/material/Radio';
|
|
10
|
+
import type { RadioProps } from '../../assets/interface/input-props.type';
|
|
11
|
+
|
|
12
|
+
export function RadioField({ form, name, labelName, required, disabled, options = [] }: RadioProps) {
|
|
13
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
14
|
+
|
|
15
|
+
if (form) {
|
|
16
|
+
return (
|
|
17
|
+
<Controller
|
|
18
|
+
name={fieldName}
|
|
19
|
+
control={form.control}
|
|
20
|
+
defaultValue=""
|
|
21
|
+
render={({ field, fieldState }) => (
|
|
22
|
+
<FormControl error={!!fieldState.error} required={required} disabled={disabled} component="fieldset">
|
|
23
|
+
{labelName && <FormLabel component="legend">{labelName}</FormLabel>}
|
|
24
|
+
<RadioGroup {...field}>
|
|
25
|
+
{options.map((opt) => (
|
|
26
|
+
<FormControlLabel key={opt.value} value={opt.value} control={<Radio />} label={opt.label} />
|
|
27
|
+
))}
|
|
28
|
+
</RadioGroup>
|
|
29
|
+
{fieldState.error && <FormHelperText>{fieldState.error.message}</FormHelperText>}
|
|
30
|
+
</FormControl>
|
|
31
|
+
)}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
package/templates/ui/mui/src/components/common/fields/assets/components/search-field.component.tsx
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import TextField from '@mui/material/TextField';
|
|
4
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
5
|
+
import IconButton from '@mui/material/IconButton';
|
|
6
|
+
import SearchIcon from '@mui/icons-material/Search';
|
|
7
|
+
import ClearIcon from '@mui/icons-material/Clear';
|
|
8
|
+
import type { SearchFieldProps } from '../../assets/interface/input-props.type';
|
|
9
|
+
|
|
10
|
+
export function SearchField({ placeholder = 'Search...', onSearch, value, setValue }: SearchFieldProps) {
|
|
11
|
+
const handleClear = () => {
|
|
12
|
+
setValue?.('');
|
|
13
|
+
onSearch?.('');
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<TextField
|
|
18
|
+
fullWidth
|
|
19
|
+
variant="outlined"
|
|
20
|
+
placeholder={placeholder}
|
|
21
|
+
size="small"
|
|
22
|
+
value={value || ''}
|
|
23
|
+
onChange={(e) => setValue?.(e.target.value)}
|
|
24
|
+
onKeyDown={(e) => { if (e.key === 'Enter') onSearch?.(e.currentTarget.value); }}
|
|
25
|
+
InputProps={{
|
|
26
|
+
startAdornment: (
|
|
27
|
+
<InputAdornment position="start">
|
|
28
|
+
<SearchIcon color="action" />
|
|
29
|
+
</InputAdornment>
|
|
30
|
+
),
|
|
31
|
+
endAdornment: value ? (
|
|
32
|
+
<InputAdornment position="end">
|
|
33
|
+
<IconButton size="small" onClick={handleClear}>
|
|
34
|
+
<ClearIcon fontSize="small" />
|
|
35
|
+
</IconButton>
|
|
36
|
+
</InputAdornment>
|
|
37
|
+
) : undefined,
|
|
38
|
+
}}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
}
|
package/templates/ui/mui/src/components/common/fields/assets/components/select-field.component.tsx
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext, Controller } from 'react-hook-form';
|
|
4
|
+
import FormControl from '@mui/material/FormControl';
|
|
5
|
+
import InputLabel from '@mui/material/InputLabel';
|
|
6
|
+
import Select from '@mui/material/Select';
|
|
7
|
+
import MenuItem from '@mui/material/MenuItem';
|
|
8
|
+
import FormHelperText from '@mui/material/FormHelperText';
|
|
9
|
+
import Chip from '@mui/material/Chip';
|
|
10
|
+
import Box from '@mui/material/Box';
|
|
11
|
+
import type { SelectFieldProps } from '../../assets/interface/input-props.type';
|
|
12
|
+
|
|
13
|
+
export function SelectField({ form, name, labelName, required, disabled, options = [], placeholder, showSearch, type = 'single', viewOnly, onValueChange, isLoading, customMessage }: SelectFieldProps) {
|
|
14
|
+
const normalisedOptions = options.map((opt) =>
|
|
15
|
+
typeof opt === 'string' ? { value: opt, label: opt } : opt
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (form) {
|
|
19
|
+
return (
|
|
20
|
+
<Controller
|
|
21
|
+
name={name}
|
|
22
|
+
control={form.control}
|
|
23
|
+
defaultValue={type === 'multiple' ? [] : ''}
|
|
24
|
+
render={({ field, fieldState }) => (
|
|
25
|
+
<FormControl fullWidth error={!!fieldState.error} required={required} disabled={disabled || viewOnly || isLoading}>
|
|
26
|
+
<InputLabel>{labelName}</InputLabel>
|
|
27
|
+
<Select
|
|
28
|
+
{...field}
|
|
29
|
+
label={labelName}
|
|
30
|
+
multiple={type === 'multiple'}
|
|
31
|
+
onChange={(e) => {
|
|
32
|
+
field.onChange(e);
|
|
33
|
+
onValueChange?.(e.target.value);
|
|
34
|
+
}}
|
|
35
|
+
renderValue={(selected: any) =>
|
|
36
|
+
type === 'multiple' ? (
|
|
37
|
+
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
|
38
|
+
{(selected as string[]).map((v) => <Chip key={v} label={v} size="small" />)}
|
|
39
|
+
</Box>
|
|
40
|
+
) : (
|
|
41
|
+
normalisedOptions.find((o) => o.value === selected)?.label || selected
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
>
|
|
45
|
+
{placeholder && type !== 'multiple' && (
|
|
46
|
+
<MenuItem value="" disabled><em>{placeholder}</em></MenuItem>
|
|
47
|
+
)}
|
|
48
|
+
{normalisedOptions.map((opt) => (
|
|
49
|
+
<MenuItem key={opt.value} value={opt.value} disabled={opt.disabled}>
|
|
50
|
+
{opt.image && <Box component="img" src={opt.image} sx={{ width: 20, height: 20, mr: 1, borderRadius: '50%' }} />}
|
|
51
|
+
{opt.flag && <span style={{ marginRight: 8 }}>{opt.flag}</span>}
|
|
52
|
+
{opt.label}
|
|
53
|
+
</MenuItem>
|
|
54
|
+
))}
|
|
55
|
+
</Select>
|
|
56
|
+
{(fieldState.error?.message || customMessage) && (
|
|
57
|
+
<FormHelperText>{fieldState.error?.message || customMessage}</FormHelperText>
|
|
58
|
+
)}
|
|
59
|
+
</FormControl>
|
|
60
|
+
)}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<FormControl fullWidth disabled={disabled || viewOnly || isLoading} required={required}>
|
|
67
|
+
<InputLabel>{labelName}</InputLabel>
|
|
68
|
+
<Select label={labelName}>
|
|
69
|
+
{normalisedOptions.map((opt) => (
|
|
70
|
+
<MenuItem key={opt.value} value={opt.value} disabled={opt.disabled}>
|
|
71
|
+
{opt.label}
|
|
72
|
+
</MenuItem>
|
|
73
|
+
))}
|
|
74
|
+
</Select>
|
|
75
|
+
</FormControl>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext, Controller } from 'react-hook-form';
|
|
4
|
+
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
5
|
+
import Checkbox from '@mui/material/Checkbox';
|
|
6
|
+
import FormHelperText from '@mui/material/FormHelperText';
|
|
7
|
+
import type { CheckboxProps } from '../../assets/interface/input-props.type';
|
|
8
|
+
|
|
9
|
+
export function SingleCheckField({ form, name, labelName, required, disabled }: CheckboxProps) {
|
|
10
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
11
|
+
|
|
12
|
+
if (form) {
|
|
13
|
+
return (
|
|
14
|
+
<Controller
|
|
15
|
+
name={fieldName}
|
|
16
|
+
control={form.control}
|
|
17
|
+
defaultValue={false}
|
|
18
|
+
render={({ field, fieldState }) => (
|
|
19
|
+
<>
|
|
20
|
+
<FormControlLabel
|
|
21
|
+
control={
|
|
22
|
+
<Checkbox
|
|
23
|
+
checked={!!field.value}
|
|
24
|
+
onChange={(e) => field.onChange(e.target.checked)}
|
|
25
|
+
disabled={disabled}
|
|
26
|
+
required={required}
|
|
27
|
+
/>
|
|
28
|
+
}
|
|
29
|
+
label={labelName}
|
|
30
|
+
/>
|
|
31
|
+
{fieldState.error && <FormHelperText error>{fieldState.error.message}</FormHelperText>}
|
|
32
|
+
</>
|
|
33
|
+
)}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return <FormControlLabel control={<Checkbox disabled={disabled} required={required} />} label={labelName} />;
|
|
39
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext, Controller } from 'react-hook-form';
|
|
4
|
+
import FormControl from '@mui/material/FormControl';
|
|
5
|
+
import InputLabel from '@mui/material/InputLabel';
|
|
6
|
+
import Select from '@mui/material/Select';
|
|
7
|
+
import MenuItem from '@mui/material/MenuItem';
|
|
8
|
+
import FormHelperText from '@mui/material/FormHelperText';
|
|
9
|
+
import type { SingleSelectProps } from '../../assets/interface/input-props.type';
|
|
10
|
+
|
|
11
|
+
export function SingleSelectField({ form, name, labelName, placeholder, required, disabled, options = [], viewOnly, onValueChange, isLoading, defaultValue, customMessage }: SingleSelectProps) {
|
|
12
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
13
|
+
|
|
14
|
+
if (form) {
|
|
15
|
+
return (
|
|
16
|
+
<Controller
|
|
17
|
+
name={fieldName}
|
|
18
|
+
control={form.control}
|
|
19
|
+
defaultValue={defaultValue || ''}
|
|
20
|
+
render={({ field, fieldState }) => (
|
|
21
|
+
<FormControl fullWidth error={!!fieldState.error} required={required} disabled={disabled || viewOnly || isLoading}>
|
|
22
|
+
<InputLabel>{labelName}</InputLabel>
|
|
23
|
+
<Select
|
|
24
|
+
{...field}
|
|
25
|
+
label={labelName}
|
|
26
|
+
onChange={(e) => {
|
|
27
|
+
field.onChange(e);
|
|
28
|
+
onValueChange?.(e.target.value);
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
{placeholder && <MenuItem value="" disabled><em>{placeholder}</em></MenuItem>}
|
|
32
|
+
{options.map((opt) => (
|
|
33
|
+
<MenuItem key={opt} value={opt}>{opt}</MenuItem>
|
|
34
|
+
))}
|
|
35
|
+
</Select>
|
|
36
|
+
{(fieldState.error?.message || customMessage) && (
|
|
37
|
+
<FormHelperText>{fieldState.error?.message || customMessage}</FormHelperText>
|
|
38
|
+
)}
|
|
39
|
+
</FormControl>
|
|
40
|
+
)}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<FormControl fullWidth disabled={disabled || viewOnly || isLoading} required={required}>
|
|
47
|
+
<InputLabel>{labelName}</InputLabel>
|
|
48
|
+
<Select label={labelName} defaultValue={defaultValue || ''}>
|
|
49
|
+
{placeholder && <MenuItem value="" disabled><em>{placeholder}</em></MenuItem>}
|
|
50
|
+
{options.map((opt) => (
|
|
51
|
+
<MenuItem key={opt} value={opt}>{opt}</MenuItem>
|
|
52
|
+
))}
|
|
53
|
+
</Select>
|
|
54
|
+
</FormControl>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext } from 'react-hook-form';
|
|
4
|
+
import TextField from '@mui/material/TextField';
|
|
5
|
+
import type { TextInputProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
export function StringNumber({ form, name, labelName, placeholder, required, disabled, viewOnly, value, setValue, customMessage }: TextInputProps) {
|
|
8
|
+
const formContext = form ? useFormContext() : null;
|
|
9
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
10
|
+
|
|
11
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
12
|
+
const val = e.target.value.replace(/\D/g, '');
|
|
13
|
+
if (form && formContext) {
|
|
14
|
+
formContext.setValue(fieldName, val);
|
|
15
|
+
} else {
|
|
16
|
+
setValue?.(val);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
if (form && formContext) {
|
|
21
|
+
return (
|
|
22
|
+
<TextField
|
|
23
|
+
fullWidth
|
|
24
|
+
variant="outlined"
|
|
25
|
+
label={labelName}
|
|
26
|
+
placeholder={placeholder}
|
|
27
|
+
required={required}
|
|
28
|
+
disabled={disabled || viewOnly}
|
|
29
|
+
error={!!formContext.getFieldState(fieldName, formContext.formState).error}
|
|
30
|
+
helperText={formContext.getFieldState(fieldName, formContext.formState).error?.message || customMessage}
|
|
31
|
+
InputProps={{ readOnly: viewOnly }}
|
|
32
|
+
value={formContext.watch(fieldName) || ''}
|
|
33
|
+
onChange={handleChange}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<TextField
|
|
40
|
+
fullWidth
|
|
41
|
+
variant="outlined"
|
|
42
|
+
label={labelName}
|
|
43
|
+
placeholder={placeholder}
|
|
44
|
+
required={required}
|
|
45
|
+
disabled={disabled || viewOnly}
|
|
46
|
+
value={value || ''}
|
|
47
|
+
onChange={handleChange}
|
|
48
|
+
helperText={customMessage}
|
|
49
|
+
InputProps={{ readOnly: viewOnly }}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
package/templates/ui/mui/src/components/common/fields/assets/components/switch-field.component.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext, Controller } from 'react-hook-form';
|
|
4
|
+
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
5
|
+
import Switch from '@mui/material/Switch';
|
|
6
|
+
import Typography from '@mui/material/Typography';
|
|
7
|
+
import Box from '@mui/material/Box';
|
|
8
|
+
import type { SwitchProps } from '../../assets/interface/input-props.type';
|
|
9
|
+
|
|
10
|
+
export function SwitchField({ form, name, labelName, required, disabled, viewOnly, description, border, value, setValue, onCheckedChange }: SwitchProps) {
|
|
11
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
12
|
+
|
|
13
|
+
const switchEl = (checked: boolean, onChange: (v: boolean) => void) => (
|
|
14
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, p: border ? 2 : 0, border: border ? 1 : 0, borderColor: 'divider', borderRadius: 1 }}>
|
|
15
|
+
<FormControlLabel
|
|
16
|
+
control={<Switch checked={checked} onChange={(_, v) => onChange(v)} disabled={disabled || viewOnly} required={required} />}
|
|
17
|
+
label={<Box><Typography variant="body2">{labelName}</Typography>{description && <Typography variant="caption" color="text.secondary">{description}</Typography>}</Box>}
|
|
18
|
+
sx={{ m: 0 }}
|
|
19
|
+
/>
|
|
20
|
+
</Box>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (form) {
|
|
24
|
+
return (
|
|
25
|
+
<Controller
|
|
26
|
+
name={fieldName}
|
|
27
|
+
control={form.control}
|
|
28
|
+
defaultValue={false}
|
|
29
|
+
render={({ field }) => switchEl(!!field.value, (v) => { field.onChange(v); onCheckedChange?.(v); })}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return switchEl(!!value, (v) => { setValue?.(v); onCheckedChange?.(v); });
|
|
35
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext } from 'react-hook-form';
|
|
4
|
+
import TextField from '@mui/material/TextField';
|
|
5
|
+
import type { TextAreaInputProps } from '../../assets/interface/input-props.type';
|
|
6
|
+
|
|
7
|
+
export function TextArea({ form, name, labelName, placeholder, required, disabled, viewOnly, rows = 3, value, setValue, customMessage }: TextAreaInputProps) {
|
|
8
|
+
const formContext = form ? useFormContext() : null;
|
|
9
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
10
|
+
|
|
11
|
+
if (form && formContext) {
|
|
12
|
+
return (
|
|
13
|
+
<TextField
|
|
14
|
+
fullWidth
|
|
15
|
+
variant="outlined"
|
|
16
|
+
label={labelName}
|
|
17
|
+
placeholder={placeholder}
|
|
18
|
+
required={required}
|
|
19
|
+
disabled={disabled || viewOnly}
|
|
20
|
+
multiline
|
|
21
|
+
rows={rows}
|
|
22
|
+
error={!!formContext.getFieldState(fieldName, formContext.formState).error}
|
|
23
|
+
helperText={formContext.getFieldState(fieldName, formContext.formState).error?.message || customMessage}
|
|
24
|
+
InputProps={{ readOnly: viewOnly }}
|
|
25
|
+
{...formContext.register(fieldName)}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<TextField
|
|
32
|
+
fullWidth
|
|
33
|
+
variant="outlined"
|
|
34
|
+
label={labelName}
|
|
35
|
+
placeholder={placeholder}
|
|
36
|
+
required={required}
|
|
37
|
+
disabled={disabled || viewOnly}
|
|
38
|
+
multiline
|
|
39
|
+
rows={rows}
|
|
40
|
+
value={value || ''}
|
|
41
|
+
onChange={(e) => setValue?.(e.target.value)}
|
|
42
|
+
helperText={customMessage}
|
|
43
|
+
InputProps={{ readOnly: viewOnly }}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
}
|
package/templates/ui/mui/src/components/common/fields/assets/components/text-field.component.tsx
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFormContext } from 'react-hook-form';
|
|
4
|
+
import TextField from '@mui/material/TextField';
|
|
5
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
6
|
+
import type { TextInputProps } from '../../assets/interface/input-props.type';
|
|
7
|
+
|
|
8
|
+
export function Text({ form, name, placeholder, labelName, required, disabled, viewOnly, value, setValue, leftIcon, rightIcon, customMessage }: TextInputProps) {
|
|
9
|
+
const formContext = form ? useFormContext() : null;
|
|
10
|
+
const fieldName = name || labelName?.toLowerCase().replace(/\s+/g, '_') || '';
|
|
11
|
+
|
|
12
|
+
if (form && formContext) {
|
|
13
|
+
return (
|
|
14
|
+
<TextField
|
|
15
|
+
fullWidth
|
|
16
|
+
variant="outlined"
|
|
17
|
+
label={labelName}
|
|
18
|
+
placeholder={placeholder}
|
|
19
|
+
required={required}
|
|
20
|
+
disabled={disabled || viewOnly}
|
|
21
|
+
error={!!formContext.getFieldState(fieldName, formContext.formState).error}
|
|
22
|
+
helperText={formContext.getFieldState(fieldName, formContext.formState).error?.message || customMessage}
|
|
23
|
+
InputProps={{
|
|
24
|
+
startAdornment: leftIcon ? <InputAdornment position="start">{leftIcon}</InputAdornment> : undefined,
|
|
25
|
+
endAdornment: rightIcon ? <InputAdornment position="end">{rightIcon}</InputAdornment> : undefined,
|
|
26
|
+
readOnly: viewOnly,
|
|
27
|
+
}}
|
|
28
|
+
{...formContext.register(fieldName)}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<TextField
|
|
35
|
+
fullWidth
|
|
36
|
+
variant="outlined"
|
|
37
|
+
label={labelName}
|
|
38
|
+
placeholder={placeholder}
|
|
39
|
+
required={required}
|
|
40
|
+
disabled={disabled || viewOnly}
|
|
41
|
+
value={value || ''}
|
|
42
|
+
onChange={(e) => setValue?.(e.target.value)}
|
|
43
|
+
helperText={customMessage}
|
|
44
|
+
InputProps={{
|
|
45
|
+
startAdornment: leftIcon ? <InputAdornment position="start">{leftIcon}</InputAdornment> : undefined,
|
|
46
|
+
endAdornment: rightIcon ? <InputAdornment position="end">{rightIcon}</InputAdornment> : undefined,
|
|
47
|
+
readOnly: viewOnly,
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|