yet-another-react-lightbox 3.23.4 → 3.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -12,7 +12,10 @@ declare function cssClass(name: string): string;
12
12
  declare function cssVar(name: string): string;
13
13
  declare function composePrefix(base: string, prefix?: string): string;
14
14
  declare function makeComposePrefix(base: string): (prefix?: string) => string;
15
+ declare function translateLabel(labels: Labels | undefined, defaultLabel: Label): string;
16
+ /** @deprecated - use `translateLabel` instead */
15
17
  declare function label(labels: Labels | undefined, defaultLabel: Label): string;
18
+ declare function translateSlideCounter(labels: Labels | undefined, slides: Slide[], index: number): string;
16
19
  declare function cleanup(...cleaners: (() => void)[]): () => void;
17
20
  declare function makeUseContext<T>(name: string, contextName: string, context: React.Context<T | null>): () => NonNullable<T>;
18
21
  declare function hasWindow(): boolean;
@@ -111,6 +114,19 @@ declare function useSensors<T extends Element>(): UseSensors<T>;
111
114
 
112
115
  declare function useThrottle(callback: (...args: unknown[]) => void, delay: number): (...args: unknown[]) => void;
113
116
 
117
+ type A11yContextType = {
118
+ focusWithin: boolean;
119
+ trackFocusWithin: (onFocus?: React.FocusEventHandler, onBlur?: React.FocusEventHandler) => {
120
+ onFocus: React.FocusEventHandler;
121
+ onBlur: React.FocusEventHandler;
122
+ };
123
+ autoPlaying: boolean;
124
+ setAutoPlaying: (value: boolean) => void;
125
+ };
126
+ declare const A11yContext: React.Context<A11yContextType | null>;
127
+ declare const useA11yContext: () => A11yContextType;
128
+ declare function A11yContextProvider({ children }: React.PropsWithChildren): React.JSX.Element;
129
+
114
130
  type DocumentContextType = {
115
131
  getOwnerDocument: (node?: Node | null) => Document;
116
132
  getOwnerWindow: (node?: Node | null) => Window;
@@ -215,7 +231,7 @@ declare function ImageSlide({ slide: image, offset, render, rect, imageFit, imag
215
231
 
216
232
  declare const LightboxRoot: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
217
233
 
218
- declare function Carousel({ carousel }: ComponentProps): React.JSX.Element;
234
+ declare function Carousel({ carousel, labels }: ComponentProps): React.JSX.Element;
219
235
  declare const CarouselModule: Module;
220
236
 
221
237
  declare enum SwipeState {
@@ -275,7 +291,7 @@ declare function useNavigationState(): {
275
291
  declare function NoScroll({ noScroll: { disabled }, children }: ComponentProps): React.JSX.Element;
276
292
  declare const NoScrollModule: Module;
277
293
 
278
- declare function Portal({ children, animation, styles, className, on, portal, close }: ComponentProps): React.ReactPortal | null;
294
+ declare function Portal({ children, animation, styles, className, on, portal, close, labels }: ComponentProps): React.ReactPortal | null;
279
295
  declare const PortalModule: Module;
280
296
 
281
297
  declare function Root({ children }: ComponentProps): React.JSX.Element;
@@ -284,5 +300,5 @@ declare const RootModule: Module;
284
300
  declare function Toolbar({ toolbar: { buttons }, render: { buttonClose, iconClose }, styles }: ComponentProps): React.JSX.Element;
285
301
  declare const ToolbarModule: Module;
286
302
 
287
- export { Augmentation, Callback, Carousel, CarouselModule, CarouselSettings, CloseIcon, Component, ComponentProps, ContainerRect, Controller, ControllerContext, ControllerModule, ControllerRef, ControllerSettings, DocumentContext, DocumentContextProvider, ErrorIcon, EventTypes, EventsContext, EventsProvider, IconButton, ImageSlide, Label, Labels, LengthOrPercentage, Lightbox, LightboxDefaultProps, LightboxDispatchContext, LightboxExternalProps, LightboxProps, LightboxPropsContext, LightboxPropsProvider, LightboxRoot, LightboxState, LightboxStateContext, LightboxStateProvider, LightboxStateSwipeAction, LightboxStateUpdateAction, LoadingIcon, Module, Navigation, NavigationButton, NavigationModule, NextIcon, NoScroll, NoScrollModule, Node$1 as Node, Plugin, Portal, PortalModule, PreviousIcon, Render, RenderFunction, Root, RootModule, Slide, SlideImage, SwipeState, TimeoutsContext, TimeoutsProvider, Toolbar, ToolbarModule, ToolbarSettings, addToolbarButton, calculatePreload, cleanup, clsx, composePrefix, computeSlideRect, createIcon, createIconDisabled, createModule, createNode, cssClass, cssVar, Lightbox as default, devicePixelRatio, getSlide, getSlideIfPresent, getSlideIndex, getSlideKey, hasSlides, hasWindow, isImageFitCover, isImageSlide, label, makeComposePrefix, makeInertWhen, makeUseContext, parseInt, parseLengthPercentage, reflow, round, setRef, stopNavigationEventsPropagation, useAnimation, useContainerRect, useController, useDelay, useDocumentContext, useEventCallback, useEvents, useForkRef, useKeyboardNavigation, useLayoutEffect, useLightboxDispatch, useLightboxProps, useLightboxState, useLoseFocus, useMotionPreference, useNavigationState, usePointerSwipe, usePreventWheelDefaults, useRTL, useSensors, useThrottle, useTimeouts, useWheelSwipe, withPlugins };
288
- export type { ComputeAnimation, ControllerContextType, DocumentContextProviderProps, DocumentContextType, Event, EventCallback, EventsContextType, IconButtonProps, ImageSlideProps, KeyboardEventType, LightboxDispatchContextType, LightboxPropsContextType, LightboxStateAction, LightboxStateContextType, LightboxStateProviderProps, NavigationButtonProps, PointerEventType, Publish, ReactEventType, RegisterSensors, SensorCallback, Subscribe, SubscribeSensors, SupportedEventType, TimeoutsContextType, Topic, Unsubscribe, UseSensors, WheelEventType };
303
+ export { A11yContext, A11yContextProvider, Augmentation, Callback, Carousel, CarouselModule, CarouselSettings, CloseIcon, Component, ComponentProps, ContainerRect, Controller, ControllerContext, ControllerModule, ControllerRef, ControllerSettings, DocumentContext, DocumentContextProvider, ErrorIcon, EventTypes, EventsContext, EventsProvider, IconButton, ImageSlide, Label, Labels, LengthOrPercentage, Lightbox, LightboxDefaultProps, LightboxDispatchContext, LightboxExternalProps, LightboxProps, LightboxPropsContext, LightboxPropsProvider, LightboxRoot, LightboxState, LightboxStateContext, LightboxStateProvider, LightboxStateSwipeAction, LightboxStateUpdateAction, LoadingIcon, Module, Navigation, NavigationButton, NavigationModule, NextIcon, NoScroll, NoScrollModule, Node$1 as Node, Plugin, Portal, PortalModule, PreviousIcon, Render, RenderFunction, Root, RootModule, Slide, SlideImage, SwipeState, TimeoutsContext, TimeoutsProvider, Toolbar, ToolbarModule, ToolbarSettings, addToolbarButton, calculatePreload, cleanup, clsx, composePrefix, computeSlideRect, createIcon, createIconDisabled, createModule, createNode, cssClass, cssVar, Lightbox as default, devicePixelRatio, getSlide, getSlideIfPresent, getSlideIndex, getSlideKey, hasSlides, hasWindow, isImageFitCover, isImageSlide, label, makeComposePrefix, makeInertWhen, makeUseContext, parseInt, parseLengthPercentage, reflow, round, setRef, stopNavigationEventsPropagation, translateLabel, translateSlideCounter, useA11yContext, useAnimation, useContainerRect, useController, useDelay, useDocumentContext, useEventCallback, useEvents, useForkRef, useKeyboardNavigation, useLayoutEffect, useLightboxDispatch, useLightboxProps, useLightboxState, useLoseFocus, useMotionPreference, useNavigationState, usePointerSwipe, usePreventWheelDefaults, useRTL, useSensors, useThrottle, useTimeouts, useWheelSwipe, withPlugins };
304
+ export type { A11yContextType, ComputeAnimation, ControllerContextType, DocumentContextProviderProps, DocumentContextType, Event, EventCallback, EventsContextType, IconButtonProps, ImageSlideProps, KeyboardEventType, LightboxDispatchContextType, LightboxPropsContextType, LightboxStateAction, LightboxStateContextType, LightboxStateProviderProps, NavigationButtonProps, PointerEventType, Publish, ReactEventType, RegisterSensors, SensorCallback, Subscribe, SubscribeSensors, SupportedEventType, TimeoutsContextType, Topic, Unsubscribe, UseSensors, WheelEventType };
package/dist/index.js CHANGED
@@ -20,10 +20,18 @@ function composePrefix(base, prefix) {
20
20
  function makeComposePrefix(base) {
21
21
  return (prefix) => composePrefix(base, prefix);
22
22
  }
23
- function label(labels, defaultLabel) {
23
+ function translateLabel(labels, defaultLabel) {
24
24
  var _a;
25
25
  return (_a = labels === null || labels === void 0 ? void 0 : labels[defaultLabel]) !== null && _a !== void 0 ? _a : defaultLabel;
26
26
  }
27
+ function label(labels, defaultLabel) {
28
+ return translateLabel(labels, defaultLabel);
29
+ }
30
+ function translateSlideCounter(labels, slides, index) {
31
+ return translateLabel(labels, "{index} of {total}")
32
+ .replace(/\{index}/g, `${getSlideIndex(index, slides.length) + 1}`)
33
+ .replace(/\{total}/g, `${slides.length}`);
34
+ }
27
35
  function cleanup(...cleaners) {
28
36
  return () => {
29
37
  cleaners.forEach((cleaner) => {
@@ -263,6 +271,30 @@ function withPlugins(root, plugins = [], augmentations = []) {
263
271
  };
264
272
  }
265
273
 
274
+ const A11yContext = React.createContext(null);
275
+ const useA11yContext = makeUseContext("useA11yContext", "A11yContext", A11yContext);
276
+ function A11yContextProvider({ children }) {
277
+ const [focusWithin, setFocusWithin] = React.useState(false);
278
+ const [autoPlaying, setAutoPlaying] = React.useState(false);
279
+ const context = React.useMemo(() => {
280
+ const trackFocusWithin = (onFocus, onBlur) => {
281
+ const trackAndDelegate = (focusWithinValue) => (event) => {
282
+ var _a;
283
+ if (!event.currentTarget.contains(event.relatedTarget)) {
284
+ setFocusWithin(focusWithinValue);
285
+ }
286
+ (_a = (focusWithinValue ? onFocus : onBlur)) === null || _a === void 0 ? void 0 : _a(event);
287
+ };
288
+ return {
289
+ onFocus: trackAndDelegate(true),
290
+ onBlur: trackAndDelegate(false),
291
+ };
292
+ };
293
+ return { focusWithin, trackFocusWithin, autoPlaying, setAutoPlaying };
294
+ }, [focusWithin, autoPlaying]);
295
+ return React.createElement(A11yContext.Provider, { value: context }, children);
296
+ }
297
+
266
298
  const DocumentContext = React.createContext(null);
267
299
  const useDocumentContext = makeUseContext("useDocument", "DocumentContext", DocumentContext);
268
300
  function DocumentContextProvider({ nodeRef, children }) {
@@ -389,9 +421,9 @@ function TimeoutsProvider({ children }) {
389
421
  return React.createElement(TimeoutsContext.Provider, { value: context }, children);
390
422
  }
391
423
 
392
- const IconButton = React.forwardRef(function IconButton({ label: label$1, className, icon: Icon, renderIcon, onClick, style, ...rest }, ref) {
424
+ const IconButton = React.forwardRef(function IconButton({ label, className, icon: Icon, renderIcon, onClick, style, ...rest }, ref) {
393
425
  const { styles, labels } = useLightboxProps();
394
- const buttonLabel = label(labels, label$1);
426
+ const buttonLabel = translateLabel(labels, label);
395
427
  return (React.createElement("button", { ref: ref, type: "button", title: buttonLabel, "aria-label": buttonLabel, className: clsx(cssClass(ELEMENT_BUTTON), className), onClick: onClick, style: { ...style, ...styles.button }, ...rest }, renderIcon ? renderIcon() : React.createElement(Icon, { className: cssClass(ELEMENT_ICON), style: styles.icon })));
396
428
  });
397
429
 
@@ -634,7 +666,7 @@ function useThrottle(callback, delay) {
634
666
  const slidePrefix = makeComposePrefix("slide");
635
667
  const slideImagePrefix = makeComposePrefix("slide_image");
636
668
  function ImageSlide({ slide: image, offset, render, rect, imageFit, imageProps, onClick, onLoad, onError, style, }) {
637
- var _a, _b, _c, _d, _e, _f, _g;
669
+ var _a, _b, _c, _d, _e, _f, _g, _h;
638
670
  const [status, setStatus] = React.useState(SLIDE_STATUS_LOADING);
639
671
  const { publish } = useEvents();
640
672
  const { setTimeout } = useTimeouts();
@@ -688,7 +720,7 @@ function ImageSlide({ slide: image, offset, render, rect, imageFit, imageProps,
688
720
  const sizes = srcSet && rect && hasWindow() ? `${Math.round(Math.min(estimateActualWidth(), rect.width))}px` : undefined;
689
721
  const { style: imagePropsStyle, className: imagePropsClassName, ...restImageProps } = imageProps || {};
690
722
  return (React.createElement(React.Fragment, null,
691
- React.createElement("img", { ref: setImageRef, onLoad: handleOnLoad, onError: handleOnError, onClick: onClick, draggable: false, className: clsx(cssClass(slideImagePrefix()), cover && cssClass(slideImagePrefix("cover")), status !== SLIDE_STATUS_COMPLETE && cssClass(slideImagePrefix("loading")), imagePropsClassName), style: { ...defaultStyle, ...style, ...imagePropsStyle }, ...restImageProps, alt: image.alt, sizes: sizes, srcSet: srcSet, src: image.src }),
723
+ React.createElement("img", { ref: setImageRef, onLoad: handleOnLoad, onError: handleOnError, onClick: onClick, draggable: false, className: clsx(cssClass(slideImagePrefix()), cover && cssClass(slideImagePrefix("cover")), status !== SLIDE_STATUS_COMPLETE && cssClass(slideImagePrefix("loading")), imagePropsClassName), style: { ...defaultStyle, ...style, ...imagePropsStyle }, ...restImageProps, alt: (_h = image.alt) !== null && _h !== void 0 ? _h : "", sizes: sizes, srcSet: srcSet, src: image.src }),
692
724
  status !== SLIDE_STATUS_COMPLETE && (React.createElement("div", { className: cssClass(slidePrefix(SLIDE_STATUS_PLACEHOLDER)) },
693
725
  status === SLIDE_STATUS_LOADING &&
694
726
  ((render === null || render === void 0 ? void 0 : render.iconLoading) ? (render.iconLoading()) : (React.createElement(LoadingIcon, { className: clsx(cssClass(ELEMENT_ICON), cssClass(slidePrefix(SLIDE_STATUS_LOADING))) }))),
@@ -696,10 +728,11 @@ function ImageSlide({ slide: image, offset, render, rect, imageFit, imageProps,
696
728
  ((render === null || render === void 0 ? void 0 : render.iconError) ? (render.iconError()) : (React.createElement(ErrorIcon, { className: clsx(cssClass(ELEMENT_ICON), cssClass(slidePrefix(SLIDE_STATUS_ERROR))) })))))));
697
729
  }
698
730
 
699
- const LightboxRoot = React.forwardRef(function LightboxRoot({ className, children, ...rest }, ref) {
731
+ const LightboxRoot = React.forwardRef(function LightboxRoot({ className, children, onFocus, onBlur, ...rest }, ref) {
700
732
  const nodeRef = React.useRef(null);
733
+ const { trackFocusWithin } = useA11yContext();
701
734
  return (React.createElement(DocumentContextProvider, { nodeRef: nodeRef },
702
- React.createElement("div", { ref: useForkRef(ref, nodeRef), className: clsx(cssClass("root"), className), ...rest }, children)));
735
+ React.createElement("div", { ref: useForkRef(ref, nodeRef), className: clsx(cssClass("root"), className), ...trackFocusWithin(onFocus, onBlur), ...rest }, children)));
703
736
  });
704
737
 
705
738
  var SwipeState;
@@ -1224,7 +1257,7 @@ function Controller({ children, ...props }) {
1224
1257
  : null),
1225
1258
  ...(controller.touchAction !== "none" ? { [cssVar("controller_touch_action")]: controller.touchAction } : null),
1226
1259
  ...styles.container,
1227
- }, ...(controller.aria ? { role: "region", "aria-live": "polite", "aria-roledescription": "carousel" } : null), tabIndex: -1, ...registerSensors }, containerRect && (React.createElement(ControllerContext.Provider, { value: context },
1260
+ }, tabIndex: -1, ...registerSensors }, containerRect && (React.createElement(ControllerContext.Provider, { value: context },
1228
1261
  children, (_a = render.controls) === null || _a === void 0 ? void 0 :
1229
1262
  _a.call(render)))));
1230
1263
  }
@@ -1238,9 +1271,9 @@ function cssSlidePrefix(value) {
1238
1271
  }
1239
1272
  function CarouselSlide({ slide, offset }) {
1240
1273
  const containerRef = React.useRef(null);
1241
- const { currentIndex } = useLightboxState();
1274
+ const { currentIndex, slides } = useLightboxState();
1242
1275
  const { slideRect, focus } = useController();
1243
- const { render, carousel: { imageFit, imageProps }, on: { click: onClick }, styles: { slide: style }, } = useLightboxProps();
1276
+ const { render, carousel: { imageFit, imageProps }, on: { click: onClick }, styles: { slide: style }, labels, } = useLightboxProps();
1244
1277
  const { getOwnerDocument } = useDocumentContext();
1245
1278
  const offscreen = offset !== 0;
1246
1279
  React.useEffect(() => {
@@ -1260,15 +1293,16 @@ function CarouselSlide({ slide, offset }) {
1260
1293
  ((_c = render.slideContainer) !== null && _c !== void 0 ? _c : (({ children }) => children))({ slide, children: rendered }), (_d = render.slideFooter) === null || _d === void 0 ? void 0 :
1261
1294
  _d.call(render, { slide }))) : null;
1262
1295
  };
1263
- return (React.createElement("div", { ref: containerRef, className: clsx(cssClass(cssSlidePrefix()), !offscreen && cssClass(cssSlidePrefix("current")), cssClass(CLASS_FLEX_CENTER)), ...makeInertWhen(offscreen), style: style, role: "region", "aria-roledescription": "slide" }, renderSlide()));
1296
+ return (React.createElement("div", { ref: containerRef, className: clsx(cssClass(cssSlidePrefix()), !offscreen && cssClass(cssSlidePrefix("current")), cssClass(CLASS_FLEX_CENTER)), ...makeInertWhen(offscreen), style: style, role: "group", "aria-roledescription": translateLabel(labels, "Slide"), "aria-label": translateSlideCounter(labels, slides, currentIndex + offset) }, renderSlide()));
1264
1297
  }
1265
1298
  function Placeholder() {
1266
1299
  const style = useLightboxProps().styles.slide;
1267
1300
  return React.createElement("div", { className: cssClass(CLASS_SLIDE), style: style });
1268
1301
  }
1269
- function Carousel({ carousel }) {
1302
+ function Carousel({ carousel, labels }) {
1270
1303
  const { slides, currentIndex, globalIndex } = useLightboxState();
1271
1304
  const { setCarouselRef } = useController();
1305
+ const { autoPlaying, focusWithin } = useA11yContext();
1272
1306
  const spacingValue = parseLengthPercentage(carousel.spacing);
1273
1307
  const paddingValue = parseLengthPercentage(carousel.padding);
1274
1308
  const preload = calculatePreload(carousel, slides, 1);
@@ -1293,7 +1327,7 @@ function Carousel({ carousel }) {
1293
1327
  [`${cssVar(cssPrefix$2("spacing_percent"))}`]: spacingValue.percent || 0,
1294
1328
  [`${cssVar(cssPrefix$2("padding_px"))}`]: paddingValue.pixel || 0,
1295
1329
  [`${cssVar(cssPrefix$2("padding_percent"))}`]: paddingValue.percent || 0,
1296
- } }, items.map(({ key, slide, offset }) => slide ? React.createElement(CarouselSlide, { key: key, slide: slide, offset: offset }) : React.createElement(Placeholder, { key: key }))));
1330
+ }, role: "region", "aria-live": autoPlaying && !focusWithin ? "off" : "polite", "aria-roledescription": translateLabel(labels, "Carousel"), "aria-label": translateLabel(labels, "Photo gallery") }, items.map(({ key, slide, offset }) => slide ? React.createElement(CarouselSlide, { key: key, slide: slide, offset: offset }) : React.createElement(Placeholder, { key: key }))));
1297
1331
  }
1298
1332
  const CarouselModule = createModule(MODULE_CAROUSEL, Carousel);
1299
1333
 
@@ -1413,7 +1447,7 @@ function setAttribute(element, attribute, value) {
1413
1447
  }
1414
1448
  };
1415
1449
  }
1416
- function Portal({ children, animation, styles, className, on, portal, close }) {
1450
+ function Portal({ children, animation, styles, className, on, portal, close, labels }) {
1417
1451
  const [mounted, setMounted] = React.useState(false);
1418
1452
  const [visible, setVisible] = React.useState(false);
1419
1453
  const cleanup = React.useRef([]);
@@ -1476,7 +1510,7 @@ function Portal({ children, animation, styles, className, on, portal, close }) {
1476
1510
  }
1477
1511
  }, [handleEnter, handleCleanup]);
1478
1512
  return mounted
1479
- ? createPortal(React.createElement(LightboxRoot, { ref: handleRef, className: clsx(className, cssClass(cssPrefix$1()), cssClass(CLASS_NO_SCROLL_PADDING), visible && cssClass(cssPrefix$1("open"))), "aria-modal": true, role: "dialog", "aria-live": "polite", "aria-roledescription": "lightbox", style: {
1513
+ ? createPortal(React.createElement(LightboxRoot, { ref: handleRef, className: clsx(className, cssClass(cssPrefix$1()), cssClass(CLASS_NO_SCROLL_PADDING), visible && cssClass(cssPrefix$1("open"))), "aria-modal": true, role: "dialog", "aria-label": translateLabel(labels, "Lightbox"), style: {
1480
1514
  ...(animation.fade !== LightboxDefaultProps.animation.fade
1481
1515
  ? { [cssVar("fade_animation_duration")]: `${animationDuration}ms` }
1482
1516
  : null),
@@ -1558,7 +1592,8 @@ function Lightbox({ carousel, animation, render, toolbar, controller, noScroll,
1558
1592
  return (React.createElement(LightboxPropsProvider, { ...props },
1559
1593
  React.createElement(LightboxStateProvider, { slides: slides || defaultSlides, index: parseInt(index || defaultIndex) },
1560
1594
  React.createElement(TimeoutsProvider, null,
1561
- React.createElement(EventsProvider, null, renderNode(createNode(RootModule, config), props))))));
1595
+ React.createElement(EventsProvider, null,
1596
+ React.createElement(A11yContextProvider, null, renderNode(createNode(RootModule, config), props)))))));
1562
1597
  }
1563
1598
 
1564
- export { ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, CLASS_FLEX_CENTER, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, CLASS_SLIDE, CLASS_SLIDE_WRAPPER, Carousel, CarouselModule, CloseIcon, Controller, ControllerContext, ControllerModule, DocumentContext, DocumentContextProvider, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, ErrorIcon, EventsContext, EventsProvider, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, IconButton, ImageSlide, Lightbox, LightboxDefaultProps, LightboxDispatchContext, LightboxPropsContext, LightboxPropsProvider, LightboxRoot, LightboxStateContext, LightboxStateProvider, LoadingIcon, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, Navigation, NavigationButton, NavigationModule, NextIcon, NoScroll, NoScrollModule, Portal, PortalModule, PreviousIcon, Root, RootModule, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SwipeState, TimeoutsContext, TimeoutsProvider, Toolbar, ToolbarModule, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, activeSlideStatus, addToolbarButton, calculatePreload, cleanup, clsx, composePrefix, computeSlideRect, createIcon, createIconDisabled, createModule, createNode, cssClass, cssVar, Lightbox as default, devicePixelRatio, getSlide, getSlideIfPresent, getSlideIndex, getSlideKey, hasSlides, hasWindow, isImageFitCover, isImageSlide, label, makeComposePrefix, makeInertWhen, makeUseContext, parseInt, parseLengthPercentage, reflow, round, setRef, stopNavigationEventsPropagation, useAnimation, useContainerRect, useController, useDelay, useDocumentContext, useEventCallback, useEvents, useForkRef, useKeyboardNavigation, useLayoutEffect, useLightboxDispatch, useLightboxProps, useLightboxState, useLoseFocus, useMotionPreference, useNavigationState, usePointerEvents, usePointerSwipe, usePreventWheelDefaults, useRTL, useSensors, useThrottle, useTimeouts, useWheelSwipe, withPlugins };
1599
+ export { A11yContext, A11yContextProvider, ACTION_CLOSE, ACTION_NEXT, ACTION_PREV, ACTION_SWIPE, CLASS_FLEX_CENTER, CLASS_NO_SCROLL, CLASS_NO_SCROLL_PADDING, CLASS_SLIDE, CLASS_SLIDE_WRAPPER, Carousel, CarouselModule, CloseIcon, Controller, ControllerContext, ControllerModule, DocumentContext, DocumentContextProvider, ELEMENT_BUTTON, ELEMENT_ICON, EVENT_ON_KEY_DOWN, EVENT_ON_KEY_UP, EVENT_ON_POINTER_CANCEL, EVENT_ON_POINTER_DOWN, EVENT_ON_POINTER_LEAVE, EVENT_ON_POINTER_MOVE, EVENT_ON_POINTER_UP, EVENT_ON_WHEEL, ErrorIcon, EventsContext, EventsProvider, IMAGE_FIT_CONTAIN, IMAGE_FIT_COVER, IconButton, ImageSlide, Lightbox, LightboxDefaultProps, LightboxDispatchContext, LightboxPropsContext, LightboxPropsProvider, LightboxRoot, LightboxStateContext, LightboxStateProvider, LoadingIcon, MODULE_CAROUSEL, MODULE_CONTROLLER, MODULE_NAVIGATION, MODULE_NO_SCROLL, MODULE_PORTAL, MODULE_ROOT, MODULE_TOOLBAR, Navigation, NavigationButton, NavigationModule, NextIcon, NoScroll, NoScrollModule, Portal, PortalModule, PreviousIcon, Root, RootModule, SLIDE_STATUS_COMPLETE, SLIDE_STATUS_ERROR, SLIDE_STATUS_LOADING, SLIDE_STATUS_PLACEHOLDER, SwipeState, TimeoutsContext, TimeoutsProvider, Toolbar, ToolbarModule, UNKNOWN_ACTION_TYPE, VK_ARROW_LEFT, VK_ARROW_RIGHT, VK_ESCAPE, activeSlideStatus, addToolbarButton, calculatePreload, cleanup, clsx, composePrefix, computeSlideRect, createIcon, createIconDisabled, createModule, createNode, cssClass, cssVar, Lightbox as default, devicePixelRatio, getSlide, getSlideIfPresent, getSlideIndex, getSlideKey, hasSlides, hasWindow, isImageFitCover, isImageSlide, label, makeComposePrefix, makeInertWhen, makeUseContext, parseInt, parseLengthPercentage, reflow, round, setRef, stopNavigationEventsPropagation, translateLabel, translateSlideCounter, useA11yContext, useAnimation, useContainerRect, useController, useDelay, useDocumentContext, useEventCallback, useEvents, useForkRef, useKeyboardNavigation, useLayoutEffect, useLightboxDispatch, useLightboxProps, useLightboxState, useLoseFocus, useMotionPreference, useNavigationState, usePointerEvents, usePointerSwipe, usePreventWheelDefaults, useRTL, useSensors, useThrottle, useTimeouts, useWheelSwipe, withPlugins };
@@ -48,7 +48,11 @@ declare module "yet-another-react-lightbox" {
48
48
  buttonCaptions?: RenderFunction<CaptionsRef>;
49
49
  }
50
50
  interface Labels {
51
+ /** Slide description ARIA role description */
52
+ Caption?: string;
53
+ /** `Show captions` button title */
51
54
  "Show captions"?: string;
55
+ /** `Hide captions` button title */
52
56
  "Hide captions"?: string;
53
57
  }
54
58
  /** Captions plugin ref */
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { cssClass, useLightboxProps, makeUseContext, useController, clsx, cssVar, createIcon, createIconDisabled, IconButton, addToolbarButton, createModule } from '../../index.js';
2
+ import { cssClass, useLightboxProps, makeUseContext, useController, clsx, cssVar, translateLabel, createIcon, createIconDisabled, IconButton, addToolbarButton, createModule } from '../../index.js';
3
3
  import { PLUGIN_CAPTIONS } from '../../types.js';
4
4
 
5
5
  const cssPrefix = (className) => cssClass(`slide_${className}`);
@@ -39,7 +39,7 @@ function Title({ title }) {
39
39
  const { visible } = useCaptions();
40
40
  if (!visible)
41
41
  return null;
42
- return (React.createElement("div", { style: styles.captionsTitleContainer, className: clsx(cssPrefix("captions_container"), cssPrefix("title_container")) },
42
+ return (React.createElement("div", { role: "heading", "aria-level": 2, style: styles.captionsTitleContainer, className: clsx(cssPrefix("captions_container"), cssPrefix("title_container")) },
43
43
  React.createElement("div", { className: cssPrefix("title"), style: {
44
44
  ...(toolbarWidth ? { [cssVar("toolbar_width")]: `${toolbarWidth}px` } : null),
45
45
  ...styles.captionsTitle,
@@ -48,7 +48,7 @@ function Title({ title }) {
48
48
 
49
49
  function Description({ description }) {
50
50
  const { descriptionTextAlign, descriptionMaxLines } = useCaptionsProps();
51
- const { styles } = useLightboxProps();
51
+ const { styles, labels } = useLightboxProps();
52
52
  const { visible } = useCaptions();
53
53
  if (!visible)
54
54
  return null;
@@ -62,7 +62,7 @@ function Description({ description }) {
62
62
  }
63
63
  : null),
64
64
  ...styles.captionsDescription,
65
- } }, typeof description === "string"
65
+ }, role: "paragraph", "aria-roledescription": translateLabel(labels, "Caption") }, typeof description === "string"
66
66
  ? description.split("\n").flatMap((line, index) => [...(index > 0 ? [React.createElement("br", { key: index })] : []), line])
67
67
  : description)));
68
68
  }
@@ -16,7 +16,7 @@ function CounterComponent({ counter }) {
16
16
  const { separator, container: { className, ...rest }, className: legacyClassName, ...legacyRest } = resolveCounterProps(counter);
17
17
  if (slides.length === 0)
18
18
  return null;
19
- return (React.createElement("div", { className: clsx(cssClass("counter"), className || legacyClassName), ...legacyRest, ...rest },
19
+ return (React.createElement("div", { className: clsx(cssClass("counter"), className || legacyClassName), ...legacyRest, ...rest, "aria-hidden": true },
20
20
  currentIndex + 1,
21
21
  " ",
22
22
  separator,
@@ -30,6 +30,7 @@ declare module "yet-another-react-lightbox" {
30
30
  iconDownload?: RenderFunction;
31
31
  }
32
32
  interface Labels {
33
+ /** `Download` button title */
33
34
  Download?: string;
34
35
  }
35
36
  interface Callbacks {
@@ -23,7 +23,9 @@ declare module "yet-another-react-lightbox" {
23
23
  iconExitFullscreen?: RenderFunction;
24
24
  }
25
25
  interface Labels {
26
+ /** `Enter Fullscreen` button title */
26
27
  "Enter Fullscreen"?: string;
28
+ /** `Exit Fullscreen` button title */
27
29
  "Exit Fullscreen"?: string;
28
30
  }
29
31
  interface Callbacks {
@@ -30,6 +30,7 @@ declare module "yet-another-react-lightbox" {
30
30
  iconShare?: RenderFunction;
31
31
  }
32
32
  interface Labels {
33
+ /** `Share` button title */
33
34
  Share?: string;
34
35
  }
35
36
  interface Callbacks {
@@ -25,7 +25,9 @@ declare module "yet-another-react-lightbox" {
25
25
  buttonSlideshow?: RenderFunction<SlideshowRef>;
26
26
  }
27
27
  interface Labels {
28
+ /** `Play` button title */
28
29
  Play?: string;
30
+ /** `Pause` button title */
29
31
  Pause?: string;
30
32
  }
31
33
  interface Callbacks {
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { makeUseContext, useLightboxState, useTimeouts, useEvents, useController, useEventCallback, cleanup, createIcon, useLightboxProps, useLoseFocus, IconButton, addToolbarButton, createModule } from '../../index.js';
2
+ import { makeUseContext, useLightboxState, useTimeouts, useEvents, useController, useA11yContext, useEventCallback, cleanup, createIcon, useLightboxProps, useLoseFocus, IconButton, addToolbarButton, createModule } from '../../index.js';
3
3
  import { SLIDE_STATUS_LOADING, SLIDE_STATUS_PLAYING, ACTIVE_SLIDE_LOADING, ACTIVE_SLIDE_PLAYING, ACTIVE_SLIDE_ERROR, SLIDE_STATUS_ERROR, ACTIVE_SLIDE_COMPLETE, SLIDE_STATUS_COMPLETE, PLUGIN_SLIDESHOW } from '../../types.js';
4
4
 
5
5
  const defaultSlideshowProps = {
@@ -24,6 +24,8 @@ function SlideshowContextProvider({ slideshow, carousel: { finite }, on, childre
24
24
  const { setTimeout, clearTimeout } = useTimeouts();
25
25
  const { subscribe } = useEvents();
26
26
  const { next } = useController();
27
+ const { setAutoPlaying } = useA11yContext();
28
+ React.useEffect(() => setAutoPlaying(playing), [playing, setAutoPlaying]);
27
29
  const disabled = slides.length === 0 || (finite && currentIndex === slides.length - 1);
28
30
  const play = React.useCallback(() => {
29
31
  if (!playing && !disabled) {
@@ -53,7 +53,11 @@ declare module "yet-another-react-lightbox" {
53
53
  buttonThumbnails?: RenderFunction<ThumbnailsRef>;
54
54
  }
55
55
  interface Labels {
56
+ /** Thumbnails ARIA label */
57
+ Thumbnails?: string;
58
+ /** `Show thumbnails` button title */
56
59
  "Show thumbnails"?: string;
60
+ /** `Hide thumbnails` button title */
57
61
  "Hide thumbnails"?: string;
58
62
  }
59
63
  /** `render.thumbnail` render function props */
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useLightboxProps, composePrefix, createIcon, ImageSlide, isImageSlide, cssClass, makeComposePrefix, useDocumentContext, useEventCallback, cssVar, clsx, getSlideKey, useRTL, useEvents, useLightboxState, useSensors, useKeyboardNavigation, useAnimation, cleanup, calculatePreload, hasSlides, getSlide, makeUseContext, LightboxPropsProvider, createIconDisabled, IconButton, addToolbarButton, createModule } from '../../index.js';
2
+ import { useLightboxProps, composePrefix, createIcon, ImageSlide, isImageSlide, cssClass, makeComposePrefix, useLightboxState, useDocumentContext, useEventCallback, translateSlideCounter, cssVar, clsx, getSlideKey, useRTL, useEvents, useSensors, useKeyboardNavigation, useAnimation, cleanup, calculatePreload, hasSlides, getSlide, translateLabel, makeUseContext, LightboxPropsProvider, createIconDisabled, IconButton, addToolbarButton, createModule } from '../../index.js';
3
3
  import { PLUGIN_THUMBNAILS, ELEMENT_ICON, CLASS_FLEX_CENTER, ACTION_SWIPE, ACTION_NEXT, ACTION_PREV, PLUGIN_FULLSCREEN, MODULE_CONTROLLER } from '../../types.js';
4
4
 
5
5
  const defaultThumbnailsProps = {
@@ -57,12 +57,14 @@ const fadeOutPrefix = makeComposePrefix("fadeout");
57
57
  const placeholderPrefix = makeComposePrefix("placeholder");
58
58
  const DELAY = "delay";
59
59
  const DURATION = "duration";
60
- function Thumbnail({ slide, onClick, active, fadeIn, fadeOut, placeholder, onLoseFocus }) {
60
+ function Thumbnail({ slide, index, onClick, fadeIn, fadeOut, placeholder, onLoseFocus }) {
61
61
  const ref = React.useRef(null);
62
- const { render, styles } = useLightboxProps();
62
+ const { render, styles, labels } = useLightboxProps();
63
+ const { slides, globalIndex } = useLightboxState();
63
64
  const { getOwnerDocument } = useDocumentContext();
64
65
  const { width, height, imageFit } = useThumbnailsProps();
65
66
  const rect = { width, height };
67
+ const active = index === globalIndex;
66
68
  const onLoseFocusCallback = useEventCallback(onLoseFocus);
67
69
  React.useEffect(() => {
68
70
  if (fadeOut && getOwnerDocument().activeElement === ref.current) {
@@ -83,7 +85,7 @@ function Thumbnail({ slide, onClick, active, fadeIn, fadeOut, placeholder, onLos
83
85
  }
84
86
  : null),
85
87
  ...styles.thumbnail,
86
- }, onClick: onClick }, slide && renderThumbnail({ slide, render, rect, imageFit })));
88
+ }, onClick: onClick, "aria-current": active ? true : undefined, "aria-label": translateSlideCounter(labels, slides, index) }, slide && renderThumbnail({ slide, render, rect, imageFit })));
87
89
  }
88
90
 
89
91
  function isHorizontal(position) {
@@ -103,7 +105,7 @@ function ThumbnailsTrack({ visible, containerRef }) {
103
105
  const track = React.useRef(null);
104
106
  const isRTL = useRTL();
105
107
  const { publish, subscribe } = useEvents();
106
- const { carousel, styles } = useLightboxProps();
108
+ const { carousel, styles, labels } = useLightboxProps();
107
109
  const { slides, globalIndex, animation } = useLightboxState();
108
110
  const { registerSensors, subscribeSensors } = useSensors();
109
111
  useKeyboardNavigation(subscribeSensors);
@@ -180,7 +182,7 @@ function ThumbnailsTrack({ visible, containerRef }) {
180
182
  ...(gap !== defaultThumbnailsProps.gap ? { [cssVar(cssThumbnailPrefix("gap"))]: `${gap}px` } : null),
181
183
  ...styles.thumbnailsContainer,
182
184
  } },
183
- React.createElement("nav", { ref: track, style: styles.thumbnailsTrack, className: clsx(cssClass(cssPrefix("track")), cssClass(CLASS_FLEX_CENTER)), tabIndex: -1, ...registerSensors }, items.map(({ key, index, slide }) => {
185
+ React.createElement("nav", { ref: track, style: styles.thumbnailsTrack, className: clsx(cssClass(cssPrefix("track")), cssClass(CLASS_FLEX_CENTER)), "aria-label": translateLabel(labels, "Thumbnails"), tabIndex: -1, ...registerSensors }, items.map(({ key, index, slide }) => {
184
186
  const fadeAnimationDuration = animationDuration / Math.abs(offset || 1);
185
187
  const fadeIn = (offset > 0 && index > globalIndex + preload - offset && index <= globalIndex + preload) ||
186
188
  (offset < 0 && index < globalIndex - preload - offset && index >= globalIndex - preload)
@@ -199,7 +201,7 @@ function ThumbnailsTrack({ visible, containerRef }) {
199
201
  : -offset - (index - (globalIndex + preload))) * fadeAnimationDuration,
200
202
  }
201
203
  : undefined;
202
- return (React.createElement(Thumbnail, { key: key, slide: slide, active: index === globalIndex, fadeIn: fadeIn, fadeOut: fadeOut, placeholder: !slide, onClick: handleClick(index), onLoseFocus: () => { var _a; return (_a = track.current) === null || _a === void 0 ? void 0 : _a.focus(); } }));
204
+ return (React.createElement(Thumbnail, { key: key, index: index, slide: slide, fadeIn: fadeIn, fadeOut: fadeOut, placeholder: !slide, onClick: handleClick(index), onLoseFocus: () => { var _a; return (_a = track.current) === null || _a === void 0 ? void 0 : _a.focus(); } }));
203
205
  })),
204
206
  vignette && React.createElement("div", { className: cssClass(cssPrefix("vignette")) })));
205
207
  }
@@ -43,7 +43,9 @@ declare module "yet-another-react-lightbox" {
43
43
  iconZoomOut?: RenderFunction;
44
44
  }
45
45
  interface Labels {
46
+ /** `Zoom in` button title */
46
47
  "Zoom in"?: string;
48
+ /** `Zoom out` button title */
47
49
  "Zoom out"?: string;
48
50
  }
49
51
  interface RenderSlideProps {
package/dist/types.d.ts CHANGED
@@ -229,7 +229,7 @@ interface ControllerSettings {
229
229
  focus: boolean;
230
230
  /** @deprecated for internal use only */
231
231
  touchAction: "none" | "pan-y";
232
- /** if `true`, set ARIA attributes on the controller div */
232
+ /** @deprecated for internal use only */
233
233
  aria: boolean;
234
234
  /** if `true`, close the lightbox on pull-up gesture */
235
235
  closeOnPullUp: boolean;
@@ -378,11 +378,30 @@ interface Callbacks {
378
378
  /** a callback called when the portal closes */
379
379
  exited?: Callback;
380
380
  }
381
- /** Custom UI labels / translations */
381
+ /** Custom UI labels / translations / localization */
382
382
  interface Labels {
383
+ /** `Previous` button title */
383
384
  Previous?: string;
385
+ /** `Next` button title */
384
386
  Next?: string;
387
+ /** `Close` button title */
385
388
  Close?: string;
389
+ /** Slide ARIA role description */
390
+ Slide?: string;
391
+ /** Carousel ARIA role description */
392
+ Carousel?: string;
393
+ /** Lightbox ARIA label */
394
+ Lightbox?: string;
395
+ /** Carousel ARIA label */
396
+ "Photo gallery"?: string;
397
+ /**
398
+ * Slide ARIA label
399
+ *
400
+ * The value is a template string supporting the following placeholders:
401
+ * - {index} - current slide index
402
+ * - {total} - total number of slides
403
+ */
404
+ "{index} of {total}"?: string;
386
405
  }
387
406
  type Label = keyof Labels;
388
407
  /** Toolbar settings */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yet-another-react-lightbox",
3
- "version": "3.23.4",
3
+ "version": "3.24.0",
4
4
  "description": "Modern React lightbox component",
5
5
  "author": "Igor Danchenko",
6
6
  "license": "MIT",