Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/redpangilinan/credenza

Ready-made responsive modal component for shadcn/ui.
https://github.com/redpangilinan/credenza

component drawer modal modal-dialog next-14 nextjs react shadcn-ui

Last synced: 5 days ago
JSON representation

Ready-made responsive modal component for shadcn/ui.

Awesome Lists containing this project

README

        

# Credenza

A responsive modal component for shadcn/ui.

![Demo](https://github.com/redpangilinan/credenza/assets/82772769/d22580b3-9dbc-4a56-95e9-15b4bd278ff0)

## Installation

1. Copy the `dialog` and `drawer` component from shadcn/ui.

```bash
npx shadcn@latest add dialog drawer
```

Alternatively, if you are not using shadcn/ui cli, you can manually copy the components from [shadcn/ui](https://ui.shadcn.com/docs) or directly copy from [dialog.tsx](src/components/ui/dialog.tsx) and [drawer.tsx](src/components/ui/drawer.tsx).

If you copied the drawer component manually, make sure to install vaul.

```
npm install vaul
```

2. Copy the `useMediaQuery` hook: [use-media-query.tsx](src/hooks/use-media-query.tsx)

```tsx
import * as React from "react"

export function useMediaQuery(query: string) {
const [value, setValue] = React.useState(false)

React.useEffect(() => {
function onChange(event: MediaQueryListEvent) {
setValue(event.matches)
}

const result = matchMedia(query)
result.addEventListener("change", onChange)
setValue(result.matches)

return () => result.removeEventListener("change", onChange)
}, [query])

return value
}
```

3. Copy the `credenza` component: [credenza.tsx](src/components/ui/credenza.tsx)

Click to show code

```tsx
"use client"

import * as React from "react"

import { cn } from "@/lib/utils"
import { useMediaQuery } from "@/hooks/use-media-query"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"

interface BaseProps {
children: React.ReactNode
}

interface RootCredenzaProps extends BaseProps {
open?: boolean
onOpenChange?: (open: boolean) => void
}

interface CredenzaProps extends BaseProps {
className?: string
asChild?: true
}

const desktop = "(min-width: 768px)"

const Credenza = ({ children, ...props }: RootCredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const Credenza = isDesktop ? Dialog : Drawer

return {children}
}

const CredenzaTrigger = ({ className, children, ...props }: CredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const CredenzaTrigger = isDesktop ? DialogTrigger : DrawerTrigger

return (

{children}

)
}

const CredenzaClose = ({ className, children, ...props }: CredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const CredenzaClose = isDesktop ? DialogClose : DrawerClose

return (

{children}

)
}

const CredenzaContent = ({ className, children, ...props }: CredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const CredenzaContent = isDesktop ? DialogContent : DrawerContent

return (

{children}

)
}

const CredenzaDescription = ({
className,
children,
...props
}: CredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const CredenzaDescription = isDesktop ? DialogDescription : DrawerDescription

return (

{children}

)
}

const CredenzaHeader = ({ className, children, ...props }: CredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const CredenzaHeader = isDesktop ? DialogHeader : DrawerHeader

return (

{children}

)
}

const CredenzaTitle = ({ className, children, ...props }: CredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const CredenzaTitle = isDesktop ? DialogTitle : DrawerTitle

return (

{children}

)
}

const CredenzaBody = ({ className, children, ...props }: CredenzaProps) => {
return (


{children}

)
}

const CredenzaFooter = ({ className, children, ...props }: CredenzaProps) => {
const isDesktop = useMediaQuery(desktop)
const CredenzaFooter = isDesktop ? DialogFooter : DrawerFooter

return (

{children}

)
}

export {
Credenza,
CredenzaTrigger,
CredenzaClose,
CredenzaContent,
CredenzaDescription,
CredenzaHeader,
CredenzaTitle,
CredenzaBody,
CredenzaFooter,
}
```

4. Update the import paths based on your project structure.

5. If you want to enable background scaling, wrap your app with the `vaul-drawer-wrapper`.

```html

{children}

```

See my implementation at [layout.tsx](src/app/layout.tsx). Make sure to update the background color to match your project's theme.

## Usage

```tsx
import {
Credenza,
CredenzaBody,
CredenzaClose,
CredenzaContent,
CredenzaDescription,
CredenzaFooter,
CredenzaHeader,
CredenzaTitle,
CredenzaTrigger,
} from "@/components/ui/credenza"
```

Basic usage with `CredenzaTrigger`

```tsx


Open modal



Credenza

A responsive modal component for shadcn/ui.



This component is built using shadcn/ui's dialog and drawer
component, which is built on top of Vaul.



Close


```

Using state to open modal

```tsx
function StateModal() {
const [open, setOpen] = React.useState(false)

const handleOpen = () => {
setOpen(true)
}

return (
<>
Open with State




Credenza

A responsive modal component for shadcn/ui.


This modal got triggered using state


Close




>
)
}
```

## Credits

- [shadcn/ui](https://github.com/shadcn-ui/ui) by [shadcn](https://github.com/shadcn)
- [Vaul](https://github.com/emilkowalski/vaul) by [emilkowalski](https://github.com/emilkowalski)