npm package: @suraj8558/react-custom-modal
Description: A fully customizable and accessible modal library for React and Next.js, written in TypeScript with comprehensive features and easy-to-use APIs.
Author: Suraj kumar
Latest version: 1.0.10

# @suraj8558/react-custom-modal

A fully customizable and accessible modal library for React and Next.js, written in TypeScript with comprehensive features and easy-to-use APIs.

## ✨ Features

- ✅ **TypeScript Support** - Full type safety with comprehensive type definitions
- ✅ **forwardRef + useImperativeHandle** - Imperative API for programmatic control
- ✅ **Escape key and outside click** - Multiple ways to close modals
- ✅ **Auto-close after timeout** - Automatic modal dismissal
- ✅ **Lazy loading** - Load modal content only when needed
- ✅ **Portal support** - Render modals outside component tree
- ✅ **Theme support** - Light and dark themes
- ✅ **Size variants** - Small, medium, large, and full-screen modals
- ✅ **Next.js integration** - Works seamlessly with Next.js
- ✅ **Global ModalProvider** - Context-based modal management
- ✅ **Confirm modal utility** - Easy confirmation dialogs
- ✅ **Customizable styling** - Override any style with CSS properties

## 📦 Installation

```bash
npm install @suraj8558/react-custom-modal
```

## 🔧 Basic Usage

### Simple Modal

```tsx
import { Modal } from '@suraj8558/react-custom-modal';
import { useState } from 'react';

function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      
      <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
        <h2>Hello World!</h2>
        <p>This is a simple modal.</p>
        <button onClick={() => setIsOpen(false)}>Close</button>
      </Modal>
    </div>
  );
}
```

### Modal with Ref (Imperative API)

```tsx
import { Modal, ModalHandles } from '@suraj8558/react-custom-modal';
import { useRef } from 'react';

function App() {
  const modalRef = useRef<ModalHandles>(null);

  const openModal = () => modalRef.current?.open();
  const closeModal = () => modalRef.current?.close();

  return (
    <div>
      <button onClick={openModal}>Open Modal</button>
      <button onClick={closeModal}>Close Modal</button>
      
      <Modal ref={modalRef}>
        <h2>Imperative Modal</h2>
        <p>This modal is controlled via ref.</p>
      </Modal>
    </div>
  );
}
```

## 🎨 Customization

### Themes and Sizes

```tsx
import { Modal } from '@suraj8558/react-custom-modal';

function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <Modal 
      isOpen={isOpen} 
      onClose={() => setIsOpen(false)}
      theme="dark"           // 'light' | 'dark'
      size="lg"              // 'sm' | 'md' | 'lg' | 'full'
      closeOnOutsideClick={true}
      closeOnEscape={true}
    >
      <h2>Customized Modal</h2>
      <p>This modal has dark theme and large size.</p>
    </Modal>
  );
}
```

### Custom Styling

```tsx
import { Modal } from '@suraj8558/react-custom-modal';

function App() {
  const [isOpen, setIsOpen] = useState(false);

  const overlayStyle = {
    backgroundColor: 'rgba(255, 0, 0, 0.3)',
    backdropFilter: 'blur(5px)'
  };

  const modalStyle = {
    borderRadius: '20px',
    boxShadow: '0 20px 40px rgba(0,0,0,0.3)',
    border: '2px solid #007bff'
  };

  return (
    <Modal 
      isOpen={isOpen} 
      onClose={() => setIsOpen(false)}
      overlayStyle={overlayStyle}
      modalStyle={modalStyle}
    >
      <h2>Custom Styled Modal</h2>
      <p>This modal has custom overlay and modal styles.</p>
    </Modal>
  );
}
```

### Auto-close Modal

```tsx
import { Modal } from '@suraj8558/react-custom-modal';

function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <Modal 
      isOpen={isOpen} 
      onClose={() => setIsOpen(false)}
      autoCloseAfter={5000}  // Auto-close after 5 seconds
    >
      <h2>Auto-close Modal</h2>
      <p>This modal will close automatically after 5 seconds.</p>
    </Modal>
  );
}
```

## 🌍 Global Modal Management

### Setup ModalProvider

```tsx
// _app.tsx (Next.js) or App.tsx (React)
import { ModalProvider } from '@suraj8558/react-custom-modal';

function MyApp({ Component, pageProps }) {
  return (
    <ModalProvider>
      <Component {...pageProps} />
    </ModalProvider>
  );
}

export default MyApp;
```

### Using Global Modals

```tsx
import { useModal } from '@suraj8558/react-custom-modal';

function MyComponent() {
  const { openModal, closeModal } = useModal();

  const showSuccessModal = () => {
    openModal(
      <div>
        <h2>Success!</h2>
        <p>Operation completed successfully.</p>
        <button onClick={closeModal}>OK</button>
      </div>
    );
  };

  const showFormModal = () => {
    openModal(
      <div>
        <h2>Contact Form</h2>
        <form>
          <input type="text" placeholder="Name" />
          <input type="email" placeholder="Email" />
          <button type="submit">Submit</button>
        </form>
        <button onClick={closeModal}>Cancel</button>
      </div>
    );
  };

  return (
    <div>
      <button onClick={showSuccessModal}>Show Success</button>
      <button onClick={showFormModal}>Show Form</button>
    </div>
  );
}
```

## ✅ Confirmation Dialogs

### Basic Confirmation

```tsx
import { useConfirm } from '@suraj8558/react-custom-modal';

function DeleteButton() {
  const confirm = useConfirm();

  const handleDelete = async () => {
    const confirmed = await confirm('Are you sure you want to delete this item?');
    
    if (confirmed) {
      // Proceed with deletion
      console.log('Item deleted');
    } else {
      // User cancelled
      console.log('Deletion cancelled');
    }
  };

  return <button onClick={handleDelete}>Delete Item</button>;
}
```

### Custom Confirmation Modal

```tsx
import { useModal } from '@suraj8558/react-custom-modal';

function CustomConfirmButton() {
  const { openModal } = useModal();

  const handleDelete = () => {
    openModal(
      <div style={{ textAlign: 'center' }}>
        <h3>⚠️ Warning</h3>
        <p>This action cannot be undone. Are you sure?</p>
        <div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
          <button 
            onClick={() => {
              // Handle deletion
              console.log('Deleted');
              // Close modal
              window.location.reload();
            }}
            style={{ backgroundColor: '#dc3545', color: 'white' }}
          >
            Yes, Delete
          </button>
          <button 
            onClick={() => window.location.reload()}
            style={{ backgroundColor: '#6c757d', color: 'white' }}
          >
            Cancel
          </button>
        </div>
      </div>
    );
  };

  return <button onClick={handleDelete}>Delete with Custom Modal</button>;
}
```

## 🚀 Next.js Integration

### App Router (Next.js 13+)

```tsx
// app/layout.tsx
'use client';
import { ModalProvider } from '@suraj8558/react-custom-modal';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <ModalProvider>
          {children}
        </ModalProvider>
      </body>
    </html>
  );
}
```

### Pages Router (Next.js 12 and below)

```tsx
// pages/_app.tsx
import { ModalProvider } from '@suraj8558/react-custom-modal';
import type { AppProps } from 'next/app';

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ModalProvider>
      <Component {...pageProps} />
    </ModalProvider>
  );
}

export default MyApp;
```

### Next.js Page Example

```tsx
// pages/index.tsx or app/page.tsx
'use client';
import { useState } from 'react';
import { Modal, useModal } from '@suraj8558/react-custom-modal';

export default function HomePage() {
  const [isOpen, setIsOpen] = useState(false);
  const { openModal } = useModal();

  return (
    <div>
      <h1>Next.js Modal Example</h1>
      
      {/* Local Modal */}
      <button onClick={() => setIsOpen(true)}>Open Local Modal</button>
      <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
        <h2>Local Modal</h2>
        <p>This modal is controlled locally.</p>
      </Modal>

      {/* Global Modal */}
      <button onClick={() => openModal(<h2>Global Modal</h2>)}>
        Open Global Modal
      </button>
    </div>
  );
}
```

## 📋 API Reference

### Modal Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | - | Modal content |
| `isOpen` | `boolean` | `false` | Controls modal visibility |
| `onClose` | `() => void` | - | Called when modal closes |
| `theme` | `'light' \| 'dark'` | `'light'` | Modal theme |
| `size` | `'sm' \| 'md' \| 'lg' \| 'full'` | `'md'` | Modal size |
| `closeOnOutsideClick` | `boolean` | `true` | Close on overlay click |
| `closeOnEscape` | `boolean` | `true` | Close on Escape key |
| `autoCloseAfter` | `number` | - | Auto-close after milliseconds |
| `lazy` | `boolean` | `false` | Load content only when open |
| `usePortal` | `boolean` | `true` | Render in portal |
| `containerId` | `string` | `'modal-root'` | Portal container ID |
| `overlayStyle` | `CSSProperties` | - | Overlay custom styles |
| `modalStyle` | `CSSProperties` | - | Modal custom styles |
| `closeIcon` | `ReactNode` | `×` | Custom close icon node/button |
| `closeIconStyle` | `CSSProperties` | - | Custom style for close icon button |

### Close Icon Example

```tsx
import { Modal } from '@suraj8558/react-custom-modal';
import { FaTimes } from 'react-icons/fa'; // or any icon library

function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <Modal
      isOpen={isOpen}
      onClose={() => setIsOpen(false)}
      closeIcon={<FaTimes />}
      closeIconStyle={{ color: 'red', fontSize: 28 }}
    >
      <h2>Modal with Custom Close Icon</h2>
      <p>You can provide your own icon and style for the close button.</p>
    </Modal>
  );
}
```

### ModalHandles (Ref API)

| Method | Description |
|--------|-------------|
| `open()` | Opens the modal |
| `close()` | Closes the modal |

### useModal Hook

| Method | Description |
|--------|-------------|
| `openModal(content, options?)` | Opens a global modal with content |
| `closeModal()` | Closes the current global modal |

### useConfirm Hook

| Method | Description |
|--------|-------------|
| `confirm(message)` | Shows confirmation dialog, returns Promise<boolean> |

## 🎯 Examples

### Form Modal

```tsx
import { Modal } from '@suraj8558/react-custom-modal';

function FormModal() {
  const [isOpen, setIsOpen] = useState(false);
  const [formData, setFormData] = useState({ name: '', email: '' });

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted:', formData);
    setIsOpen(false);
  };

  return (
    <Modal isOpen={isOpen} onClose={() => setIsOpen(false)} size="lg">
      <form onSubmit={handleSubmit}>
        <h2>Contact Form</h2>
        <div>
          <label>Name:</label>
          <input
            type="text"
            value={formData.name}
            onChange={(e) => setFormData({...formData, name: e.target.value})}
          />
        </div>
        <div>
          <label>Email:</label>
          <input
            type="email"
            value={formData.email}
            onChange={(e) => setFormData({...formData, email: e.target.value})}
          />
        </div>
        <button type="submit">Submit</button>
        <button type="button" onClick={() => setIsOpen(false)}>Cancel</button>
      </form>
    </Modal>
  );
}
```

### Image Gallery Modal

```tsx
import { Modal } from '@suraj8558/react-custom-modal';

function ImageGallery() {
  const [selectedImage, setSelectedImage] = useState(null);

  const images = [
    'https://picsum.photos/300/200?random=1',
    'https://picsum.photos/300/200?random=2',
    'https://picsum.photos/300/200?random=3',
  ];

  return (
    <div>
      <div style={{ display: 'flex', gap: '10px' }}>
        {images.map((src, index) => (
          <img
            key={index}
            src={src}
            alt={`Image ${index + 1}`}
            style={{ width: '100px', height: '70px', cursor: 'pointer' }}
            onClick={() => setSelectedImage(src)}
          />
        ))}
      </div>

      <Modal 
        isOpen={!!selectedImage} 
        onClose={() => setSelectedImage(null)}
        size="lg"
        theme="dark"
      >
        {selectedImage && (
          <img 
            src={selectedImage} 
            alt="Full size" 
            style={{ maxWidth: '100%', height: 'auto' }}
          />
        )}
      </Modal>
    </div>
  );
}
```

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## 📄 License

MIT License - see the [LICENSE](LICENSE) file for details.

## 🔗 Links

- [npm package](https://www.npmjs.com/package/@suraj8558/react-custom-modal)
- [GitHub repository](https://github.com/Suraj8558/react-custom-modal)
- [Issues](https://github.com/Suraj8558/react-custom-modal/issues)