authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-10-10 21:47:09-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-10 21:47:09-03:00
log0ecb25a8d45b7768bada974a9d6a99b17cf8bf49
tree54518636a7ef939a9284684d6005b7e44f3f8582
parente4ef8d36bad346e0b8c38da276329cc54a1cce6f
signature Signed by PGP key B5690EEEBB952194

fix(web): media preview centers its content and pins the close button (#17951)


1 files changed, 17 insertions(+), 16 deletions(-)

apps/web/src/components/chat/ExpandedImageDialog.tsx+17-16
...@@ -180,7 +180,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({...@@ -180,7 +180,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({
180 variant="media"180 variant="media"
181 showCloseButton={false}181 showCloseButton={false}
182 bottomStickOnMobile={false}182 bottomStickOnMobile={false}
183 className="row-start-1 max-h-[92vh] w-[92vw] max-w-[92vw] items-center overflow-visible [--media-width:92vw] [--media-height:min(86vh,calc(100vh-160px))] sm:[--media-width:calc(92vw-96px)]"183 className="row-start-1 h-full max-h-[92vh] w-[92vw] max-w-[92vw] items-center justify-center overflow-visible [--media-width:92vw] [--media-height:min(86vh,calc(100vh-160px))] sm:[--media-width:calc(92vw-96px)]"
184 onKeyDown={onKeyDown}184 onKeyDown={onKeyDown}
185 initialFocus={closeButtonRef}185 initialFocus={closeButtonRef}
186 finalFocus={() => returnFocusTarget}186 finalFocus={() => returnFocusTarget}
...@@ -189,12 +189,24 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({...@@ -189,12 +189,24 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({
189 }}189 }}
190 >190 >
191 <DialogTitle className="sr-only">Expanded {mediaLabel} preview</DialogTitle>191 <DialogTitle className="sr-only">Expanded {mediaLabel} preview</DialogTitle>
192 {/* The stage keeps a fixed size, so the close button stays put whatever the media or caption. */}
193 <Button
194 type="button"
195 ref={closeButtonRef}
196 size="icon-sm"
197 variant="media-close"
198 className="absolute right-0 top-0 z-20"
199 onClick={onClose}
200 aria-label={`Close ${mediaLabel} preview`}
201 >
202 <XIcon />
203 </Button>
192 {preview.images.length > 1 && (204 {preview.images.length > 1 && (
193 <Button205 <Button
194 type="button"206 type="button"
195 size="icon"207 size="icon"
196 variant="media-navigation"208 variant="media-navigation"
197 className="left-0 top-auto -bottom-12 translate-y-0 sm:top-1/2 sm:bottom-auto sm:-translate-y-1/2"209 className="left-0 top-auto bottom-0 translate-y-0 sm:top-1/2 sm:bottom-auto sm:-translate-y-1/2"
198 aria-label="Previous media"210 aria-label="Previous media"
199 onClick={() => navigateImage(-1)}211 onClick={() => navigateImage(-1)}
200 >212 >
...@@ -202,18 +214,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({...@@ -202,18 +214,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({
202 </Button>214 </Button>
203 )}215 )}
204 <MediaActions source={actionsSource}>216 <MediaActions source={actionsSource}>
205 <div className="relative isolate z-10 max-h-[92vh] max-w-[var(--media-width)]">217 <div className="relative isolate z-10 flex max-h-full max-w-[var(--media-width)] flex-col items-center">
206 <Button
207 type="button"
208 ref={closeButtonRef}
209 size="icon-xs"
210 variant="media-close"
211 className="absolute right-0 -top-10 z-20"
212 onClick={onClose}
213 aria-label={`Close ${mediaLabel} preview`}
214 >
215 <XIcon />
216 </Button>
217 {item.type === "video" ? (218 {item.type === "video" ? (
218 <ExpandedVideo key={index} item={item} />219 <ExpandedVideo key={index} item={item} />
219 ) : showingAccessibilityDetails ? (220 ) : showingAccessibilityDetails ? (
...@@ -241,7 +242,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({...@@ -241,7 +242,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({
241 onError={() => setFailedImageSrc(item.src)}242 onError={() => setFailedImageSrc(item.src)}
242 />243 />
243 )}244 )}
244 <div className="mt-2 flex max-w-[var(--media-width)] items-center justify-center gap-1.5 text-xs text-white/80">245 <div className="mt-2 flex max-w-[var(--media-width)] items-center justify-center gap-1.5 text-xs text-white/80 max-sm:px-10">
245 <span className="truncate" aria-live="polite" aria-atomic="true">246 <span className="truncate" aria-live="polite" aria-atomic="true">
246 {item.name}247 {item.name}
247 {preview.images.length > 1 ? ` (${index + 1}/${preview.images.length})` : ""}248 {preview.images.length > 1 ? ` (${index + 1}/${preview.images.length})` : ""}
...@@ -280,7 +281,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({...@@ -280,7 +281,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({
280 type="button"281 type="button"
281 size="icon"282 size="icon"
282 variant="media-navigation"283 variant="media-navigation"
283 className="right-0 top-auto -bottom-12 translate-y-0 sm:top-1/2 sm:bottom-auto sm:-translate-y-1/2"284 className="right-0 top-auto bottom-0 translate-y-0 sm:top-1/2 sm:bottom-auto sm:-translate-y-1/2"
284 aria-label="Next media"285 aria-label="Next media"
285 onClick={() => navigateImage(1)}286 onClick={() => navigateImage(1)}
286 >287 >