Make life easier handling dialogs, sheets and drawers for shadcn.
pnpm add pushmodal
- First initialize your modals
// file: src/modals/index.tsx (alias '@/modals')
import Modal1 from './modal-1'
import Modal2 from './model-2'
import { createPushModal, SheetWrapper } from 'pushmodal'
export const {
pushModal,
popModal,
ModalProvider
} = createPushModal({
modals: {
Modal1: {
Component: Modal1
},
Modal2: {
Component: Modal2
// This will appear as a sheet instead of dialog
Wrapper: SheetWrapper,
// Options for this modal
// onEscapeKeyDown(event) {
// event.preventDefault();
// },
// onInteractOutside(event) {
// event.preventDefault();
// },
// onPointerDownOutside(event) {
// event.preventDefault();
// },
},
},
// Change default wrapper
// defaultWrapper: SheetWrapper
})
How we usually structure things
src
├── ...
├── modals
│ ├── modal-1.tsx
│ ├── modal-2.tsx
│ ├── modal-3.tsx
│ ├── modal-4.tsx
│ ├── modal-5.tsx
│ ├── modal-6.tsx
│ └── index.tsx
├── ...
└── ...
- Add the
<ModalProvider />
to your root file.
import { ModalProvider } from '@/modals'
export default function App({ children }: { children: React.ReactNode }) {
return (
<>
{/* Notice! You should not wrap your children */}
<ModalProvider />
{children}
</>
)
}
- Use
pushModal
pushModal
can have 3 arguments
name
- name of your modalprops
- props for your specific modal, types are infered from your component!options
- options for the modal
import { SheetWrapper } from 'pushmodal'
import { pushModal } from '@/modals'
export default function RandomComponent() {
return (
<div>
<button onClick={() => {
pushModal('Modal1')
}}>
Open modal
</button>
<button onClick={() => {
pushModal('Modal1', {}, { Wrapper: SheetWrapper })
}}>
Open modal in sheet instead!
</button>
<button onClick={() => {
pushModal('Modal1', {}, {
onInteractOutside: (event) => event.preventDefault()
})
}}>
Open modal with custom config
</button>
</div>
)
}
- Closing modals
You can close modals on three different ways
popModal()
- will pop the last added modalpopModal('Modal1')
- will pop the last added modal with nameModal1
popAllModals()
- will close all your modals
export interface PushModalOptions {
Wrapper?: React.ComponentType<{
open: boolean
onOpenChange: (open: boolean) => void
}>
onInteractOutside?: (event: Event) => void;
onPointerDownOutside?: (event: Event) => void;
onEscapeKeyDown?: (event: KeyboardEvent) => void;
}
It's common to want to update options on the fly. Then you can use usePushModal
hook.
export default Modal() {
const [options, setOptions] = usePushModal()
const someCondition = false
useEffect(() => {
if(someCondition) {
setOptions(prev => ({
onInteractOutside: (event) => event.preventDefault()
}))
}
}, [])
return (
<>
...
</>
)
}
You can also pass options directly to the hook if you want your options to take effect immediately
export default Modal() {
usePushModal({
onInteractOutside: (event) => event.preventDefault()
})
return (
<>...</>
)
}
You might need to add the following to you tailwind.config. Of provide your own wrapper as option for createPushModal
.
// tailwind.config.js
...
content: [
'./node_modules/pushmodal/**/*.js', // <---
],
...