diff --git a/src/modules/Carousel.tsx b/src/modules/Carousel.tsx index 748ed49..01131268 100644 --- a/src/modules/Carousel.tsx +++ b/src/modules/Carousel.tsx @@ -12,11 +12,12 @@ import { getSlideKey, hasSlides, isImageSlide, + makeInertWhen, parseLengthPercentage, } from "../utils.js"; import { ImageSlide } from "../components/index.js"; import { useController } from "./Controller/index.js"; -import { useLightboxProps, useLightboxState } from "../contexts/index.js"; +import { useDocumentContext, useLightboxProps, useLightboxState } from "../contexts/index.js"; import { CLASS_FLEX_CENTER, CLASS_SLIDE_WRAPPER, MODULE_CAROUSEL } from "../consts.js"; function cssPrefix(value?: string) { @@ -36,7 +37,7 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) { const containerRef = React.useRef(null); const { currentIndex } = useLightboxState(); - const { slideRect, close } = useController(); + const { slideRect, close, focus } = useController(); const { render, carousel: { imageFit, imageProps }, @@ -44,6 +45,15 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) { controller: { closeOnBackdropClick }, styles: { slide: style }, } = useLightboxProps(); + const { getOwnerDocument } = useDocumentContext(); + + const offscreen = offset !== 0; + + React.useEffect(() => { + if (offscreen && containerRef.current?.contains(getOwnerDocument().activeElement)) { + focus(); + } + }, [offscreen, focus, getOwnerDocument]); const renderSlide = () => { let rendered = render.slide?.({ slide, offset, rect: slideRect }); @@ -57,7 +67,7 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) { rect={slideRect} imageFit={imageFit} imageProps={imageProps} - onClick={offset === 0 ? () => onClick?.({ index: currentIndex }) : undefined} + onClick={!offscreen ? () => onClick?.({ index: currentIndex }) : undefined} /> ); } @@ -93,10 +103,10 @@ function CarouselSlide({ slide, offset }: CarouselSlideProps) { ref={containerRef} className={clsx( cssClass(cssSlidePrefix()), - offset === 0 && cssClass(cssSlidePrefix("current")), + !offscreen && cssClass(cssSlidePrefix("current")), cssClass(CLASS_FLEX_CENTER), )} - aria-hidden={offset !== 0} + {...makeInertWhen(offscreen)} onClick={handleBackdropClick} style={style} > diff --git a/src/modules/Portal.tsx b/src/modules/Portal.tsx index 08a3e7f..f0456bd 100644 --- a/src/modules/Portal.tsx +++ b/src/modules/Portal.tsx @@ -84,7 +84,7 @@ export function Portal({ children, animation, styles, className, on, portal, clo for (let i = 0; i < elements.length; i += 1) { const element = elements[i]; if (["TEMPLATE", "SCRIPT", "STYLE"].indexOf(element.tagName) === -1 && element !== node) { - cleanup.current.push(setAttribute(element, "inert", "true")); + cleanup.current.push(setAttribute(element, "inert", "")); cleanup.current.push(setAttribute(element, "aria-hidden", "true")); } } diff --git a/src/utils.ts b/src/utils.ts index a937343..05bdd76 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -157,3 +157,11 @@ export function calculatePreload(carousel: CarouselSettings, slides: Slide[], mi Math.max(carousel.finite ? slides.length - 1 : Math.floor(slides.length / 2), minimum), ); } + +const isReact19 = Number(React.version.split(".")[0]) >= 19; + +// this hack is necessary to support the upcoming breaking change in React 19 - https://github.com/facebook/react/pull/24730 +export function makeInertWhen(condition: boolean) { + const legacyValue = condition ? "" : undefined; + return { inert: isReact19 ? condition : legacyValue }; +}