responsive-dialog.tsx

quick hack for a responsive dialog-drawer built on components from ui.shadcn.com

import * as React from 'react';

import { useMediaQuery } from '@/lib/hooks';
import * as DialogParts from '@/ui/dialog';
import * as DrawerParts from '@/ui/drawer';

type ResponsiveDialogPartsKey =
  | ''
  | 'Portal'
  | 'Overlay'
  | 'Trigger'
  | 'Close'
  | 'Content'
  | 'Header'
  | 'Footer'
  | 'Title'
  | 'Description';

const makeResponsivePart = (type: ResponsiveDialogPartsKey) => {
  type PartKey = typeof type;
  type DialogTypeProps = React.ComponentPropsWithoutRef<
    (typeof DialogParts)[`Dialog${PartKey}`]
  >;
  type DrawerTypeProps = React.ComponentPropsWithoutRef<
    (typeof DrawerParts)[`Drawer${PartKey}`]
  >;
  type Props = DialogTypeProps | DrawerTypeProps;

  const c = function ResponsiveComponent(props: Props) {
    const isDesktop = useMediaQuery('(min-width: 768px)');
    const Component = isDesktop
      ? DialogParts[`Dialog${type}`]
      : DrawerParts[`Drawer${type}`];
    // @ts-expect-error - needs to be typed properly
    return <Component {...props} />;
  };
  c.displayName = `ResponsiveDialog${type}`;

  return c as React.FC<Props>;
};

export const ResponsiveDialog = makeResponsivePart('');
export const ResponsiveDialogPortal = makeResponsivePart('Portal');
export const ResponsiveDialogOverlay = makeResponsivePart('Overlay');
export const ResponsiveDialogTrigger = makeResponsivePart('Trigger');
export const ResponsiveDialogClose = makeResponsivePart('Close');
export const ResponsiveDialogContent = makeResponsivePart('Content');
export const ResponsiveDialogHeader = makeResponsivePart('Header');
export const ResponsiveDialogFooter = makeResponsivePart('Footer');
export const ResponsiveDialogTitle = makeResponsivePart('Title');
export const ResponsiveDialogDescription = makeResponsivePart('Description');