# Modal Component
Modal component tương tự như Modal của Ant Design, được tạo cho React Native/Expo.
## Cài đặt
Component này sử dụng `@expo/vector-icons` cho các icon. Đảm bảo bạn đã cài đặt:
```bash
npx expo install @expo/vector-icons
```
## Import
```tsx
import Modal from "@/components/ui/modal";
```
## Các tính năng chính
### 1. Basic Modal
```tsx
import React, { useState } from "react";
import { View, Text, Button } from "react-native";
import Modal from "@/components/ui/modal";
export default function BasicExample() {
const [open, setOpen] = useState(false);
return (
);
}
```
### 2. Async Close (với confirmLoading)
```tsx
import React, { useState } from "react";
import { View, Text, Button } from "react-native";
import Modal from "@/components/ui/modal";
export default function AsyncExample() {
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const handleOk = async () => {
setLoading(true);
// Giả lập API call
await new Promise((resolve) => setTimeout(resolve, 2000));
setLoading(false);
setOpen(false);
};
return (
);
}
```
### 3. Customized Footer
```tsx
import React, { useState } from "react";
import { View, Text, Button, TouchableOpacity, StyleSheet } from "react-native";
import Modal from "@/components/ui/modal";
export default function CustomFooterExample() {
const [open, setOpen] = useState(false);
return (
);
}
const styles = StyleSheet.create({
customFooter: {
flexDirection: "row",
gap: 8,
},
customButton: {
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 6,
borderWidth: 1,
borderColor: "#d9d9d9",
},
submitButton: {
backgroundColor: "#1890ff",
borderColor: "#1890ff",
},
buttonText: {
fontSize: 14,
color: "#000",
},
submitText: {
color: "#fff",
},
});
```
### 4. No Footer
```tsx
setOpen(false)}
>
Modal content without footer buttons
```
### 5. Centered Modal
```tsx
setOpen(false)}
onCancel={() => setOpen(false)}
>
This modal is centered on the screen
```
### 6. Custom Width
```tsx
setOpen(false)}
onCancel={() => setOpen(false)}
>
This modal has custom width
```
### 7. Confirm Modal với useModal Hook
**Đây là cách khuyến nghị sử dụng trong React Native để có context đầy đủ:**
```tsx
import React from "react";
import { View, Button } from "react-native";
import Modal from "@/components/ui/modal";
export default function HookExample() {
const [modal, contextHolder] = Modal.useModal();
const showConfirm = () => {
modal.confirm({
title: "Do you want to delete these items?",
content:
"When clicked the OK button, this dialog will be closed after 1 second",
onOk: async () => {
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log("OK");
},
onCancel: () => {
console.log("Cancel");
},
});
};
const showInfo = () => {
modal.info({
title: "This is a notification message",
content: "Some additional information...",
});
};
const showSuccess = () => {
modal.success({
title: "Success",
content: "Operation completed successfully!",
});
};
const showError = () => {
modal.error({
title: "Error",
content: "Something went wrong!",
});
};
const showWarning = () => {
modal.warning({
title: "Warning",
content: "This is a warning message!",
});
};
return (
{/* contextHolder phải được đặt trong component */}
{contextHolder}
);
}
```
### 8. Update Modal Instance
```tsx
import React from "react";
import { View, Button } from "react-native";
import Modal from "@/components/ui/modal";
export default function UpdateExample() {
const [modal, contextHolder] = Modal.useModal();
const showModal = () => {
const instance = modal.success({
title: "Loading...",
content: "Please wait...",
});
// Update after 2 seconds
setTimeout(() => {
instance.update({
title: "Success!",
content: "Operation completed successfully!",
});
}, 2000);
// Close after 4 seconds
setTimeout(() => {
instance.destroy();
}, 4000);
};
return (
{contextHolder}
);
}
```
## API
### Modal Props
| Prop | Type | Default | Description |
| --------------- | ----------------------------- | --------- | ------------------------------------------------------------ |
| open | boolean | false | Whether the modal dialog is visible or not |
| title | ReactNode | - | The modal dialog's title |
| closable | boolean | true | Whether a close (x) button is visible on top right or not |
| closeIcon | ReactNode | - | Custom close icon |
| maskClosable | boolean | true | Whether to close the modal dialog when the mask is clicked |
| centered | boolean | false | Centered Modal |
| width | number \| string | 520 | Width of the modal dialog |
| confirmLoading | boolean | false | Whether to apply loading visual effect for OK button |
| okText | string | 'OK' | Text of the OK button |
| cancelText | string | 'Cancel' | Text of the Cancel button |
| okType | 'primary' \| 'default' | 'primary' | Button type of the OK button |
| footer | ReactNode \| null | - | Footer content, set as footer={null} to hide default buttons |
| mask | boolean | true | Whether show mask or not |
| zIndex | number | 1000 | The z-index of the Modal |
| onOk | (e?) => void \| Promise | - | Callback when clicking OK button |
| onCancel | (e?) => void | - | Callback when clicking cancel button or close icon |
| afterOpenChange | (open: boolean) => void | - | Callback when animation ends |
| afterClose | () => void | - | Callback when modal is closed completely |
| destroyOnClose | boolean | false | Whether to unmount child components on close |
| keyboard | boolean | true | Whether support press back button to close (Android) |
### Modal.useModal()
Khi bạn cần sử dụng Context, bạn có thể dùng `Modal.useModal()` để tạo `contextHolder` và chèn vào children. Modal được tạo bởi hooks sẽ có tất cả context nơi `contextHolder` được đặt.
**Returns:** `[modalMethods, contextHolder]`
- `modalMethods`: Object chứa các methods
- `info(config)`: Show info modal
- `success(config)`: Show success modal
- `error(config)`: Show error modal
- `warning(config)`: Show warning modal
- `confirm(config)`: Show confirm modal
- `contextHolder`: React element cần được render trong component tree
### Modal Methods Config
| Prop | Type | Default | Description |
| ---------- | -------------------------------------------------------- | --------- | ----------------------------- |
| type | 'info' \| 'success' \| 'error' \| 'warning' \| 'confirm' | 'confirm' | Type of the modal |
| title | ReactNode | - | Title |
| content | ReactNode | - | Content |
| icon | ReactNode | - | Custom icon |
| okText | string | 'OK' | Text of the OK button |
| cancelText | string | 'Cancel' | Text of the Cancel button |
| onOk | (e?) => void \| Promise | - | Callback when clicking OK |
| onCancel | (e?) => void | - | Callback when clicking Cancel |
### Modal Instance
Modal instance được trả về bởi `Modal.useModal()`:
```tsx
interface ModalInstance {
destroy: () => void;
update: (config: ConfirmModalProps) => void;
}
```
## Lưu ý
1. **React Native Limitations**: Các static methods như `Modal.info()`, `Modal.confirm()` gọi trực tiếp (không qua hook) không được hỗ trợ đầy đủ trong React Native do không thể render imperatively. Hãy sử dụng `Modal.useModal()` hook thay thế.
2. **Context Support**: Khi cần sử dụng Context (như Redux, Theme Context), bắt buộc phải dùng `Modal.useModal()` hook và đặt `contextHolder` trong component tree.
3. **Animation**: Modal sử dụng React Native's built-in Modal với `animationType="fade"`.
4. **Icons**: Component sử dụng `@expo/vector-icons` (Ionicons). Đảm bảo đã cài đặt package này.
5. **Keyboard**: Prop `keyboard` trong React Native chỉ hoạt động với nút back của Android (không có ESC key như web).
## So sánh với Ant Design Modal
| Feature | Ant Design (Web) | This Component (RN) |
| --------------------------- | ---------------- | --------------------------- |
| Basic Modal | ✅ | ✅ |
| Centered | ✅ | ✅ |
| Custom Footer | ✅ | ✅ |
| Confirm Dialog | ✅ | ✅ (via useModal) |
| Info/Success/Error/Warning | ✅ | ✅ (via useModal) |
| Async close | ✅ | ✅ |
| Custom width | ✅ | ✅ |
| Mask closable | ✅ | ✅ |
| Keyboard close | ✅ (ESC) | ✅ (Back button on Android) |
| Static methods without hook | ✅ | ⚠️ (Limited support) |
| useModal hook | ✅ | ✅ (Recommended) |
| Draggable | ✅ | ❌ (Not applicable) |
| destroyAll() | ✅ | ❌ |
## License
MIT