yet-another-react-lightbox-lite 1.0.0 → 1.2.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/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  Lightweight React lightbox component. This is a trimmed-down version of the
4
4
  [yet-another-react-lightbox](https://github.com/igordanchenko/yet-another-react-lightbox)
5
- that provides essential lightbox features and slick UX with just 2.8KB bundle
5
+ that provides essential lightbox features and slick UX with just 3KB bundle
6
6
  size.
7
7
 
8
8
  ## Overview
9
9
 
10
- [![NPM Version](https://img.shields.io/npm/v/yet-another-react-lightbox-lite.svg?color=blue)](https://www.npmjs.com/package/yet-another-react-lightbox)
11
- [![Bundle Size](https://img.shields.io/bundlephobia/minzip/yet-another-react-lightbox-lite.svg?color=blue)](https://bundlephobia.com/package/yet-another-react-lightbox)
12
- [![License MIT](https://img.shields.io/npm/l/yet-another-react-lightbox-lite.svg?color=blue)](https://github.com/igordanchenko/yet-another-react-lightbox/blob/main/LICENSE)
10
+ [![NPM Version](https://img.shields.io/npm/v/yet-another-react-lightbox-lite.svg?color=blue)](https://www.npmjs.com/package/yet-another-react-lightbox-lite)
11
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/yet-another-react-lightbox-lite.svg?color=blue)](https://bundlephobia.com/package/yet-another-react-lightbox-lite)
12
+ [![License MIT](https://img.shields.io/npm/l/yet-another-react-lightbox-lite.svg?color=blue)](https://github.com/igordanchenko/yet-another-react-lightbox-lite/blob/main/LICENSE)
13
13
 
14
14
  - **Built for React:** works with React 18+
15
15
  - **UX:** supports keyboard, mouse, touchpad and touchscreen navigation
@@ -110,7 +110,6 @@ with `next/image` with the following `render.slide` render function.
110
110
 
111
111
  ```tsx
112
112
  <Lightbox
113
- // ...
114
113
  render={{
115
114
  slide: ({ slide, rect }) => {
116
115
  const width =
@@ -147,6 +146,7 @@ with `next/image` with the following `render.slide` render function.
147
146
  );
148
147
  },
149
148
  }}
149
+ // ...
150
150
  />
151
151
  ```
152
152
 
@@ -203,6 +203,63 @@ Custom UI labels / translations.
203
203
  />
204
204
  ```
205
205
 
206
+ ### toolbar
207
+
208
+ Type: `object`
209
+
210
+ Toolbar settings.
211
+
212
+ - `buttons` - custom toolbar buttons (type: `ReactNode[]`)
213
+ - `fixed` - if `true`, the toolbar is positioned statically above the carousel
214
+
215
+ Usage example:
216
+
217
+ ```tsx
218
+ <Lightbox
219
+ toolbar={{
220
+ fixed: true,
221
+ buttons: [
222
+ <button
223
+ type="button"
224
+ className="yarll__button"
225
+ onClick={() => {
226
+ // ...
227
+ }}
228
+ >
229
+ <ButtonIcon />
230
+ </button>,
231
+ ],
232
+ }}
233
+ // ...
234
+ />
235
+ ```
236
+
237
+ ### controller
238
+
239
+ Type: `object`
240
+
241
+ Controller settings.
242
+
243
+ - `closeOnPullUp` - if `true`, close the lightbox on pull-up gesture (default:
244
+ `true`)
245
+ - `closeOnPullDown` - if `true`, close the lightbox on pull-down gesture
246
+ (default: `true`)
247
+ - `closeOnBackdropClick` - if `true`, close the lightbox when the backdrop is
248
+ clicked (default: `true`)
249
+
250
+ Usage example:
251
+
252
+ ```tsx
253
+ <Lightbox
254
+ controller={{
255
+ closeOnPullUp: false,
256
+ closeOnPullDown: false,
257
+ closeOnBackdropClick: false,
258
+ }}
259
+ // ...
260
+ />
261
+ ```
262
+
206
263
  ### render
207
264
 
208
265
  Type: `object`
@@ -268,7 +325,6 @@ slides counter.
268
325
 
269
326
  ```tsx
270
327
  <Lightbox
271
- // ...
272
328
  render={{
273
329
  controls: () =>
274
330
  index !== undefined && (
@@ -277,29 +333,7 @@ slides counter.
277
333
  </div>
278
334
  ),
279
335
  }}
280
- />
281
- ```
282
-
283
- You can also use the `render.controls` render function to add custom buttons to
284
- the toolbar.
285
-
286
- ```tsx
287
- <Lightbox
288
336
  // ...
289
- render={{
290
- controls: () => (
291
- <button
292
- type="button"
293
- className="yarll__button"
294
- style={{ position: "absolute", top: 8, right: 64 }}
295
- onClick={() => {
296
- // ...
297
- }}
298
- >
299
- <ButtonIcon />
300
- </button>
301
- ),
302
- }}
303
337
  />
304
338
  ```
305
339
 
@@ -315,6 +349,42 @@ Render custom `Next` icon.
315
349
 
316
350
  Render custom `Close` icon.
317
351
 
352
+ ### styles
353
+
354
+ Type: `{ [key in Slot]?: SlotCSSProperties }`
355
+
356
+ Customization slots styles allow you to specify custom CSS styles or override
357
+ `--yarll__*` CSS variables by passing your custom styles through to the
358
+ corresponding lightbox elements.
359
+
360
+ Supported customization slots:
361
+
362
+ - `portal` - lightbox portal (root)
363
+ - `carousel` - lightbox carousel
364
+ - `slide` - lightbox slide
365
+ - `image` - lightbox slide image
366
+ - `toolbar` - lightbox toolbar
367
+ - `button` - lightbox button
368
+ - `icon` - lightbox icon
369
+
370
+ Usage example:
371
+
372
+ ```tsx
373
+ <Lightbox
374
+ styles={{
375
+ portal: { "--yarll__backdrop_color": "rgba(0, 0, 0, 0.6)" },
376
+ }}
377
+ // ...
378
+ />
379
+ ```
380
+
381
+ ### className
382
+
383
+ Type: `string`
384
+
385
+ CSS class of the lightbox root element. You can use this class name to provide
386
+ module-scoped style overrides.
387
+
318
388
  ## Custom Slide Attributes
319
389
 
320
390
  You can add custom slide attributes with the following module augmentation.
@@ -420,6 +490,31 @@ export default function App() {
420
490
  }
421
491
  ```
422
492
 
493
+ ## Body Scroll Lock
494
+
495
+ By default, the lightbox hides the browser window scrollbar and prevents
496
+ document `<body>` from scrolling underneath the lightbox by assigning the
497
+ `height: 100%; overflow: hidden;` styles to the document `<body>` element.
498
+
499
+ If this behavior causes undesired side effects in your case, and you prefer not
500
+ to use this feature, you can turn it off by assigning the
501
+ `yarll__no_scroll_lock` class to the lightbox.
502
+
503
+ ```tsx
504
+ <Lightbox
505
+ className="yarll__no_scroll_lock"
506
+ // ...
507
+ />
508
+ ```
509
+
510
+ However, if you keep the body scroll lock feature on, you may notice a visual
511
+ layout shift of some fixed-positioned page elements when the lightbox opens. To
512
+ address this, you can assign the `yarll__fixed` CSS class to your
513
+ fixed-positioned elements to keep them in place. Please note that the
514
+ fixed-positioned element container should not have its own border or padding
515
+ styles. If that's the case, you can always add an extra wrapper that just
516
+ defines the fixed position without visual styles.
517
+
423
518
  ## License
424
519
 
425
520
  MIT © 2024 [Igor Danchenko](https://github.com/igordanchenko)
package/dist/index.d.ts CHANGED
@@ -13,6 +13,14 @@ interface LightboxProps {
13
13
  labels?: Labels;
14
14
  /** custom render functions */
15
15
  render?: Render;
16
+ /** toolbar settings */
17
+ toolbar?: ToolbarSettings;
18
+ /** controller settings */
19
+ controller?: ControllerSettings;
20
+ /** customization slots styles */
21
+ styles?: SlotStyles;
22
+ /** CSS class of the lightbox root element */
23
+ className?: string;
16
24
  }
17
25
  /** Slide */
18
26
  type Slide = SlideTypes[SlideTypeKey];
@@ -88,6 +96,49 @@ interface RenderSlideProps {
88
96
  /** if `true`, the slide is the current slide in the viewport */
89
97
  current: boolean;
90
98
  }
99
+ /** Toolbar settings */
100
+ interface ToolbarSettings {
101
+ /** custom toolbar buttons */
102
+ buttons?: React.ReactNode[];
103
+ /** if `true`, the toolbar is positioned statically above the carousel */
104
+ fixed?: boolean;
105
+ }
106
+ /** Controller settings */
107
+ interface ControllerSettings {
108
+ /** if `true`, close the lightbox on pull-up gesture (default: `true`) */
109
+ closeOnPullUp?: boolean;
110
+ /** if `true`, close the lightbox on pull-down gesture (default: `true`) */
111
+ closeOnPullDown?: boolean;
112
+ /** if `true`, close the lightbox when the backdrop is clicked (default: `true`) */
113
+ closeOnBackdropClick?: boolean;
114
+ }
115
+ /** Customization slots */
116
+ interface SlotType {
117
+ /** lightbox portal (root) customization slot */
118
+ portal: "portal";
119
+ /** lightbox carousel customization slot */
120
+ carousel: "carousel";
121
+ /** lightbox slide customization slot */
122
+ slide: "slide";
123
+ /** lightbox slide image customization slot */
124
+ image: "image";
125
+ /** lightbox toolbar customization slot */
126
+ toolbar: "toolbar";
127
+ /** lightbox button customization slot */
128
+ button: "button";
129
+ /** lightbox icon customization slot */
130
+ icon: "icon";
131
+ }
132
+ /** Customization slots */
133
+ type Slot = SlotType[keyof SlotType];
134
+ /** Customization slot CSS properties */
135
+ interface SlotCSSProperties extends React.CSSProperties {
136
+ [key: `--yarll__${string}`]: string | number;
137
+ }
138
+ /** Customization slots styles */
139
+ type SlotStyles = {
140
+ [key in Slot]?: SlotCSSProperties;
141
+ };
91
142
  /** Rect */
92
143
  type Rect = {
93
144
  width: number;
@@ -100,4 +151,4 @@ type RenderFunction<T = void> = [T] extends [void] ? () => React.ReactNode : (pr
100
151
 
101
152
  declare function Lightbox({ slides, index, setIndex, ...rest }: LightboxProps): react_jsx_runtime.JSX.Element | null;
102
153
 
103
- export { type Callback, type GenericSlide, type ImageSource, type Label, type Labels, type LightboxProps, type Rect, type Render, type RenderFunction, type RenderSlideProps, type Slide, type SlideImage, type SlideTypeKey, type SlideTypes, Lightbox as default };
154
+ export { type Callback, type ControllerSettings, type GenericSlide, type ImageSource, type Label, type Labels, type LightboxProps, type Rect, type Render, type RenderFunction, type RenderSlideProps, type Slide, type SlideImage, type SlideTypeKey, type SlideTypes, type Slot, type SlotStyles, type SlotType, type ToolbarSettings, Lightbox as default };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { useContext, createContext, useState, useRef, useCallback, useMemo, useEffect } from 'react';
2
+ import { useContext, createContext, useState, useRef, useCallback, useMemo, useEffect, isValidElement, cloneElement } from 'react';
3
3
  import { flushSync, createPortal } from 'react-dom';
4
4
 
5
5
  const cssPrefix = "yarll__";
@@ -41,6 +41,7 @@ function LightboxContextProvider({ children, ...props }) {
41
41
  }
42
42
 
43
43
  function ImageSlide({ slide, rect }) {
44
+ const { styles } = useLightboxContext();
44
45
  const { width, height } = slide.srcSet?.[0] ?? slide;
45
46
  const imageAspectRatio = width && height ? width / height : undefined;
46
47
  const srcSet = slide.srcSet
@@ -50,11 +51,11 @@ function ImageSlide({ slide, rect }) {
50
51
  const sizes = imageAspectRatio
51
52
  ? `${imageAspectRatio < rect.width / rect.height ? Math.round(imageAspectRatio * rect.height) : rect.width}px`
52
53
  : undefined;
53
- return (jsx("img", { draggable: false, className: cssClass("slide_image"), srcSet: srcSet, sizes: sizes, src: slide.src, alt: slide.alt }));
54
+ return (jsx("img", { draggable: false, style: styles?.image, className: cssClass("slide_image"), srcSet: srcSet, sizes: sizes, src: slide.src, alt: slide.alt }));
54
55
  }
55
56
 
56
57
  function Carousel() {
57
- const { slides, index, render: { slide: renderSlide, slideHeader, slideFooter } = {} } = useLightboxContext();
58
+ const { slides, index, styles, render: { slide: renderSlide, slideHeader, slideFooter } = {} } = useLightboxContext();
58
59
  const [rect, setRect] = useState();
59
60
  const observer = useRef();
60
61
  const handleRef = useCallback((node) => {
@@ -69,7 +70,7 @@ function Carousel() {
69
70
  updateRect();
70
71
  }
71
72
  }, []);
72
- return (jsx("div", { ref: handleRef, className: cssClass("carousel"), children: rect &&
73
+ return (jsx("div", { ref: handleRef, style: styles?.carousel, className: cssClass("carousel"), children: rect &&
73
74
  Array.from({ length: 5 }).map((_, i) => {
74
75
  const slideIndex = index - 2 + i;
75
76
  if (slideIndex < 0 || slideIndex >= slides.length)
@@ -77,7 +78,7 @@ function Carousel() {
77
78
  const slide = slides[slideIndex];
78
79
  const current = slideIndex === index;
79
80
  const context = { slide, rect, current };
80
- return (jsxs("div", { role: "group", "aria-roledescription": "slide", className: cssClass("slide"), hidden: !current, children: [slideHeader?.(context), renderSlide?.(context) ?? jsx(ImageSlide, { ...context }), slideFooter?.(context)] }, slide.key ?? `${slideIndex}-${slide.src}`));
81
+ return (jsxs("div", { role: "group", "aria-roledescription": "slide", className: cssClass("slide"), hidden: !current, style: styles?.slide, children: [slideHeader?.(context), renderSlide?.(context) ?? jsx(ImageSlide, { ...context }), slideFooter?.(context)] }, slide.key ?? `${slideIndex}-${slide.src}`));
81
82
  }) }));
82
83
  }
83
84
 
@@ -115,9 +116,9 @@ function Controller({ setIndex, children }) {
115
116
  }
116
117
 
117
118
  function Button({ icon: Icon, renderIcon, label, onClick, disabled, className }) {
118
- const { labels } = useLightboxContext();
119
+ const { labels, styles } = useLightboxContext();
119
120
  const buttonLabel = translateLabel(labels, label);
120
- return (jsx("button", { type: "button", title: buttonLabel, "aria-label": buttonLabel, onClick: onClick, disabled: disabled, className: clsx(cssClass("button"), className), children: renderIcon?.() ?? jsx(Icon, { className: cssClass("icon") }) }));
121
+ return (jsx("button", { type: "button", title: buttonLabel, "aria-label": buttonLabel, onClick: onClick, disabled: disabled, style: styles?.button, className: clsx(cssClass("button"), className), children: renderIcon?.() ?? jsx(Icon, { style: styles?.icon, className: cssClass("icon") }) }));
121
122
  }
122
123
 
123
124
  function svgIcon(name, children) {
@@ -136,9 +137,9 @@ const Next = createIcon("Next", jsx("path", { d: "M10 6L8.59 7.41 13.17 12l-4.58
136
137
  const Previous = createIcon("Previous", jsx("path", { d: "M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" }));
137
138
 
138
139
  function Navigation() {
139
- const { slides, index, render: { iconPrev, iconNext, iconClose, controls } = {} } = useLightboxContext();
140
- const { prev, next, close } = useController();
141
- return (jsxs(Fragment, { children: [slides.length > 1 && (jsxs(Fragment, { children: [jsx(Button, { label: "Previous", icon: Previous, renderIcon: iconPrev, onClick: prev, className: cssClass("button_prev"), disabled: index <= 0 }), jsx(Button, { label: "Next", icon: Next, renderIcon: iconNext, onClick: next, className: cssClass("button_next"), disabled: index >= slides.length - 1 })] })), jsx(Button, { label: "Close", icon: Close, renderIcon: iconClose, onClick: close, className: cssClass("button_close") }), controls?.()] }));
140
+ const { slides, index, render: { iconPrev, iconNext, controls } = {} } = useLightboxContext();
141
+ const { prev, next } = useController();
142
+ return (jsxs(Fragment, { children: [slides.length > 1 && (jsxs(Fragment, { children: [jsx(Button, { label: "Previous", icon: Previous, renderIcon: iconPrev, onClick: prev, className: cssClass("button_prev"), disabled: index <= 0 }), jsx(Button, { label: "Next", icon: Next, renderIcon: iconNext, onClick: next, className: cssClass("button_next"), disabled: index >= slides.length - 1 })] })), controls?.()] }));
142
143
  }
143
144
 
144
145
  function useSensors() {
@@ -147,6 +148,12 @@ function useSensors() {
147
148
  const wheelCooldownMomentum = useRef(null);
148
149
  const activePointer = useRef(null);
149
150
  const { prev, next, close } = useController();
151
+ const { closeOnPullUp, closeOnPullDown, closeOnBackdropClick } = {
152
+ closeOnPullUp: true,
153
+ closeOnPullDown: true,
154
+ closeOnBackdropClick: true,
155
+ ...useLightboxContext().controller,
156
+ };
150
157
  return useMemo(() => {
151
158
  const onKeyDown = (event) => {
152
159
  switch (event.key) {
@@ -173,8 +180,9 @@ function useSensors() {
173
180
  const onPointerUp = (event) => {
174
181
  if (event.pointerId === activePointer.current?.pointerId) {
175
182
  const dx = event.clientX - activePointer.current.clientX;
183
+ const dy = event.clientY - activePointer.current.clientY;
176
184
  const deltaX = Math.abs(dx);
177
- const deltaY = Math.abs(event.clientY - activePointer.current.clientY);
185
+ const deltaY = Math.abs(dy);
178
186
  if (deltaX > 50 && deltaX > 1.2 * deltaY) {
179
187
  if (dx > 0) {
180
188
  prev();
@@ -183,9 +191,10 @@ function useSensors() {
183
191
  next();
184
192
  }
185
193
  }
186
- else if ((deltaY > 50 && deltaY > 1.2 * deltaX) ||
187
- (activePointer.current.target instanceof HTMLElement &&
188
- activePointer.current.target.className.split(" ").includes(cssClass("slide")))) {
194
+ else if ((deltaY > 50 && deltaY > 1.2 * deltaX && ((closeOnPullUp && dy < 0) || (closeOnPullDown && dy > 0))) ||
195
+ (closeOnBackdropClick &&
196
+ activePointer.current.target instanceof Element &&
197
+ Array.from(activePointer.current.target.classList).some((className) => [cssClass("slide"), cssClass("portal")].includes(className)))) {
189
198
  close();
190
199
  }
191
200
  activePointer.current = null;
@@ -229,7 +238,7 @@ function useSensors() {
229
238
  onPointerCancel: onPointerUp,
230
239
  onWheel,
231
240
  };
232
- }, [prev, next, close]);
241
+ }, [prev, next, close, closeOnPullUp, closeOnPullDown, closeOnBackdropClick]);
233
242
  }
234
243
 
235
244
  function setAttribute(element, attribute, value) {
@@ -245,6 +254,7 @@ function setAttribute(element, attribute, value) {
245
254
  };
246
255
  }
247
256
  function Portal({ children }) {
257
+ const { styles, className } = useLightboxContext();
248
258
  const cleanup = useRef([]);
249
259
  const [mounted, setMounted] = useState(false);
250
260
  const [visible, setVisible] = useState(false);
@@ -307,7 +317,7 @@ function Portal({ children }) {
307
317
  }
308
318
  }, [handleCleanup]);
309
319
  return mounted
310
- ? createPortal(jsx("div", { "aria-modal": true, role: "dialog", "aria-roledescription": "carousel", tabIndex: -1, ref: handleRef, className: clsx(cssClass("portal"), !visible && cssClass("portal_closed")), onTransitionEnd: onTransitionEnd.current, onFocus: (event) => {
320
+ ? createPortal(jsx("div", { "aria-modal": true, role: "dialog", "aria-roledescription": "carousel", tabIndex: -1, ref: handleRef, style: styles?.portal, className: clsx(cssClass("portal"), !visible && cssClass("portal_closed"), className), onTransitionEnd: onTransitionEnd.current, onFocus: (event) => {
311
321
  if (!restoreFocus.current) {
312
322
  restoreFocus.current = event.relatedTarget;
313
323
  }
@@ -315,10 +325,16 @@ function Portal({ children }) {
315
325
  : null;
316
326
  }
317
327
 
328
+ function Toolbar() {
329
+ const { render: { iconClose } = {}, toolbar: { buttons, fixed } = {}, styles } = useLightboxContext();
330
+ const { close } = useController();
331
+ return (jsxs("div", { style: styles?.toolbar, className: clsx(cssClass("toolbar"), fixed && cssClass("toolbar_fixed")), children: [buttons?.map((button, key) => (isValidElement(button) && !button.key ? cloneElement(button, { key }) : button)), jsx(Button, { label: "Close", icon: Close, renderIcon: iconClose, onClick: close })] }));
332
+ }
333
+
318
334
  function Lightbox({ slides, index, setIndex, ...rest }) {
319
335
  if (!Array.isArray(slides) || index === undefined || index < 0 || index >= slides.length)
320
336
  return null;
321
- return (jsx(LightboxContextProvider, { slides, index, ...rest, children: jsx(Controller, { setIndex, children: jsxs(Portal, { children: [jsx(Carousel, {}), jsx(Navigation, {})] }) }) }));
337
+ return (jsx(LightboxContextProvider, { slides, index, ...rest, children: jsx(Controller, { setIndex, children: jsxs(Portal, { children: [jsx(Toolbar, {}), jsx(Carousel, {}), jsx(Navigation, {})] }) }) }));
322
338
  }
323
339
 
324
340
  export { Lightbox as default };
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- body:has(>.yarll__portal){overscroll-behavior:none}body:has(>.yarll__portal):not(.yarll__no_scroll_lock){height:100%;overflow:hidden;padding-right:var(--yarll__scrollbar-width,0)}.yarll__portal{align-items:center;display:flex;flex-direction:column;inset:0;justify-content:center;outline:none;overflow:hidden;overscroll-behavior:none;position:fixed;touch-action:none;-moz-user-select:none;user-select:none;-webkit-user-select:none;z-index:var(--yarll__portal_zindex,9999);-webkit-touch-callout:none;background-color:var(--yarll__backdrop_color,#000);color:var(--yarll__color,#fff);opacity:1;transition:var(--yarll__fade_transition,opacity .3s ease)}.yarll__portal_closed{opacity:0}.yarll__portal *{box-sizing:border-box}.yarll__carousel{align-self:stretch;flex:1;margin:var(--yarll__carousel_margin,16px);position:relative}.yarll__slide{align-items:center;display:flex;flex-direction:column;inset:0;justify-content:center;position:absolute}.yarll__slide[hidden]{display:none}.yarll__slide_image{display:block;flex:1;max-height:100%;max-width:100%;min-height:0;min-width:0;-o-object-fit:contain;object-fit:contain}.yarll__button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--yarll__button_background_color,transparent);border:0;color:var(--yarll__button_color,var(--yarll__button_color,hsla(0,0%,100%,.8)));cursor:pointer;filter:var(--yarll__button_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));margin:0;padding:var(--yarll__button_padding,8px)}.yarll__button:focus-visible{box-shadow:var(--yarll__button_focus_box_shadow,0 0 0 4px #fff);color:var(--yarll__button_color_active,#fff);outline:var(--yarll__button_focus_outline,6px double #000)}@supports not selector(:focus-visible){.yarll__button:focus{box-shadow:var(--yarll__button_focus_box_shadow,0 0 0 4px #fff);color:var(--yarll__button_color_active,#fff);outline:var(--yarll__button_focus_outline,6px double #000)}}@media (hover:hover){.yarll__button:focus-visible:hover,.yarll__button:focus:hover,.yarll__button:hover{color:var(--yarll__button_color_active,#fff)}}.yarll__button_close{position:absolute;right:8px;top:8px}.yarll__button_prev{left:8px}.yarll__button_next{right:8px}.yarll__button_next,.yarll__button_prev{padding:var(--yarll__navigation_button_padding,24px 8px);position:absolute}.yarll__button:disabled{color:var(--yarll__button_color_disabled,var(--yarll__button_color_disabled,hsla(0,0%,100%,.4)));cursor:default}.yarll__icon{display:block;height:var(--yarll__icon_size,32px);width:var(--yarll__icon_size,32px)}
1
+ body:has(>.yarll__portal){overscroll-behavior:none}body:has(>.yarll__portal:not(.yarll__no_scroll_lock)){height:100%;overflow:hidden;padding-right:var(--yarll__scrollbar-width,0)}body:has(>.yarll__portal:not(.yarll__no_scroll_lock)) .yarll_fixed{padding-right:var(--yarll__scrollbar-width,0)}.yarll__portal{align-items:center;display:flex;flex-direction:column;inset:0;justify-content:center;outline:none;overflow:hidden;overscroll-behavior:none;position:fixed;touch-action:none;-moz-user-select:none;user-select:none;-webkit-user-select:none;z-index:var(--yarll__portal_zindex,9999);-webkit-touch-callout:none;background-color:var(--yarll__backdrop_color,#000);color:var(--yarll__color,#fff);opacity:1;transition:var(--yarll__fade_transition,opacity .3s ease)}.yarll__portal_closed{opacity:0}.yarll__portal *{box-sizing:border-box}.yarll__carousel{align-self:stretch;flex:1;margin:var(--yarll__carousel_margin,16px);position:relative}.yarll__slide{align-items:center;display:flex;flex-direction:column;inset:0;justify-content:center;position:absolute}.yarll__slide[hidden]{display:none}.yarll__slide_image{display:block;flex:1;max-height:100%;max-width:100%;min-height:0;min-width:0;-o-object-fit:contain;object-fit:contain}.yarll__toolbar{align-items:center;display:flex;position:absolute;right:var(--yarll__toolbar_margin,8px);top:var(--yarll__toolbar_margin,8px);z-index:1}.yarll__toolbar_fixed{align-self:flex-end;margin-inline-end:var(--yarll__toolbar_margin,8px);position:static;z-index:unset}.yarll__toolbar_fixed,.yarll__toolbar_fixed+.yarll__carousel{margin-block-start:var(--yarll__toolbar_margin,8px)}.yarll__button{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--yarll__button_background_color,transparent);border:0;color:var(--yarll__button_color,var(--yarll__button_color,hsla(0,0%,100%,.8)));cursor:pointer;filter:var(--yarll__button_filter,drop-shadow(2px 2px 2px rgba(0,0,0,.8)));margin:0;padding:var(--yarll__button_padding,8px)}.yarll__button:focus-visible{box-shadow:var(--yarll__button_focus_box_shadow,0 0 0 4px #fff);color:var(--yarll__button_color_active,#fff);outline:var(--yarll__button_focus_outline,6px double #000)}@supports not selector(:focus-visible){.yarll__button:focus{box-shadow:var(--yarll__button_focus_box_shadow,0 0 0 4px #fff);color:var(--yarll__button_color_active,#fff);outline:var(--yarll__button_focus_outline,6px double #000)}}@media (hover:hover){.yarll__button:focus-visible:hover,.yarll__button:focus:hover,.yarll__button:hover{color:var(--yarll__button_color_active,#fff)}}.yarll__button_next,.yarll__button_prev{padding:var(--yarll__navigation_button_padding,24px 8px);position:absolute}.yarll__button_prev{left:8px}.yarll__button_next{right:8px}.yarll__button:disabled{color:var(--yarll__button_color_disabled,var(--yarll__button_color_disabled,hsla(0,0%,100%,.4)));cursor:default}.yarll__icon{display:block;height:var(--yarll__icon_size,32px);width:var(--yarll__icon_size,32px)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yet-another-react-lightbox-lite",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Lightweight React lightbox component",
5
5
  "author": "Igor Danchenko",
6
6
  "license": "MIT",