have back button dismiss lightbox on web (#9874)
This commit is contained in:
@@ -108,6 +108,22 @@ export const atoms = {
|
|||||||
animation: `zoomIn ${EXP_CURVE} 0.3s, fadeIn ${EXP_CURVE} 0.3s`,
|
animation: `zoomIn ${EXP_CURVE} 0.3s, fadeIn ${EXP_CURVE} 0.3s`,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visually hidden but available to screen readers (web).
|
||||||
|
* Use for live regions or off-screen labels (e.g. "Image 1 of 3").
|
||||||
|
*/
|
||||||
|
sr_only: web({
|
||||||
|
position: 'absolute',
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
padding: 0,
|
||||||
|
margin: -1,
|
||||||
|
overflow: 'hidden',
|
||||||
|
clip: 'rect(0,0,0,0)',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
borderWidth: 0,
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Layout.SCROLLBAR_OFFSET}
|
* {@link Layout.SCROLLBAR_OFFSET}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -72,19 +72,7 @@ export function VideoEmbedInnerWeb({
|
|||||||
loop={loop}
|
loop={loop}
|
||||||
/>
|
/>
|
||||||
{embed.alt && (
|
{embed.alt && (
|
||||||
<figcaption
|
<figcaption id={figId} style={a.sr_only}>
|
||||||
id={figId}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
width: 1,
|
|
||||||
height: 1,
|
|
||||||
padding: 0,
|
|
||||||
margin: -1,
|
|
||||||
overflow: 'hidden',
|
|
||||||
clip: 'rect(0, 0, 0, 0)',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
borderWidth: 0,
|
|
||||||
}}>
|
|
||||||
{embed.alt}
|
{embed.alt}
|
||||||
</figcaption>
|
</figcaption>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {useCallback, useEffect, useState} from 'react'
|
import {useCallback, useEffect, useRef, useState} from 'react'
|
||||||
import {Pressable, StyleSheet, View} from 'react-native'
|
import {Pressable, StyleSheet, View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
@@ -69,7 +69,13 @@ function LightboxContainer({
|
|||||||
<Backdrop />
|
<Backdrop />
|
||||||
<RemoveScrollBar />
|
<RemoveScrollBar />
|
||||||
<FocusScope.FocusScope loop trapped asChild>
|
<FocusScope.FocusScope loop trapped asChild>
|
||||||
<div style={{position: 'absolute', inset: 0}}>{children}</div>
|
<div
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label={_(msg`Image viewer`)}
|
||||||
|
style={{position: 'absolute', inset: 0}}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
</FocusScope.FocusScope>
|
</FocusScope.FocusScope>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)
|
)
|
||||||
@@ -125,6 +131,32 @@ function LightboxGallery({
|
|||||||
return () => window.removeEventListener('keydown', onKeyDown)
|
return () => window.removeEventListener('keydown', onKeyDown)
|
||||||
}, [onKeyDown])
|
}, [onKeyDown])
|
||||||
|
|
||||||
|
// Push a history entry so the browser back button closes the lightbox
|
||||||
|
// instead of navigating away from the page.
|
||||||
|
const closedByPopStateRef = useRef(false)
|
||||||
|
useEffect(() => {
|
||||||
|
history.pushState({lightbox: true}, '')
|
||||||
|
|
||||||
|
const handlePopState = () => {
|
||||||
|
closedByPopStateRef.current = true
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
window.addEventListener('popstate', handlePopState)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('popstate', handlePopState)
|
||||||
|
// Only pop our entry if it's still the current one. If navigation
|
||||||
|
// already pushed a new entry on top, leave the orphaned entry —
|
||||||
|
// it shares the same URL so traversing through it is harmless.
|
||||||
|
if (
|
||||||
|
!closedByPopStateRef.current &&
|
||||||
|
(history.state as {lightbox?: boolean})?.lightbox
|
||||||
|
) {
|
||||||
|
history.back()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [onClose])
|
||||||
|
|
||||||
const delayedFadeInAnim = !reduceMotionEnabled && [
|
const delayedFadeInAnim = !reduceMotionEnabled && [
|
||||||
a.fade_in,
|
a.fade_in,
|
||||||
{animationDelay: '0.2s', animationFillMode: 'both'},
|
{animationDelay: '0.2s', animationFillMode: 'both'},
|
||||||
@@ -205,6 +237,11 @@ function LightboxGallery({
|
|||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
{imgs.length > 1 && (
|
||||||
|
<div aria-live="polite" aria-atomic="true" style={a.sr_only}>
|
||||||
|
<Text>{_(msg`Image ${index + 1} of ${imgs.length}`)}</Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
onPress={onClose}
|
onPress={onClose}
|
||||||
style={[
|
style={[
|
||||||
|
|||||||
Reference in New Issue
Block a user