Files
SeaGateway-App/MODAL_USAGE.md
2025-11-19 14:23:17 +07:00

13 KiB

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:

npx expo install @expo/vector-icons

Import

import Modal from "@/components/ui/modal";

Các tính năng chính

1. Basic Modal

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 (
    <View>
      <Button title="Open Modal" onPress={() => setOpen(true)} />

      <Modal
        open={open}
        title="Basic Modal"
        onOk={() => {
          console.log("OK clicked");
          setOpen(false);
        }}
        onCancel={() => setOpen(false)}
      >
        <Text>Some contents...</Text>
        <Text>Some contents...</Text>
        <Text>Some contents...</Text>
      </Modal>
    </View>
  );
}

2. Async Close (với confirmLoading)

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 (
    <View>
      <Button title="Open Modal" onPress={() => setOpen(true)} />

      <Modal
        open={open}
        title="Async Modal"
        confirmLoading={loading}
        onOk={handleOk}
        onCancel={() => setOpen(false)}
      >
        <Text>Modal will be closed after 2 seconds when you click OK.</Text>
      </Modal>
    </View>
  );
}
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 (
    <View>
      <Button title="Open Modal" onPress={() => setOpen(true)} />

      <Modal
        open={open}
        title="Custom Footer Modal"
        onCancel={() => setOpen(false)}
        footer={
          <View style={styles.customFooter}>
            <TouchableOpacity
              style={styles.customButton}
              onPress={() => setOpen(false)}
            >
              <Text style={styles.buttonText}>Return</Text>
            </TouchableOpacity>
            <TouchableOpacity
              style={[styles.customButton, styles.submitButton]}
              onPress={() => {
                console.log("Submit");
                setOpen(false);
              }}
            >
              <Text style={[styles.buttonText, styles.submitText]}>Submit</Text>
            </TouchableOpacity>
          </View>
        }
      >
        <Text>Custom footer buttons</Text>
      </Modal>
    </View>
  );
}

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",
  },
});
<Modal
  open={open}
  title="Modal Without Footer"
  footer={null}
  onCancel={() => setOpen(false)}
>
  <Text>Modal content without footer buttons</Text>
</Modal>

5. Centered Modal

<Modal
  open={open}
  title="Centered Modal"
  centered
  onOk={() => setOpen(false)}
  onCancel={() => setOpen(false)}
>
  <Text>This modal is centered on the screen</Text>
</Modal>

6. Custom Width

<Modal
  open={open}
  title="Custom Width Modal"
  width={700}
  onOk={() => setOpen(false)}
  onCancel={() => setOpen(false)}
>
  <Text>This modal has custom width</Text>
</Modal>

7. Confirm Modal với useModal Hook

Đây là cách khuyến nghị sử dụng trong React Native để có context đầy đủ:

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 (
    <View style={{ padding: 20, gap: 10 }}>
      {/* contextHolder phải được đặt trong component */}
      {contextHolder}

      <Button title="Confirm" onPress={showConfirm} />
      <Button title="Info" onPress={showInfo} />
      <Button title="Success" onPress={showSuccess} />
      <Button title="Error" onPress={showError} />
      <Button title="Warning" onPress={showWarning} />
    </View>
  );
}

8. Update Modal Instance

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 (
    <View>
      {contextHolder}
      <Button title="Show Updating Modal" onPress={showModal} />
    </View>
  );
}

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():

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