@mehdashti/modals
v0.4.0
Published
Enterprise-grade modal and drawer management with global utilities for Smart Platform
Maintainers
Readme
@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
pnpm add @mehdashti/modalsUsage
Basic Modal
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
<Modal
open={open}
onClose={onClose}
title="Small Modal"
size="sm" // sm | md | lg | xl | full
>
<p>Content</p>
</Modal>Modal Context (Programmatic Control)
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
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)
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
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:
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
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
interface DrawerProps extends ModalProps {
side?: 'left' | 'right'
}ConfirmDialog Props
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
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 for more examples.
License
MIT
