-
Notifications
You must be signed in to change notification settings - Fork 462
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5e57c2c
commit 975c8fc
Showing
5 changed files
with
314 additions
and
7 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import { useMediaQuery } from "foxact/use-media-query"; | ||
import { type ComponentProps, useCallback, useState } from "react"; | ||
import { | ||
Dialog, | ||
DialogBody, | ||
DialogCloseButton, | ||
DialogContent, | ||
DialogFooter, | ||
DialogHeader, | ||
DialogOverlay, | ||
DialogPortal, | ||
DialogTitle, | ||
DialogTrigger, | ||
} from "./Dialog"; | ||
import { | ||
Drawer, | ||
DrawerClose, | ||
DrawerContent, | ||
DrawerFooter, | ||
DrawerHeader, | ||
DrawerOverlay, | ||
DrawerPortal, | ||
DrawerTitle, | ||
DrawerTrigger, | ||
} from "./Drawer"; | ||
|
||
const useIsMobile = () => useMediaQuery("(max-width: 640px)", false); | ||
|
||
export const ResponsiveDialog = ({ | ||
open: openProp, | ||
onOpenChange: onOpenChangeProp, | ||
...props | ||
}: ComponentProps<typeof Dialog> & ComponentProps<typeof Drawer>) => { | ||
const [open, setOpen] = useState(openProp); | ||
|
||
const onOpenChange = useCallback( | ||
(newOpen: boolean) => { | ||
setOpen(newOpen); | ||
if (typeof onOpenChangeProp === "function") onOpenChangeProp(newOpen); | ||
}, | ||
[onOpenChangeProp], | ||
); | ||
|
||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) | ||
return <Drawer {...props} open={open} onOpenChange={onOpenChange} />; | ||
|
||
return <Dialog {...props} open={open} onOpenChange={onOpenChange} />; | ||
}; | ||
ResponsiveDialog.displayName = "ResponsiveDialog"; | ||
|
||
export const ResponsiveDialogTrigger = ( | ||
props: ComponentProps<typeof DialogTrigger> & | ||
ComponentProps<typeof DrawerTrigger>, | ||
) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) return <DrawerTrigger {...props} />; | ||
|
||
return <DialogTrigger {...props} />; | ||
}; | ||
ResponsiveDialogTrigger.displayName = "ResponsiveDialogTrigger"; | ||
|
||
export const ResponsiveDialogPortal = ( | ||
props: ComponentProps<typeof DialogPortal> & | ||
ComponentProps<typeof DrawerPortal>, | ||
) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) return <DrawerPortal {...props} />; | ||
|
||
return <DialogPortal {...props} />; | ||
}; | ||
ResponsiveDialogPortal.displayName = "ResponsiveDialogPortal"; | ||
|
||
export const ResponsiveDialogOverlay = ( | ||
props: ComponentProps<typeof DialogOverlay> & | ||
ComponentProps<typeof DrawerOverlay>, | ||
) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) return <DrawerOverlay {...props} />; | ||
|
||
return <DialogOverlay {...props} />; | ||
}; | ||
ResponsiveDialogOverlay.displayName = "ResponsiveDialogOverlay"; | ||
|
||
export const ResponsiveDialogContent = ( | ||
props: ComponentProps<typeof DialogContent> & | ||
ComponentProps<typeof DrawerContent>, | ||
) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) return <DrawerContent {...props} />; | ||
|
||
return <DialogContent {...props} />; | ||
}; | ||
ResponsiveDialogContent.displayName = "ResponsiveDialogContent"; | ||
|
||
export const ResponsiveDialogCloseButton = ({ | ||
children, | ||
asChild, | ||
...props | ||
}: ComponentProps<typeof DialogCloseButton> & | ||
ComponentProps<typeof DrawerClose>) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) | ||
return ( | ||
<DrawerClose {...props} asChild={asChild}> | ||
{children} | ||
</DrawerClose> | ||
); | ||
|
||
return <DialogCloseButton {...props} />; | ||
}; | ||
ResponsiveDialogCloseButton.displayName = "ResponsiveDialogCloseButton"; | ||
|
||
export const ResponsiveDialogHeader = ( | ||
props: ComponentProps<typeof DialogHeader> & | ||
ComponentProps<typeof DrawerHeader>, | ||
) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) return <DrawerHeader {...props} />; | ||
|
||
return <DialogHeader {...props} />; | ||
}; | ||
ResponsiveDialogHeader.displayName = "ResponsiveDialogHeader"; | ||
|
||
export const ResponsiveDialogTitle = ( | ||
props: ComponentProps<typeof DialogTitle> & | ||
ComponentProps<typeof DrawerTitle>, | ||
) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) return <DrawerTitle {...props} />; | ||
|
||
return <DialogTitle {...props} />; | ||
}; | ||
ResponsiveDialogTitle.displayName = "ResponsiveDialogTitle"; | ||
|
||
export const ResponsiveDialogBody = DialogBody; | ||
ResponsiveDialogBody.displayName = "ResponsiveDialogBody"; | ||
|
||
export const ResponsiveDialogFooter = ( | ||
props: ComponentProps<typeof DialogFooter> & | ||
ComponentProps<typeof DrawerFooter>, | ||
) => { | ||
const isMobile = useIsMobile(); | ||
|
||
if (isMobile) return <DrawerFooter {...props} />; | ||
|
||
return <DialogFooter {...props} />; | ||
}; | ||
ResponsiveDialogFooter.displayName = "ResponsiveDialogFooter"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { Button } from "app/components/ui/Button"; | ||
import type { DialogContentProps } from "app/components/ui/Dialog"; | ||
import { | ||
ResponsiveDialog, | ||
ResponsiveDialogBody, | ||
ResponsiveDialogCloseButton, | ||
ResponsiveDialogContent, | ||
ResponsiveDialogFooter, | ||
ResponsiveDialogHeader, | ||
ResponsiveDialogTitle, | ||
ResponsiveDialogTrigger, | ||
} from "app/components/ui/ResponsiveDialog"; | ||
|
||
import type { ComponentProps } from "react"; | ||
|
||
const ResponsiveDialogExample = ({ | ||
size, | ||
longContent, | ||
scrollBody, | ||
showHeader = true, | ||
showFooter, | ||
}: { | ||
size?: DialogContentProps["size"]; | ||
longContent?: ComponentProps<typeof DynamicDialogContent>["longContent"]; | ||
scrollBody?: boolean; | ||
showHeader?: boolean; | ||
showFooter?: boolean; | ||
}) => ( | ||
<ResponsiveDialog> | ||
<ResponsiveDialogTrigger>Open responsive dialog</ResponsiveDialogTrigger> | ||
<ResponsiveDialogContent size={size} scrollBody={scrollBody}> | ||
{showHeader && ( | ||
<ResponsiveDialogHeader> | ||
<ResponsiveDialogTitle>Responsive dialog</ResponsiveDialogTitle> | ||
</ResponsiveDialogHeader> | ||
)} | ||
|
||
<ResponsiveDialogBody scroll={scrollBody}> | ||
<DynamicDialogContent longContent={longContent} /> | ||
</ResponsiveDialogBody> | ||
|
||
{showFooter && ( | ||
<ResponsiveDialogFooter>Sneaky footer</ResponsiveDialogFooter> | ||
)} | ||
|
||
<ResponsiveDialogCloseButton asChild> | ||
<Button variant="subtle" className="mx-4 mb-4"> | ||
Close | ||
</Button> | ||
</ResponsiveDialogCloseButton> | ||
</ResponsiveDialogContent> | ||
</ResponsiveDialog> | ||
); | ||
|
||
const meta: Meta<typeof ResponsiveDialogExample> = { | ||
title: "Design system/ResponsiveDialog", | ||
component: ResponsiveDialogExample, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof ResponsiveDialogExample>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
longContent: false, | ||
size: "md", | ||
scrollBody: false, | ||
showHeader: true, | ||
showFooter: false, | ||
}, | ||
argTypes: { | ||
size: { | ||
control: { | ||
type: "select", | ||
}, | ||
options: ["sm", "md", "lg", "xl", "2xl", "3xl", "4xl"], | ||
}, | ||
}, | ||
}; | ||
|
||
const DynamicDialogContent = ({ longContent }: { longContent?: boolean }) => { | ||
if (!longContent) return <p>This is a simple dialog.</p>; | ||
|
||
return ( | ||
<> | ||
<p> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin a luctus | ||
lorem. Etiam aliquam vel est vel lacinia. Nulla vehicula tortor quis | ||
erat pellentesque, et interdum nisl aliquet. Aenean ac malesuada mauris. | ||
Aliquam scelerisque lorem ut metus interdum, ut venenatis orci egestas. | ||
In semper mollis eros lobortis mollis. Praesent vestibulum convallis | ||
ipsum at fermentum. Donec porta facilisis lacus eu dignissim. Proin | ||
hendrerit orci gravida risus commodo fermentum. Donec finibus sapien eu | ||
nibh mattis dictum. Praesent ac tempor odio, et lobortis ex. Donec nec | ||
mauris et lorem facilisis auctor. Maecenas vitae convallis leo. | ||
</p> | ||
<p> | ||
Maecenas maximus felis scelerisque turpis euismod rutrum. Pellentesque a | ||
dolor scelerisque, elementum sapien eu, porta libero. Donec volutpat | ||
egestas tincidunt. Vivamus blandit eros mollis viverra aliquam. Mauris | ||
eu turpis id est gravida finibus. In viverra, elit eget eleifend | ||
sagittis, massa quam faucibus erat, sed mattis odio nunc vitae enim. | ||
Donec lacus diam, lobortis at facilisis in, placerat ut diam. Maecenas | ||
sed dui sit amet massa tristique vulputate non ac erat. Nullam orci | ||
urna, finibus eu blandit et, pharetra ac enim. Donec magna augue, | ||
interdum at sollicitudin id, fringilla nec sapien. In nisl quam, rhoncus | ||
blandit ipsum in, volutpat aliquam nisi. Nam accumsan lobortis ante, at | ||
tristique ante vehicula eget. Sed ornare varius velit, ut ultrices ante | ||
auctor non. Cras bibendum, libero sed varius fringilla, quam lorem | ||
fermentum nunc, vitae varius mauris libero sed tortor. | ||
</p> | ||
<p> | ||
Donec ut aliquam massa. Etiam congue turpis at purus tempor maximus. | ||
Etiam semper libero non varius pellentesque. Ut egestas faucibus purus | ||
non faucibus. Duis sed nisi consequat, laoreet nisi in, laoreet purus. | ||
Integer aliquam mi ac metus interdum rhoncus. In ac iaculis quam. Sed eu | ||
nibh lectus. Donec imperdiet vestibulum nisl in facilisis. Sed molestie | ||
ipsum eu orci imperdiet cursus. Morbi sit amet quam mi. Etiam maximus | ||
scelerisque orci, id gravida ligula sagittis in. Maecenas volutpat quam | ||
elit, vel vehicula ex fringilla ac. Aenean risus lectus, pellentesque id | ||
est eget, feugiat hendrerit ligula. Nulla tempor pulvinar lacus, ut | ||
euismod tortor. | ||
</p> | ||
</> | ||
); | ||
}; |