| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | | import { memo, useCallback, useEffect, useState, type ReactNode } from "react"; |
| 1 | import { memo, useCallback, useEffect, useRef, useState, type ReactNode } from "react"; |
| 2 | 2 | import { createPortal } from "react-dom"; |
| 3 | 3 | import { ChevronLeftIcon, ChevronRightIcon, XIcon } from "lucide-react"; |
| 4 | 4 | import { Button } from "../ui/button"; |
| ... | ... | @@ -9,6 +9,7 @@ import { OpenMediaLink } from "../media/OpenMediaLink"; |
| 9 | 9 | import { MediaActions, type MediaActionSource } from "../media/MediaActions"; |
| 10 | 10 | import { MediaVideoPlayer } from "../media/MediaVideoPlayer"; |
| 11 | 11 | import { isContextMenuOpen } from "../../contextMenuFallback"; |
| 12 | import { composerFloatingLayerProps } from "./composerEventScope"; |
| 12 | 13 | |
| 13 | 14 | interface ExpandedImageDialogProps { |
| 14 | 15 | preview: ExpandedImagePreview; |
| ... | ... | @@ -79,6 +80,20 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ |
| 79 | 80 | setImageOffset((current) => current + direction); |
| 80 | 81 | }, []); |
| 81 | 82 | |
| 83 | // The element that opened the preview gets focus back on close. Without |
| 84 | // this a close button click leaves focus on the unmounted dialog, and the |
| 85 | // composer that owned the opener reads that as a blur and rests. |
| 86 | const openerRef = useRef<Element | null>(null); |
| 87 | useEffect(() => { |
| 88 | openerRef.current = document.activeElement; |
| 89 | return () => { |
| 90 | const opener = openerRef.current; |
| 91 | if (opener instanceof HTMLElement && opener.isConnected) { |
| 92 | opener.focus({ preventScroll: true }); |
| 93 | } |
| 94 | }; |
| 95 | }, []); |
| 96 | |
| 82 | 97 | useEffect(() => { |
| 83 | 98 | const onKeyDown = (event: globalThis.KeyboardEvent) => { |
| 84 | 99 | if (event.defaultPrevented || isContextMenuOpen()) { |
| ... | ... | @@ -115,6 +130,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ |
| 115 | 130 | |
| 116 | 131 | return createPortal( |
| 117 | 132 | <div |
| 133 | {...composerFloatingLayerProps} |
| 118 | 134 | className="fixed inset-0 z-[60] flex items-center justify-center bg-black/75 px-4 py-6 [-webkit-app-region:no-drag]" |
| 119 | 135 | role="dialog" |
| 120 | 136 | aria-modal="true" |