feat: add dialog

This commit is contained in:
Mathias Schopmans
2024-02-16 10:02:21 +01:00
committed by Mathias Schopmans
parent 86c1d9090b
commit 38a59b029b
8 changed files with 705 additions and 13 deletions

View File

@@ -0,0 +1,98 @@
.overlay {
background: var(--overlay);
position: fixed;
inset: 0;
backdrop-filter: blur(2px);
animation: overlayShow 300ms cubic-bezier(0.16, 1, 0.3, 1);
&[data-state="closed"] {
animation: overlayHide 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
}
.content {
background-color: var(--dialog-bg);
border-radius: 6px;
box-shadow:
rgba(0, 0, 0, 0.2) 0px 10px 38px -10px,
rgba(0, 0, 0, 0.2) 0px 10px 20px -15px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90vw;
max-width: 450px;
max-height: 85vh;
padding: 25px;
animation: contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
&[data-state="closed"] {
animation: contentHide 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
}
.content:focus {
outline: none;
}
.close {
position: absolute;
top: 22px;
right: 22px;
width: 22px;
height: 22px;
cursor: pointer;
background: none;
border: none;
}
.title {
margin: 0;
font-size: 17px;
}
.description {
margin: 10px 0 20px;
font-size: 15px;
line-height: 1.5;
}
@keyframes overlayShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes overlayHide {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes contentShow {
from {
opacity: 0;
transform: translate(-50%, -40%) scale(0.9);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
@keyframes contentHide {
from {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
to {
opacity: 0;
transform: translate(-50%, -40%) scale(0.9);
}
}

View File

@@ -0,0 +1,102 @@
import {
ComponentPropsWithoutRef,
ElementRef,
HTMLAttributes,
forwardRef,
} from "react";
import styles from "./Dialog.module.css";
import Close from "@/components/Icons/Close";
import { cn } from "@/lib/utils";
import * as DialogPrimitive from "@radix-ui/react-dialog";
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = forwardRef<
ElementRef<typeof DialogPrimitive.Overlay>,
ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(styles.overlay, className)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = forwardRef<
ElementRef<typeof DialogPrimitive.Content>,
ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal
container={
typeof document != "undefined"
? document.getElementById("layout")
: undefined
}
>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(styles.content, className)}
{...props}
>
{children}
<DialogPrimitive.Close asChild>
<button type="button" className={styles.close} aria-label="Close">
<Close />
</button>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: HTMLAttributes<HTMLDivElement>) => (
<div className={cn(className)} {...props} />
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
...props
}: HTMLAttributes<HTMLDivElement>) => (
<div className={cn(className)} {...props} />
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = forwardRef<
ElementRef<typeof DialogPrimitive.Title>,
ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title ref={ref} className={cn(className)} {...props} />
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = forwardRef<
ElementRef<typeof DialogPrimitive.Description>,
ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description ref={ref} className={cn(className)} {...props} />
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
};

View File

@@ -22,7 +22,10 @@ export const Layout: FC<LayoutProps> = ({
layoutClass = "default",
}) => {
return (
<div className={cn(styles.layout, font.className, styles[layoutClass])}>
<div
id="layout"
className={cn(styles.layout, font.className, styles[layoutClass])}
>
<header className={cn(styles.container, styles.header)}>
<Logo />
<Navigation />

View File

@@ -9,7 +9,7 @@
display: inline-block;
vertical-align: middle;
width: 22px;
margin: 0 10px 0 0;
margin: 0 6px 0 0;
}
@media (max-width: 900px) {

View File

@@ -1,7 +1,17 @@
import Link from "next/link";
import { useRouter } from "next/router";
import styles from "./Navigation.module.css";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/Dialog/Dialog";
import IconFilter from "@/components/Icons/Filter";
import IconOverview from "@/components/Icons/Overview";
import IconQuestion from "@/components/Icons/Question";
@@ -19,8 +29,19 @@ export function Navigation() {
</Link>
</li>
<li className={styles.item}>
<IconFilter className={styles.icon} />
<span className={styles.label}>Filter</span>
<Dialog>
<DialogTrigger asChild>
<a href="#">
<IconFilter className={styles.icon} />
<span className={styles.label}>Filter</span>
</a>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Filter by Tags</DialogTitle>
</DialogHeader>
</DialogContent>
</Dialog>
</li>
<li className={styles.item}>
<Link href="/overview">

View File

@@ -9,6 +9,10 @@
--background: #173d7a;
--highlight: #029df7;
--border: rgba(255, 255, 255, 0.1);
--overlay: #081a37b5;
--dialog-bg: #173d7a;
--badge: #333; /* local color which get's overridden by the element */
}
@@ -25,7 +29,8 @@ html {
html,
body {
max-width: 100vw;
overflow-x: hidden;
margin: 0;
padding: 0;
}
body {