| ... | @@ -1,4 +1,4 @@ | ... | @@ -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 | import { createPortal } from "react-dom"; | 2 | import { createPortal } from "react-dom"; |
| 3 | import { ChevronLeftIcon, ChevronRightIcon, XIcon } from "lucide-react"; | 3 | import { ChevronLeftIcon, ChevronRightIcon, XIcon } from "lucide-react"; |
| 4 | import { Button } from "../ui/button"; | 4 | import { Button } from "../ui/button"; |
| ... | @@ -9,6 +9,7 @@ import { OpenMediaLink } from "../media/OpenMediaLink"; | ... | @@ -9,6 +9,7 @@ import { OpenMediaLink } from "../media/OpenMediaLink"; |
| 9 | import { MediaActions, type MediaActionSource } from "../media/MediaActions"; | 9 | import { MediaActions, type MediaActionSource } from "../media/MediaActions"; |
| 10 | import { MediaVideoPlayer } from "../media/MediaVideoPlayer"; | 10 | import { MediaVideoPlayer } from "../media/MediaVideoPlayer"; |
| 11 | import { isContextMenuOpen } from "../../contextMenuFallback"; | 11 | import { isContextMenuOpen } from "../../contextMenuFallback"; |
| | 12 | import { composerFloatingLayerProps } from "./composerEventScope"; |
| 12 | | 13 | |
| 13 | interface ExpandedImageDialogProps { | 14 | interface ExpandedImageDialogProps { |
| 14 | preview: ExpandedImagePreview; | 15 | preview: ExpandedImagePreview; |
| ... | @@ -79,6 +80,20 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ | ... | @@ -79,6 +80,20 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ |
| 79 | setImageOffset((current) => current + direction); | 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 | useEffect(() => { | 97 | useEffect(() => { |
| 83 | const onKeyDown = (event: globalThis.KeyboardEvent) => { | 98 | const onKeyDown = (event: globalThis.KeyboardEvent) => { |
| 84 | if (event.defaultPrevented || isContextMenuOpen()) { | 99 | if (event.defaultPrevented || isContextMenuOpen()) { |
| ... | @@ -115,6 +130,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ | ... | @@ -115,6 +130,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ |
| 115 | | 130 | |
| 116 | return createPortal( | 131 | return createPortal( |
| 117 | <div | 132 | <div |
| | 133 | {...composerFloatingLayerProps} |
| 118 | className="fixed inset-0 z-[60] flex items-center justify-center bg-black/75 px-4 py-6 [-webkit-app-region:no-drag]" | 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 | role="dialog" | 135 | role="dialog" |
| 120 | aria-modal="true" | 136 | aria-modal="true" |