DEV Community

Akram Khan
Akram Khan

Posted on

1

I create a custom Modal component in react + typescript.

I create a custom Modal component in react + typescript.

Modal.tsx

// src/components/ui/Modal.tsx
import React, { useState, useEffect, useRef } from 'react';

interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  children: React.ReactNode;
  dismissable?: boolean;
  closeOnEsc?: boolean;
  closeButtonLabel?: string;
  size?: 'small' | 'medium' | 'large';
  position?: 'center' | 'top' | 'bottom';
}

const Modal: React.FC<ModalProps> = ({
  isOpen,
  onClose,
  children,
  dismissable = true,
  closeOnEsc = true,
  closeButtonLabel = '×',
  size = 'medium',
  position = 'center',
}) => {
  const [isModalOpen, setIsModalOpen] = useState(isOpen);
  const modalRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    setIsModalOpen(isOpen);
  }, [isOpen]);

  useEffect(() => {
    const handleOutsideClick = (event: MouseEvent) => {
      if (dismissable && modalRef.current && !modalRef.current.contains(event.target as Node)) {
        onClose();
      }
    };

    const handleEscKey = (event: KeyboardEvent) => {
      if (closeOnEsc && event.key === 'Escape') {
        onClose();
      }
    };

    if (isModalOpen) {
      document.addEventListener('mousedown', handleOutsideClick);
      document.addEventListener('keydown', handleEscKey);
    }

    return () => {
      document.removeEventListener('mousedown', handleOutsideClick);
      document.removeEventListener('keydown', handleEscKey);
    };
  }, [isModalOpen, onClose, dismissable, closeOnEsc]);

  if (!isModalOpen) {
    return null;
  }

  const getSizeStyle = () => {
    switch (size) {
      case 'small':
        return { width: '300px', maxWidth: '100%' };
      case 'large':
        return { width: '800px', maxWidth: '100%' };
      default:
        return { width: '500px', maxWidth: '100%' };
    }
  };

  const getPositionStyle = () => {
    switch (position) {
      case 'top':
        return { alignItems: 'flex-start' };
      case 'bottom':
        return { alignItems: 'flex-end' };
      default:
        return { alignItems: 'center' };
    }
  };

  return (
    <div style={{
      position: 'fixed',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
      backgroundColor: 'rgba(0, 0, 0, 0.5)',
      display: 'flex',
      justifyContent: 'center',
      zIndex: 1000,
      ...getPositionStyle(),
    }}>
      <div
        ref={modalRef}
        style={{
          backgroundColor: 'white',
          padding: '20px',
          borderRadius: '8px',
          maxHeight: '80vh',
          overflow: 'auto',
          position: 'relative',
          ...getSizeStyle(),
        }}
      >
          <button
            onClick={onClose}
            style={{
              position: 'absolute',
              top: '10px',
              right: '10px',
              background: 'none',
              border: 'none',
              fontSize: '18px',
              cursor: 'pointer',
            }}
          >
            {closeButtonLabel}
          </button>
        {children}
      </div>
    </div>
  );
};

export default Modal;
Enter fullscreen mode Exit fullscreen mode

App.tsx

import Modal from "@/components/ui/Modal";
import { useState } from "react";

export default function Home() {
  const [visible, setVisible] = useState(false);

  return (
    <main>
      <button onClick={() => setVisible(true)}>isOpen</button>
      <Modal position="bottom" isOpen={visible} onClose={() => setVisible(false)}>
        <h2>Modal Content</h2>
        <p>This is a simple modal component.</p>
      </Modal>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more