mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-18 13:10:29 -05:00
* Add to nav * Allow nav items on front page to take full width * Initial+ * Fix vods page title * Add page title * Common art type * ArtGrid to a different component * User arts page initial * Add art tab to user page * Preview initial * Fix art counting * Fix link and onclick overlapping * Link to user art page to arts they made * Artist role initial * Show description * Make toggle in art page saved in search params * Add white-space pre-wrap to plus comments Not sure why it was removed in the first place? * Commission open / text and edit those * Add simple pagination * New art link display logic * New art initial * Upload art * Hide art from side nav too * Show banner when waiting for approval * Edit art * Fix art sub nav link not showing active * Relocate unvalidated art text * Delete art * Extract ImagePreview component * Eliminate some layout shift * BigImageDialog extract component + prevent layout shift * i18n * Fix unused var * Fix tests
72 lines
1.7 KiB
TypeScript
72 lines
1.7 KiB
TypeScript
import React from "react";
|
|
import invariant from "tiny-invariant";
|
|
|
|
export function Dialog({
|
|
children,
|
|
isOpen,
|
|
close,
|
|
className,
|
|
closeOnAnyClick,
|
|
}: {
|
|
children: React.ReactNode;
|
|
isOpen: boolean;
|
|
close?: () => void;
|
|
className?: string;
|
|
closeOnAnyClick?: boolean;
|
|
}) {
|
|
const ref = useDOMSync(isOpen);
|
|
|
|
// https://stackoverflow.com/a/26984690
|
|
const closeOnOutsideClick = close
|
|
? (event: React.MouseEvent<HTMLDialogElement, MouseEvent>) => {
|
|
if (closeOnAnyClick) return close();
|
|
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();
|
|
}
|
|
}
|
|
: undefined;
|
|
|
|
return (
|
|
<dialog className={className} ref={ref} onClick={closeOnOutsideClick}>
|
|
{children}
|
|
</dialog>
|
|
);
|
|
}
|
|
|
|
function useDOMSync(isOpen: boolean) {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const ref = React.useRef<any>(null);
|
|
|
|
React.useEffect(() => {
|
|
const dialog = ref.current;
|
|
|
|
if (dialog.open && isOpen) return;
|
|
if (!dialog.open && !isOpen) return;
|
|
|
|
const html = document.getElementsByTagName("html")[0];
|
|
invariant(html);
|
|
|
|
if (isOpen) {
|
|
dialog.showModal();
|
|
// TODO: can be replaced with https://twitter.com/argyleink/status/1529869352660439048 once gets control
|
|
html.classList.add("lock-scroll");
|
|
} else {
|
|
dialog.close();
|
|
html.classList.remove("lock-scroll");
|
|
}
|
|
|
|
return () => {
|
|
dialog.close();
|
|
html.classList.remove("lock-scroll");
|
|
};
|
|
}, [isOpen]);
|
|
|
|
return ref;
|
|
}
|