react-data-entry
Version:
React data entry components library
512 lines (424 loc) • 13.6 kB
Markdown
[](https://www.npmjs.com/package/react-data-entry)
[](https://opensource.org/licenses/ISC)
Thư viện React components chuyên dụng cho việc nhập liệu dữ liệu (data entry), được tích hợp sẵn với **React Hook Form** và **Ant Design**, cung cấp các component tối ưu cho form validation và UI/UX hiện đại.
- 🎯 **16+ Components** nhập liệu đa dạng và chuyên biệt
- 🔗 **Tích hợp React Hook Form** sẵn có với validation
- 🎨 **Ant Design UI** đẹp mắt và nhất quán
- 📱 **Responsive** hỗ trợ mọi kích thước màn hình
- ⚡ **Performance** tối ưu với React.memo và useCallback
- 🛡️ **TypeScript** hỗ trợ đầy đủ với type safety
- 🎛️ **Flexible** cấu hình linh hoạt cho mọi use case
- 🔧 **Error Handling** xử lý lỗi tự động và fallback UI
```bash
npm install react-data-entry
```
```bash
yarn add react-data-entry
```
```bash
pnpm add react-data-entry
```
Thư viện yêu cầu các dependencies sau:
```bash
npm install react react-dom react-hook-form
```
Component chính `RenderDataEntry` cung cấp cách sử dụng thống nhất cho tất cả các loại input:
```tsx
import React from "react";
import { useForm } from "react-hook-form";
import { RenderDataEntry, KeyEditType } from "react-data-entry";
const MyForm = () => {
const hookForm = useForm();
const fieldConfig: KeyEditType = {
type: "text",
key: "username",
label: "Tên đăng nhập",
placeholder: "Nhập tên đăng nhập",
rules: { required: "Vui lòng nhập tên đăng nhập" },
width: "300px",
};
return (
<form onSubmit={hookForm.handleSubmit(console.log)}>
<RenderDataEntry data={fieldConfig} hookForm={hookForm} size="large" />
<button type="submit">Gửi</button>
</form>
);
};
```
```tsx
import React from "react";
import { useForm, Controller } from "react-hook-form";
import { CTextfield, CCalendar, CSelector } from "react-data-entry";
const MyForm = () => {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit(console.log)}>
<Controller
name="username"
control={control}
rules={{ required: "Vui lòng nhập tên" }}
render={({ field, fieldState: { error } }) => (
<CTextfield
data={{
label: "Tên đăng nhập",
placeholder: "Nhập tên đăng nhập",
required: true,
message: error?.message,
}}
field={field}
size="large"
/>
)}
/>
</form>
);
};
```
| Component | Type | Mô tả |
| ---------------- | ------------ | ------------------------------------ |
| `CTextfield` | `text` | Input text cơ bản |
| `CInputNumber` | `number` | Input số với validation |
| `CTextarea` | `textarea` | Textarea nhiều dòng |
| `CPassword` | `password` | Input mật khẩu với toggle visibility |
| `CSwitch` | `switch` | Switch on/off |
| `CCalendar` | `date` | Date picker |
| `CRangeCalendar` | `range-date` | Date range picker |
| `CTime` | `time` | Time picker |
| `CSelector` | `select` | Dropdown select (single/multiple) |
| `CCurrency` | `currency` | Input tiền tệ với format |
| `CEditor` | `editor` | Rich text editor (Quill) |
| `CUpload` | `upload` | File upload với preview |
| `CRadio` | `radio` | Radio button group |
| `CLink` | `link` | Link input với validation URL |
| `CColorPicker` | `color` | Color picker |
## 🔧 Cấu hình Field Data
### Interface KeyEditType
```typescript
interface KeyEditType {
type: RenderDataEntryType; // Loại component
key: string; // Field name
label: string; // Nhãn hiển thị
role?: any[] | null; // Phân quyền (nếu có)
width?: string; // Chiều rộng
placeholder?: string; // Placeholder text
direction?: "row" | "column"; // Hướng layout
labelWidth?: string; // Chiều rộng label
rules?: any; // Validation rules
disabled?: boolean; // Vô hiệu hóa
defaultValue?: any; // Giá trị mặc định
// Cho số và currency
max?: number;
min?: number;
suffix?: string;
// Cho date
min_date?: string;
max_date?: string;
picker?: "week" | "month" | "year" | "quarter";
formatCalendar?: string;
// Cho select
type_select?: "multiple" | "single";
options?: any[];
// Cho textfield
type_text?: "text" | "number";
// Cho upload
uploadImage?: any;
length_img?: number;
}
```
```tsx
const textConfig: KeyEditType = {
type: "text",
key: "fullName",
label: "Họ và tên",
placeholder: "Nhập họ và tên đầy đủ",
rules: {
required: "Vui lòng nhập họ và tên",
minLength: {
value: 2,
message: "Tên phải có ít nhất 2 ký tự",
},
},
width: "100%",
direction: "column",
};
```
```tsx
const numberConfig: KeyEditType = {
type: "number",
key: "age",
label: "Tuổi",
placeholder: "Nhập tuổi",
min: 18,
max: 100,
rules: {
required: "Vui lòng nhập tuổi",
min: { value: 18, message: "Tuổi phải từ 18 trở lên" },
},
};
```
```tsx
const dateConfig: KeyEditType = {
type: "date",
key: "birthDate",
label: "Ngày sinh",
picker: "date",
formatCalendar: "DD/MM/YYYY",
min_date: "1900-01-01",
max_date: new Date().toISOString().split("T")[0],
rules: { required: "Vui lòng chọn ngày sinh" },
};
```
```tsx
const selectConfig: KeyEditType = {
type: "select",
key: "skills",
label: "Kỹ năng",
type_select: "multiple",
options: [
{ label: "React", value: "react" },
{ label: "Vue", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Node.js", value: "nodejs" },
],
placeholder: "Chọn kỹ năng của bạn",
};
```
```tsx
const currencyConfig: KeyEditType = {
type: "currency",
key: "salary",
label: "Mức lương mong muốn",
suffix: "VNĐ",
min: 0,
max: 100000000,
placeholder: "Nhập mức lương",
};
```
```tsx
const editorConfig: KeyEditType = {
type: "editor",
key: "description",
label: "Mô tả chi tiết",
placeholder: "Nhập mô tả...",
rules: {
required: "Vui lòng nhập mô tả",
validate: (value: string) => {
const textLength = value.replace(/<[^>]*>/g, "").length;
if (textLength < 10) return "Mô tả phải có ít nhất 10 ký tự";
return true;
},
},
};
```
```tsx
const uploadConfig: KeyEditType = {
type: "upload",
key: "avatar",
label: "Ảnh đại diện",
length_img: 1,
uploadImage: {
accept: "image/*",
maxSize: 5 * 1024 * 1024, // 5MB
listType: "picture-card",
},
};
```
```css
/* Custom styles */
.custom-form-field {
margin-bottom: 16px;
}
.custom-form-field .ant-form-item-label {
font-weight: 600;
}
.custom-form-field .ant-input {
border-radius: 8px;
}
```
```tsx
const fieldConfig: KeyEditType = {
type: "text",
key: "username",
label: "Username",
// className sẽ được áp dụng cho field container
};
```
```tsx
import React from "react";
import { useForm } from "react-hook-form";
import { RenderDataEntry, KeyEditType } from "react-data-entry";
const UserRegistrationForm = () => {
const hookForm = useForm();
const formFields: KeyEditType[] = [
{
type: "text",
key: "username",
label: "Tên đăng nhập",
placeholder: "Nhập tên đăng nhập",
rules: { required: "Vui lòng nhập tên đăng nhập" },
},
{
type: "password",
key: "password",
label: "Mật khẩu",
placeholder: "Nhập mật khẩu",
rules: {
required: "Vui lòng nhập mật khẩu",
minLength: { value: 6, message: "Mật khẩu ít nhất 6 ký tự" },
},
},
{
type: "text",
key: "email",
label: "Email",
placeholder: "Nhập email",
rules: {
required: "Vui lòng nhập email",
pattern: {
value: /^\S+@\S+$/i,
message: "Email không hợp lệ",
},
},
},
{
type: "date",
key: "birthDate",
label: "Ngày sinh",
picker: "date",
rules: { required: "Vui lòng chọn ngày sinh" },
},
{
type: "select",
key: "gender",
label: "Giới tính",
type_select: "single",
options: [
{ label: "Nam", value: "male" },
{ label: "Nữ", value: "female" },
{ label: "Khác", value: "other" },
],
},
];
const onSubmit = (data: any) => {
console.log("Form Data:", data);
};
return (
<form onSubmit={hookForm.handleSubmit(onSubmit)}>
<div style={{ display: "grid", gap: "16px", maxWidth: "600px" }}>
{formFields.map((field) => (
<RenderDataEntry
key={field.key}
data={field}
hookForm={hookForm}
size="large"
/>
))}
</div>
<div style={{ marginTop: "24px" }}>
<button
type="submit"
style={{
padding: "12px 24px",
backgroundColor: "#1890ff",
color: "white",
border: "none",
borderRadius: "6px",
cursor: "pointer",
}}
>
Đăng ký
</button>
</div>
</form>
);
};
export default UserRegistrationForm;
```
| Prop | Type | Default | Mô tả |
| ---------------- | -------------------------------- | --------- | ------------------------ |
| `data` | `KeyEditType` | - | Cấu hình field |
| `hookForm` | `object` | - | React Hook Form instance |
| `disabled` | `boolean` | `false` | Vô hiệu hóa field |
| `size` | `'large' \| 'middle' \| 'small'` | `'large'` | Kích thước component |
| `customOnChange` | `function` | - | Custom onChange handler |
| Prop | Type | Default | Mô tả |
| ------------- | ------------------- | ---------- | ------------------ |
| `label` | `string` | - | Nhãn field |
| `placeholder` | `string` | - | Placeholder text |
| `width` | `string` | `'100%'` | Chiều rộng |
| `direction` | `'row' \| 'column'` | `'column'` | Layout direction |
| `labelWidth` | `string` | - | Chiều rộng label |
| `disabled` | `boolean` | `false` | Trạng thái disable |
| `required` | `boolean` | `false` | Bắt buộc nhập |
Thư viện có hệ thống xử lý lỗi tự động:
```tsx
// Error Boundary tự động bắt lỗi render
<ErrorBoundary fallback={<div>Có lỗi xảy ra</div>}>
<RenderDataEntry data={fieldConfig} hookForm={hookForm} />
</ErrorBoundary>;
// Validation errors từ React Hook Form
const rules = {
required: "Trường này bắt buộc",
validate: (value) => {
if (!value) return "Giá trị không hợp lệ";
return true;
},
};
```
```bash
git clone https://github.com/HiNT89/react-data-entry.git
cd react-data-entry
npm install
npm run build
npm run pack:local
```
```
src/
├── components/
├── types/
├── utils/
├── *.tsx
├── styles.css
└── index.tsx
```
ISC License - xem file [LICENSE](LICENSE) để biết thêm chi tiết.
Contributions are welcome! Vui lòng đọc [CONTRIBUTING.md](CONTRIBUTING.md) để biết hướng dẫn chi tiết.
- 🐛 **Issues**: [GitHub Issues](https://github.com/HiNT89/react-data-entry/issues)
- 💬 **Discussions**: [GitHub Discussions](https://github.com/HiNT89/react-data-entry/discussions)
- 📧 **Email**: support@hint89.dev
---
Được phát triển bởi **HiNT** với ❤️