mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 06:58:10 -05:00
Add Dialog element
This commit is contained in:
parent
6a23c4f2bb
commit
7af4912453
61
app/components/Dialog.tsx
Normal file
61
app/components/Dialog.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import React from "react";
|
||||
|
||||
export function Dialog({
|
||||
children,
|
||||
isOpen,
|
||||
close,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
isOpen: boolean;
|
||||
close: () => void;
|
||||
}) {
|
||||
const ref = useDOMSync(isOpen);
|
||||
|
||||
// https://stackoverflow.com/a/26984690
|
||||
const closeOnOutsideClick = (
|
||||
event: React.MouseEvent<HTMLDialogElement, MouseEvent>
|
||||
) => {
|
||||
const rect: DOMRect = ref.current.getBoundingClientRect();
|
||||
const isInDialog =
|
||||
rect.top <= event.clientY &&
|
||||
event.clientY <= rect.top + rect.height &&
|
||||
rect.left <= event.clientX &&
|
||||
event.clientX <= rect.left + rect.width;
|
||||
if (!isInDialog) {
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<dialog ref={ref} onClick={closeOnOutsideClick}>
|
||||
<button onClick={close}>x</button>
|
||||
{children}
|
||||
</dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function useDOMSync(isOpen: boolean) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const ref = React.useRef<any>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (ref.current.open && isOpen) return;
|
||||
if (!ref.current.open && !isOpen) return;
|
||||
|
||||
const html = document.getElementsByTagName("html")[0];
|
||||
|
||||
if (isOpen) {
|
||||
ref.current.showModal();
|
||||
html.classList.add("lock-scroll");
|
||||
} else {
|
||||
ref.current.close();
|
||||
html.classList.remove("lock-scroll");
|
||||
}
|
||||
|
||||
return () => {
|
||||
html.classList.remove("lock-scroll");
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
return ref;
|
||||
}
|
||||
11
app/routes/plus/comment.tsx
Normal file
11
app/routes/plus/comment.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { useNavigate } from "@remix-run/react";
|
||||
import { Dialog } from "~/components/Dialog";
|
||||
|
||||
export default function PlusCommentModalPage() {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Dialog isOpen close={() => navigate("/plus")}>
|
||||
hello world
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,21 +4,21 @@ import type {
|
|||
MetaFunction,
|
||||
} from "@remix-run/node";
|
||||
import { json } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { Outlet, useLoaderData } from "@remix-run/react";
|
||||
import * as React from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import { Button } from "~/components/Button";
|
||||
import { Catcher } from "~/components/Catcher";
|
||||
import { upcomingVoting } from "~/core/plus";
|
||||
import { db } from "~/db";
|
||||
import type * as plusSuggestions from "~/db/models/plusSuggestions.server";
|
||||
import { useUser } from "~/hooks/useUser";
|
||||
import { makeTitle, requireUser } from "~/utils/remix";
|
||||
import styles from "~/styles/plus.css";
|
||||
import { Catcher } from "~/components/Catcher";
|
||||
import * as React from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
import type { Unpacked } from "~/utils/types";
|
||||
import { Avatar } from "~/components/Avatar";
|
||||
import { Button } from "~/components/Button";
|
||||
import { discordFullName } from "~/utils/strings";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import { makeTitle, requireUser } from "~/utils/remix";
|
||||
import { discordFullName } from "~/utils/strings";
|
||||
import type { Unpacked } from "~/utils/types";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
|
|
@ -67,36 +67,39 @@ export default function PlusPage() {
|
|||
invariant(visibleSuggestions);
|
||||
|
||||
return (
|
||||
<div className="plus__container">
|
||||
<SuggestedForInfo />
|
||||
<div className="stack md">
|
||||
<div className="plus__radios">
|
||||
{data.suggestions.map(({ tier, users }) => {
|
||||
const id = String(tier);
|
||||
return (
|
||||
<div key={id} className="plus__radio-container">
|
||||
<label htmlFor={id} className="plus__radio-label">
|
||||
+{tier}{" "}
|
||||
<span className="plus__users-count">({users.length})</span>
|
||||
</label>
|
||||
<input
|
||||
id={id}
|
||||
name="tier"
|
||||
type="radio"
|
||||
checked={tierVisible === tier}
|
||||
onChange={() => setTierVisible(tier)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="stack lg">
|
||||
{visibleSuggestions.users.map((u) => (
|
||||
<SuggestedUser key={`${u.info.id}-${tierVisible}`} user={u} />
|
||||
))}
|
||||
<>
|
||||
<Outlet />
|
||||
<div className="plus__container">
|
||||
<SuggestedForInfo />
|
||||
<div className="stack md">
|
||||
<div className="plus__radios">
|
||||
{data.suggestions.map(({ tier, users }) => {
|
||||
const id = String(tier);
|
||||
return (
|
||||
<div key={id} className="plus__radio-container">
|
||||
<label htmlFor={id} className="plus__radio-label">
|
||||
+{tier}{" "}
|
||||
<span className="plus__users-count">({users.length})</span>
|
||||
</label>
|
||||
<input
|
||||
id={id}
|
||||
name="tier"
|
||||
type="radio"
|
||||
checked={tierVisible === tier}
|
||||
onChange={() => setTierVisible(tier)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="stack lg">
|
||||
{visibleSuggestions.users.map((u) => (
|
||||
<SuggestedUser key={`${u.info.id}-${tierVisible}`} user={u} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,10 @@
|
|||
gap: var(--s-8);
|
||||
}
|
||||
|
||||
.lock-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Utility classes
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
--bg-lighter-transparent: hsla(237.3deg 42.3% 35.6% / 50%);
|
||||
--bg-darker-very-transparent: hsla(237.3deg 42.3% 26.6% / 50%);
|
||||
--bg-darker-transparent: hsla(237.3deg 42.3% 26.6% / 90%);
|
||||
--bg-modal-backdrop: hsla(237deg 98% 1% / 70%);
|
||||
--border: hsl(237.3deg 42.3% 45.6%);
|
||||
--button-text: rgb(0 0 0 / 85%);
|
||||
--button-text-transparent: rgb(0 0 0 / 65%);
|
||||
|
|
@ -318,3 +317,33 @@ select::selection {
|
|||
hr {
|
||||
border-color: var(--theme-transparent);
|
||||
}
|
||||
|
||||
dialog {
|
||||
border: 0;
|
||||
margin: auto;
|
||||
background-color: var(--bg);
|
||||
border-radius: var(--rounded);
|
||||
}
|
||||
|
||||
dialog::backdrop {
|
||||
background: hsla(237deg 98% 1% / 70%);
|
||||
}
|
||||
|
||||
@supports ((-webkit-backdrop-filter: none) or (backdrop-filter: none)) {
|
||||
dialog::backdrop {
|
||||
-webkit-backdrop-filter: blur(7px) brightness(70%);
|
||||
backdrop-filter: blur(7px) brightness(70%);
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
dialog[open],
|
||||
dialog::backdrop {
|
||||
animation: show 500ms ease;
|
||||
}
|
||||
|
||||
@keyframes show {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user