Shuip
blockresponsive-dialogsource: ok · hit
Responsive Dialog
npx shadcn add "https://shuip.plvo.dev/r/responsive-dialog.json"free
Interaction
modal
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightreact
Registry dependencies
drawerside-dialog
Files1 · source cached 10/9/2026, 7:47:20 AM
./items/blocks/responsive-dialog/component.tsx
'use client';
import * as React from 'react';
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from '@/components/ui/drawer';
import {
SideDialog,
SideDialogBody,
SideDialogClose,
SideDialogContent,
SideDialogDescription,
SideDialogFooter,
SideDialogHeader,
SideDialogTitle,
SideDialogTrigger,
} from '@/components/ui/shuip/side-dialog';
import { cn } from '@/lib/utils';
type ResponsiveDialogPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'left' | 'right';
type ResponsiveDialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
type ResponsiveDialogBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | number;
export interface ResponsiveDialogProps {
open?: boolean;
onOpenChange?: (open: boolean) => void;
position?: ResponsiveDialogPosition;
size?: ResponsiveDialogSize;
breakpoint?: ResponsiveDialogBreakpoint;
children?: React.ReactNode;
}
export interface ResponsiveDialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
asChild?: boolean;
children: React.ReactNode;
}
export interface ResponsiveDialogContentProps extends React.HTMLAttributes<HTMLDivElement> {
showCloseButton?: boolean;
children: React.ReactNode;
}
export interface ResponsiveDialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
export interface ResponsiveDialogTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
children: React.ReactNode;
}
export interface ResponsiveDialogDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {
children: React.ReactNode;
}
export interface ResponsiveDialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
export interface ResponsiveDialogCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
asChild?: boolean;
children?: React.ReactNode;
}
export interface ResponsiveDialogBodyProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
// Helper function to convert breakpoint to pixels
const getBreakpointValue = (breakpoint: ResponsiveDialogBreakpoint): number => {
if (typeof breakpoint === 'number') {
return breakpoint;
}
const breakpoints = {
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
};
return breakpoints[breakpoint];
};
// Custom hook for responsive breakpoint detection
const useCustomBreakpoint = (breakpoint: ResponsiveDialogBreakpoint) => {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
React.useEffect(() => {
const breakpointValue = getBreakpointValue(breakpoint);
const mql = window.matchMedia(`(max-width: ${breakpointValue - 1}px)`);
const onChange = () => {
setIsMobile(window.innerWidth < breakpointValue);
};
mql.addEventListener('change', onChange);
setIsMobile(window.innerWidth < breakpointValue);
return () => mql.removeEventListener('change', onChange);
}, [breakpoint]);
return !!isMobile;
};
// Context for managing responsive state
const ResponsiveDialogContext = React.createContext<{
isMobile: boolean;
}>({
isMobile: false,
});
export function useResponsiveDialog() {
return React.useContext(ResponsiveDialogContext);
}
export function ResponsiveDialog({
open,
onOpenChange,
position = 'bottom-right',
size = 'sm',
breakpoint = 'md',
children,
}: ResponsiveDialogProps) {
const isMobile = useCustomBreakpoint(breakpoint);
return (
<ResponsiveDialogContext.Provider value={{ isMobile }}>
{isMobile ? (
<Drawer open={open} onOpenChange={onOpenChange} repositionInputs={false}>
{children}
</Drawer>
) : (
<SideDialog open={open} onOpenChange={onOpenChange} position={position} size={size}>
{children}
</SideDialog>
)}
</ResponsiveDialogContext.Provider>
);
}
export function ResponsiveDialogTrigger({ asChild, children, ...props }: ResponsiveDialogTriggerProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<DrawerTrigger asChild={asChild} {...props}>
{children}
</DrawerTrigger>
);
}
return (
<SideDialogTrigger asChild={asChild} {...props}>
{children}
</SideDialogTrigger>
);
}
export function ResponsiveDialogContent({
showCloseButton = true,
className,
children,
...props
}: ResponsiveDialogContentProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<DrawerContent {...props}>
<div className={cn('flex flex-col max-h-[75dvh]', className)}>{children}</div>
</DrawerContent>
);
}
return (
<SideDialogContent showCloseButton={showCloseButton} className={className} {...props}>
{children}
</SideDialogContent>
);
}
export function ResponsiveDialogHeader({ className, children, ...props }: ResponsiveDialogHeaderProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<DrawerHeader className={cn('shrink-0', className)} {...props}>
{children}
</DrawerHeader>
);
}
return (
<SideDialogHeader className={className} {...props}>
{children}
</SideDialogHeader>
);
}
export function ResponsiveDialogTitle({ className, children, ...props }: ResponsiveDialogTitleProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<DrawerTitle className={className} {...props}>
{children}
</DrawerTitle>
);
}
return (
<SideDialogTitle className={className} {...props}>
{children}
</SideDialogTitle>
);
}
export function ResponsiveDialogDescription({ className, children, ...props }: ResponsiveDialogDescriptionProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<DrawerDescription className={className} {...props}>
{children}
</DrawerDescription>
);
}
return (
<SideDialogDescription className={className} {...props}>
{children}
</SideDialogDescription>
);
}
export function ResponsiveDialogFooter({ className, children, ...props }: ResponsiveDialogFooterProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<DrawerFooter className={cn('shrink-0', className)} {...props}>
{children}
</DrawerFooter>
);
}
return (
<SideDialogFooter className={className} {...props}>
{children}
</SideDialogFooter>
);
}
export function ResponsiveDialogBody({ className, children, ...props }: ResponsiveDialogBodyProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<div className={cn('flex-1 min-h-0 overflow-y-auto overflow-x-hidden px-4 py-3', className)} {...props}>
{children}
</div>
);
}
return (
<SideDialogBody className={className} {...props}>
{children}
</SideDialogBody>
);
}
export function ResponsiveDialogClose({ asChild, children, ...props }: ResponsiveDialogCloseProps) {
const { isMobile } = React.useContext(ResponsiveDialogContext);
if (isMobile) {
return (
<DrawerClose asChild={asChild} {...props}>
{children}
</DrawerClose>
);
}
return (
<SideDialogClose asChild={asChild} {...props}>
{children}
</SideDialogClose>
);
}