diff --git a/src/components/layout/Modal.tsx b/src/components/layout/Modal.tsx deleted file mode 100644 index 11ed75b7..00000000 --- a/src/components/layout/Modal.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { ReactNode } from "react"; -import { createPortal } from "react-dom"; - -import { Overlay } from "@/components/Overlay"; -import { Transition } from "@/components/Transition"; - -interface Props { - show: boolean; - children?: ReactNode; -} - -export function ModalFrame(props: Props) { - return ( - - - - {props.children} - - - - ); -} - -export function Modal(props: Props) { - return createPortal( - {props.children}, - document.body - ); -} - -export function ModalCard(props: { className?: string; children?: ReactNode }) { - return ( -
- {props.children} -
- ); -} diff --git a/src/components/overlays/Modal.tsx b/src/components/overlays/Modal.tsx new file mode 100644 index 00000000..d1534b42 --- /dev/null +++ b/src/components/overlays/Modal.tsx @@ -0,0 +1,42 @@ +import { ReactNode, useCallback } from "react"; +import { Helmet } from "react-helmet-async"; + +import { OverlayPortal } from "@/components/overlays/OverlayDisplay"; +import { useQueryParam } from "@/hooks/useQueryParams"; + +export function useModal(id: string) { + const [currentModal, setCurrentModal] = useQueryParam("m"); + const show = useCallback(() => setCurrentModal(id), [id, setCurrentModal]); + const hide = useCallback(() => setCurrentModal(null), [setCurrentModal]); + return { + id, + isShown: currentModal === id, + show, + hide, + }; +} + +export function ModalCard(props: { children?: ReactNode }) { + return ( +
+
+ {props.children} +
+
+ ); +} + +export function Modal(props: { id: string; children?: ReactNode }) { + const modal = useModal(props.id); + + return ( + + + + +
+ {props.children} +
+
+ ); +} diff --git a/src/components/overlays/OverlayDisplay.tsx b/src/components/overlays/OverlayDisplay.tsx index 2f21d0ac..a9a09d74 100644 --- a/src/components/overlays/OverlayDisplay.tsx +++ b/src/components/overlays/OverlayDisplay.tsx @@ -29,14 +29,16 @@ export function OverlayDisplay(props: { children: ReactNode }) { return
{props.children}
; } -export function Overlay(props: OverlayProps) { - const router = useInternalOverlayRouter(props.id); +export function OverlayPortal(props: { + children?: ReactNode; + darken?: boolean; + show?: boolean; + close?: () => void; +}) { const [portalElement, setPortalElement] = useState(null); const ref = useRef(null); const target = useRef(null); - - // listen for anchor updates - useRouterAnchorUpdate(props.id); + const close = props.close; useEffect(() => { function listen(e: MouseEvent) { @@ -55,9 +57,9 @@ export function Overlay(props: OverlayProps) { if (e.currentTarget !== e.target) return; if (!startedTarget) return; if (!startedTarget.isEqualNode(e.currentTarget as Element)) return; - router.close(); + close?.(); }, - [router] + [close] ); useEffect(() => { @@ -69,8 +71,8 @@ export function Overlay(props: OverlayProps) {
{portalElement ? createPortal( - -
+ +
); } + +export function Overlay(props: OverlayProps) { + const router = useInternalOverlayRouter(props.id); + + // listen for anchor updates + useRouterAnchorUpdate(props.id); + + return ( + + {props.children} + + ); +} diff --git a/src/hooks/useOverlayRouter.ts b/src/hooks/useOverlayRouter.ts index 3c46fc6c..725b516b 100644 --- a/src/hooks/useOverlayRouter.ts +++ b/src/hooks/useOverlayRouter.ts @@ -13,8 +13,8 @@ function joinPath(path: string[]): string { } export function useRouterAnchorUpdate(id: string) { - const setAnchorPoint = useOverlayStore((s) => s.setAnchorPoint); const [route] = useQueryParam("r"); + const setAnchorPoint = useOverlayStore((s) => s.setAnchorPoint); const routerActive = useMemo( () => !!route && route.startsWith(`/${id}`), [route, id] diff --git a/src/pages/settings/AccountActionsPart.tsx b/src/pages/settings/AccountActionsPart.tsx index 61166ccc..2933a22e 100644 --- a/src/pages/settings/AccountActionsPart.tsx +++ b/src/pages/settings/AccountActionsPart.tsx @@ -3,7 +3,8 @@ import { useAsyncFn } from "react-use"; import { deleteUser } from "@/backend/accounts/user"; import { Button } from "@/components/Button"; import { SolidSettingsCard } from "@/components/layout/SettingsCard"; -import { Heading2, Heading3 } from "@/components/utils/Text"; +import { Modal, ModalCard, useModal } from "@/components/overlays/Modal"; +import { Heading2, Heading3, Paragraph } from "@/components/utils/Text"; import { useAuthData } from "@/hooks/auth/useAuthData"; import { useBackendUrl } from "@/hooks/auth/useBackendUrl"; import { useAuthStore } from "@/stores/auth"; @@ -12,13 +13,14 @@ export function AccountActionsPart() { const url = useBackendUrl(); const account = useAuthStore((s) => s.account); const { logout } = useAuthData(); + const deleteModal = useModal("account-delete"); + const [deleteResult, deleteExec] = useAsyncFn(async () => { if (!account) return; - // eslint-disable-next-line no-restricted-globals - if (!confirm("You sure bro?")) return; await deleteUser(url, account); - logout(); - }, [logout, account, url]); + await logout(); + deleteModal.hide(); + }, [logout, account, url, deleteModal.hide]); if (!account) return null; @@ -39,13 +41,29 @@ export function AccountActionsPart() {
+ + + Are you sure? + + Are you sure you want to delete your account? All your data will be + lost! + + + +
); } diff --git a/src/setup/index.css b/src/setup/index.css index efec734f..6317bc34 100644 --- a/src/setup/index.css +++ b/src/setup/index.css @@ -31,6 +31,10 @@ body[data-no-select] { user-select: none; } +html[data-no-scroll], html[data-no-scroll] body { + overflow: hidden; +} + .roll { animation: roll 1s; } @@ -218,4 +222,4 @@ input[type=range].styled-slider.slider-progress::-ms-fill-lower { .tabbable:focus-visible { outline: 2px solid theme('colors.themePreview.primary'); box-shadow: 0 0 10px theme('colors.themePreview.secondary'); -} \ No newline at end of file +}