diff --git a/external/lang b/external/lang index 4f3c6a8d..2a0b524d 160000 --- a/external/lang +++ b/external/lang @@ -1 +1 @@ -Subproject commit 4f3c6a8d77f2977c8d5c69690ba4fbe477610f09 +Subproject commit 2a0b524d892349cb956ee49af873fc87999e2206 diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index 67009c7c..d9c9a100 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -1,9 +1,10 @@ import styled, { css, keyframes } from "styled-components"; -import { createPortal, useEffect } from "preact/compat"; +import { createPortal, useEffect, useState } from "preact/compat"; import { Children } from "../../types/Preact"; import Button, { ButtonProps } from "./Button"; +import { internalSubscribe } from "../../lib/eventEmitter"; const open = keyframes` 0% {opacity: 0;} @@ -11,12 +12,23 @@ const open = keyframes` 100% {opacity: 1;} `; +const close = keyframes` + 0% {opacity: 1;} + 70% {opacity: 0;} + 100% {opacity: 0;} +`; + const zoomIn = keyframes` 0% {transform: scale(0.5);} 98% {transform: scale(1.01);} 100% {transform: scale(1);} `; +const zoomOut = keyframes` + 0% {transform: scale(1);} + 100% {transform: scale(0.5);} +`; + const ModalBase = styled.div` top: 0; left: 0; @@ -36,6 +48,14 @@ const ModalBase = styled.div` color: var(--foreground); background: rgba(0, 0, 0, 0.8); + + &.closing { + animation-name: ${close}; + } + + &.closing > div { + animation-name: ${zoomOut}; + } `; const ModalContainer = styled.div` @@ -120,6 +140,8 @@ interface Props { visible: boolean; } +export let isModalClosing = false; + export default function Modal(props: Props) { if (!props.visible) return null; @@ -138,12 +160,21 @@ export default function Modal(props: Props) { return content; } + const [animateClose, setAnimateClose] = useState(false); + isModalClosing = animateClose; + function onClose() { + setAnimateClose(true); + setTimeout(() => props.onClose(), 2e2); + } + + useEffect(() => internalSubscribe('Modal', 'close', onClose), []); + useEffect(() => { if (props.disallowClosing) return; function keyDown(e: KeyboardEvent) { if (e.key === "Escape") { - props.onClose(); + onClose(); } } @@ -154,6 +185,7 @@ export default function Modal(props: Props) { let confirmationAction = props.actions?.find( (action) => action.confirmation, ); + useEffect(() => { if (!confirmationAction) return; @@ -171,7 +203,7 @@ export default function Modal(props: Props) { }, [confirmationAction]); return createPortal( - (e.cancelBubble = true)}> {content} diff --git a/src/context/intermediate/Modals.tsx b/src/context/intermediate/Modals.tsx index 4db686dd..9ed2438e 100644 --- a/src/context/intermediate/Modals.tsx +++ b/src/context/intermediate/Modals.tsx @@ -1,3 +1,5 @@ +import { isModalClosing } from "../../components/ui/Modal"; +import { internalEmit } from "../../lib/eventEmitter"; import { Screen } from "./Intermediate"; import { ClipboardModal } from "./modals/Clipboard"; import { ErrorModal } from "./modals/Error"; @@ -12,7 +14,7 @@ export interface Props { } export default function Modals({ screen, openScreen }: Props) { - const onClose = () => openScreen({ id: "none" }); + const onClose = () => isModalClosing ? openScreen({ id: "none" }) : internalEmit('Modal', 'close'); switch (screen.id) { case "_prompt": diff --git a/src/context/intermediate/Popovers.tsx b/src/context/intermediate/Popovers.tsx index f2dc82cc..44c0d9ec 100644 --- a/src/context/intermediate/Popovers.tsx +++ b/src/context/intermediate/Popovers.tsx @@ -1,4 +1,6 @@ import { useContext } from "preact/hooks"; +import { isModalClosing } from "../../components/ui/Modal"; +import { internalEmit } from "../../lib/eventEmitter"; import { IntermediateContext, useIntermediate } from "./Intermediate"; import { SpecialInputModal } from "./modals/Input"; @@ -14,7 +16,7 @@ export default function Popovers() { const { screen } = useContext(IntermediateContext); const { openScreen } = useIntermediate(); - const onClose = () => openScreen({ id: "none" }); + const onClose = () => isModalClosing ? openScreen({ id: "none" }) : internalEmit('Modal', 'close'); switch (screen.id) { case "profile": diff --git a/src/lib/eventEmitter.ts b/src/lib/eventEmitter.ts index 192bae46..751f52d2 100644 --- a/src/lib/eventEmitter.ts +++ b/src/lib/eventEmitter.ts @@ -26,4 +26,5 @@ export function internalEmit(ns: string, event: string, ...args: any[]) { // - MessageBox/append // - TextArea/focus // - ReplyBar/add +// - Modal/close // - PWA/update