npm package: @mehdashti/modals
Description: Enterprise-grade modal and drawer management with global utilities for Smart Platform
Author: mehdashti
Latest version: 0.4.0

# @mehdashti/modals

Modal and drawer management with stacking support for Smart Platform.

## Features

- **Multiple Modal Types**: Modal, Drawer, Confirmation Dialog
- **Modal Stacking**: Support for multiple modals simultaneously
- **Focus Management**: Automatic focus trap and restoration
- **Accessible**: Built with Radix UI for WCAG compliance
- **Customizable**: 5 sizes and flexible styling
- **Keyboard Support**: ESC to close, Tab for focus management
- **Programmatic Control**: Imperative API via context
- **Animations**: Smooth enter/exit animations
- **Type Safe**: Full TypeScript support

## Installation

```bash
pnpm add @mehdashti/modals
```

## Usage

### Basic Modal

```tsx
import { Modal } from '@mehdashti/modals'
import { useState } from 'react'

function MyComponent() {
  const [open, setOpen] = useState(false)

  return (
    <>
      <button onClick={() => setOpen(true)}>Open Modal</button>

      <Modal
        open={open}
        onClose={() => setOpen(false)}
        title="Modal Title"
        description="This is a modal description"
      >
        <p>Modal content goes here</p>
      </Modal>
    </>
  )
}
```

### Modal Sizes

```tsx
<Modal
  open={open}
  onClose={onClose}
  title="Small Modal"
  size="sm" // sm | md | lg | xl | full
>
  <p>Content</p>
</Modal>
```

### Modal Context (Programmatic Control)

```tsx
import { ModalProvider, useModal } from '@mehdashti/modals'

// Wrap your app
function App() {
  return (
    <ModalProvider>
      <YourApp />
    </ModalProvider>
  )
}

// Use in any component
function MyComponent() {
  const { openModal, closeModal } = useModal()

  const handleOpen = () => {
    openModal('user-profile', {
      title: 'User Profile',
      children: <UserProfile />,
    })
  }

  const handleClose = () => {
    closeModal('user-profile')
  }

  return <button onClick={handleOpen}>Open Profile</button>
}
```

### Confirmation Dialog

```tsx
import { ConfirmDialog } from '@mehdashti/modals'

function DeleteButton() {
  const [open, setOpen] = useState(false)

  const handleConfirm = async () => {
    await deleteUser()
    setOpen(false)
  }

  return (
    <>
      <button onClick={() => setOpen(true)}>Delete User</button>

      <ConfirmDialog
        open={open}
        onClose={() => setOpen(false)}
        onConfirm={handleConfirm}
        title="Delete User"
        description="Are you sure you want to delete this user? This action cannot be undone."
        confirmText="Delete"
        cancelText="Cancel"
        variant="destructive"
      />
    </>
  )
}
```

### Drawer (Slide from Side)

```tsx
import { Drawer } from '@mehdashti/modals'

function MyComponent() {
  const [open, setOpen] = useState(false)

  return (
    <>
      <button onClick={() => setOpen(true)}>Open Drawer</button>

      <Drawer
        open={open}
        onClose={() => setOpen(false)}
        title="Settings"
        side="right" // left | right
      >
        <SettingsForm />
      </Drawer>
    </>
  )
}
```

### Form Modal

```tsx
import { FormModal } from '@mehdashti/modals'
import { useSmartForm } from '@mehdashti/forms'

function CreateUserModal({ open, onClose }) {
  const form = useSmartForm({
    schema: userSchema,
    onSubmit: async (data) => {
      await createUser(data)
      onClose()
    },
  })

  return (
    <FormModal
      open={open}
      onClose={onClose}
      title="Create User"
      form={form}
      submitText="Create"
    >
      <FormField name="name" label="Name" control={form.control} />
      <FormField name="email" label="Email" control={form.control} />
    </FormModal>
  )
}
```

### Modal Stacking

Multiple modals can be opened simultaneously with automatic z-index management:

```tsx
function App() {
  return (
    <ModalProvider>
      <YourApp />
    </ModalProvider>
  )
}

function Component() {
  const { openModal } = useModal()

  const handleOpenFirst = () => {
    openModal('first', {
      title: 'First Modal',
      children: (
        <button onClick={() => openModal('second', {
          title: 'Second Modal',
          children: <p>This is stacked on top!</p>
        })}>
          Open Second Modal
        </button>
      ),
    })
  }

  return <button onClick={handleOpenFirst}>Open Modal</button>
}
```

**Z-Index Management:**
- Each modal/drawer automatically gets the correct z-index
- First modal: z-50 (overlay), z-51 (content)
- Second modal: z-60 (overlay), z-61 (content)
- Third modal: z-70 (overlay), z-71 (content)
- Supports unlimited stacking without conflicts

## API

### Modal Props

```typescript
interface ModalProps {
  open: boolean
  onClose?: () => void
  title?: React.ReactNode
  description?: React.ReactNode
  children: React.ReactNode
  size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'
  showClose?: boolean
  dismissable?: boolean
  className?: string
}
```

### Drawer Props

```typescript
interface DrawerProps extends ModalProps {
  side?: 'left' | 'right'
}
```

### ConfirmDialog Props

```typescript
interface ConfirmDialogProps {
  open: boolean
  onClose: () => void
  onConfirm: () => void | Promise<void>
  title: string
  description: string
  confirmText?: string
  cancelText?: string
  variant?: 'default' | 'destructive'
  isLoading?: boolean
}
```

### ModalContext API

```typescript
interface ModalContextValue {
  openModal: (id: string, props: Omit<ModalProps, 'open' | 'onClose'>) => void
  closeModal: (id: string) => void
  closeAllModals: () => void
  isOpen: (id: string) => boolean
}
```

## Accessibility

All components follow WCAG 2.1 guidelines:

- **Focus trap**: Focus is trapped within the modal
- **Focus restoration**: Focus returns to trigger on close
- **Keyboard navigation**: ESC to close, Tab to navigate
- **ARIA attributes**: Proper roles and labels
- **Screen reader support**: Announcements for state changes

## Examples

See [Storybook stories](./src/components/modal.stories.tsx) for more examples.

## License

MIT