Lightbox
Повноекранний компонент Lightbox-галереї зображень і відео на основі @reelkit/react-lightbox.
Features
Встановлення
npm install @reelkit/react-lightbox @reelkit/react lucide-reactНе забудьте імпортувати стилі:
import '@reelkit/react-lightbox/styles.css';Icons
Стандартні елементи керування використовують lucide-react для іконок. Якщо ви віддаєте перевагу іншій бібліотеці іконок, скористайтеся renderControls та renderNavigation , щоб передати власні.
Швидкий старт
The LightboxOverlay показує зображення на весь екран. Передайте масив об’єктів LightboxItem і керуйте видимістю через індекс, який може бути null.
import { useState } from 'react';
import { LightboxOverlay, type LightboxItem } from '@reelkit/react-lightbox';
import '@reelkit/react-lightbox/styles.css';
const images: LightboxItem[] = [
{
src: 'https://example.com/image1.jpg',
title: 'Sunset',
description: 'Beautiful sunset over the ocean',
},
{
src: 'https://example.com/image2.jpg',
title: 'Mountains',
},
];
function App() {
const [index, setIndex] = useState<number | null>(null);
return (
<>
{images.map((img, i) => (
<img
key={i}
src={img.src}
onClick={() => setIndex(i)}
/>
))}
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
/>
</>
);
}import { useState } from 'react';
import {
LightboxOverlay,
flipTransition,
lightboxFadeTransition,
lightboxZoomTransition,
slideTransition,
type LightboxItem,
} from '@reelkit/react-lightbox';
import type { TransitionTransformFn } from '@reelkit/react';
import '@reelkit/react-lightbox/styles.css';
const images: LightboxItem[] = [
{
src: '/cdn/samples/images/image-01.jpg',
title: 'Mountain River',
description: 'A beautiful mountain river flowing through the forest',
width: 1600,
height: 1000,
},
{
src: '/cdn/samples/images/image-02.jpg',
title: 'Snowy Peaks',
description: 'Majestic snow-capped mountains reaching for the sky',
width: 1000,
height: 1600,
},
{
src: '/cdn/samples/images/image-03.jpg',
title: 'Foggy Forest',
description: 'Misty morning in the dense forest',
width: 1600,
height: 900,
},
{
src: '/cdn/samples/images/image-04.jpg',
title: 'Ocean Waves',
description: 'Powerful ocean waves crashing against the rocky shore',
width: 900,
height: 1400,
},
{
src: '/cdn/samples/images/image-05.jpg',
title: 'Autumn Path',
description: 'A winding path through the autumn forest',
width: 1600,
height: 1067,
},
{
src: '/cdn/samples/images/image-06.jpg',
title: 'Coastal Cliffs',
description: 'Dramatic coastal cliffs overlooking the deep blue sea',
width: 1600,
height: 1067,
},
];
const transitions: { label: string; fn: TransitionTransformFn }[] = [
{ label: 'slide', fn: slideTransition },
{ label: 'fade', fn: lightboxFadeTransition },
{ label: 'flip', fn: flipTransition },
{ label: 'zoom-in', fn: lightboxZoomTransition },
];
export default function App() {
const [index, setIndex] = useState<number | null>(null);
const [transitionFn, setTransitionFn] = useState<TransitionTransformFn>(
() => slideTransition,
);
return (
<div style={{ padding: 16, background: '#f8fafc', minHeight: '100vh' }}>
{/* Transition picker */}
<div style={{ display: 'flex', gap: 6, marginBottom: 12 }}>
{transitions.map((t) => (
<button
key={t.label}
onClick={() => setTransitionFn(() => t.fn)}
style={{
padding: '6px 14px', borderRadius: 8, border: 'none', cursor: 'pointer',
fontSize: 13, fontWeight: 500,
background: transitionFn === t.fn ? '#6366f1' : '#e2e8f0',
color: transitionFn === t.fn ? '#fff' : '#334155',
}}
>
{t.label}
</button>
))}
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6 }}>
{images.map((img, i) => (
<button
key={i}
onClick={() => setIndex(i)}
style={{
position: 'relative', aspectRatio: '4 / 3', borderRadius: 8,
overflow: 'hidden', border: 'none', padding: 0, cursor: 'pointer',
background: '#e2e8f0',
}}
>
<img
src={img.src}
alt={img.title}
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
/>
</button>
))}
</div>
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
transitionFn={transitionFn}
/>
</div>
);
}Щоб показати галерею як слайдшоу й зберігати оригінал кожного фото, дивіться рецепт «Галерея зі слайдшоу».
Слайди-відео (за бажанням)
Підтримка відео вмикається за бажанням і піддається tree-shaking — використання лише зображень нічого не додає до бандла. Імпортуйте useVideoSlideRenderer і передайте повернені значення у LightboxOverlay. Хук сам дає раду станам завантаження, керуванню звуком і життєвим циклом відео.
import {
LightboxOverlay,
useVideoSlideRenderer,
type LightboxItem,
} from '@reelkit/react-lightbox';
import '@reelkit/react-lightbox/styles.css';
const items: LightboxItem[] = [
{ src: '/photo.jpg', title: 'Photo' },
{
src: '/clip.mp4',
type: 'video',
poster: '/clip-thumb.jpg',
title: 'Video Clip',
},
];
function Gallery() {
const [index, setIndex] = useState<number | null>(null);
const isOpen = index !== null;
const { renderSlide, renderControls, SoundProvider } =
useVideoSlideRenderer(items);
return (
<SoundProvider>
{/* thumbnails… */}
<LightboxOverlay
isOpen={isOpen}
images={items}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
renderSlide={renderSlide}
renderControls={renderControls}
/>
</SoundProvider>
);
}Як це працює
- Хук повертає
SoundProvider— загорніть у нього оверлей, щоб перемикання звуку працювало - Відео відтворюється автоматично (типово без звуку), коли слайд стає активним
- Спільний елемент відео повторно використовується між слайдами заради безперервності звуку на iOS
- Кнопка звуку з’являється на слайдах-відео автоматично, з реактивним перемикачем
- Елементи без
type: 'video'рендеряться як зображення (зворотна сумісність)
Customization
Власні елементи керування
Використовуйте renderControls щоб замінити стандартну кнопку закриття, лічильник і перемикач повного екрана. Складайте з експортованих підкомпонентів:
import {
LightboxOverlay,
CloseButton,
Counter,
FullscreenButton,
} from '@reelkit/react-lightbox';
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderControls={({ onClose, activeIndex, count, isFullscreen, onToggleFullscreen }) => (
<div style={{ position: 'absolute', top: 12, left: 16, right: 16, display: 'flex', justifyContent: 'space-between', zIndex: 10 }}>
<Counter currentIndex={activeIndex} count={count} />
<div>
<FullscreenButton isFullscreen={isFullscreen} onToggle={onToggleFullscreen} />
<CloseButton onClick={onClose} />
</div>
</div>
)}
/>Власний інформаційний оверлей
Використовуйте renderInfo щоб замінити стандартний градієнт із заголовком та описом, або передайте renderInfo={() => null} щоб сховати його повністю:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderInfo={({ item, index }) => (
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: 16, background: 'linear-gradient(transparent, rgba(0,0,0,0.8))', color: '#fff', zIndex: 10 }}>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
)}
/>Власна навігація
Використовуйте renderNavigation щоб замінити стандартні стрілки вперед і назад:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderNavigation={({ onPrev, onNext, activeIndex, count }) => (
<div style={{ position: 'absolute', bottom: 24, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 12, zIndex: 10 }}>
<button onClick={onPrev} disabled={activeIndex === 0}>Prev</button>
<span>{activeIndex + 1} / {count}</span>
<button onClick={onNext} disabled={activeIndex === count - 1}>Next</button>
</div>
)}
/>Власний слайд
Використовуйте renderSlide для повністю власного вмісту слайда. Поверніть null щоб лишити стандартний слайд-зображення:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={handleClose}
renderSlide={({ item, index, size, isActive, onReady, onError }) => {
// Custom CTA on last slide
if (index === images.length - 1) {
return (
<div style={{ width: size[0], height: size[1], display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff' }}>
<h2>View all photos</h2>
</div>
);
}
return null; // default image slide
}}
/>Завантаження вмісту та обробка помилок
Lightbox стежить за станом завантаження й помилок кожного слайда. Поки вміст вантажиться, показується індикатор; для медіа, що не завантажилося, — значок зіпсованого зображення. URL з помилками кешуються, тож повторний перехід одразу показує помилку без нової спроби.
Колбеки життєвого циклу
Якщо ви використовуєте renderSlide, call these callbacks to control the loading indicator:
| Колбек | Коли викликати |
|---|---|
onReady | Зображення завантажилося або відео почало грати. Скидає стани завантаження й помилки. |
onWaiting | Відео бу феризується посеред відтворення. Показує індикатор завантаження. |
onError | Вміст не завантажився. Показує оверлей помилки й кешує URL як зіпсований. |
// Inside renderSlide — wire callbacks to your custom media
renderSlide={({ item, index, size, isActive, onReady, onWaiting, onError }) => (
<div style={{ width: size[0], height: size[1] }}>
{item.type === 'video' ? (
<video
src={item.src}
poster={item.poster}
autoPlay={isActive}
onCanPlay={onReady}
onWaiting={onWaiting}
onError={onError}
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
/>
) : (
<img
src={item.src}
onLoad={onReady}
onError={onError}
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
/>
)}
</div>
)}Власний інтерфейс завантаження та помилок
Замініть стандартний індикатор і значок помилки власними компонентами:
<LightboxOverlay
isOpen={isOpen}
images={images}
onClose={() => setIsOpen(false)}
renderLoading={({ item, activeIndex }) => (
<div style={{
position: 'absolute', inset: 0, zIndex: 10,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: '#fff', fontSize: 14,
}}>
Loading image {activeIndex + 1}...
</div>
)}
renderError={({ item, activeIndex }) => (
<div style={{
position: 'absolute', inset: 0, zIndex: 10,
display: 'flex', flexDirection: 'column',
alignItems: 'center', justifyContent: 'center',
gap: 12, color: 'rgba(255,255,255,0.5)',
}}>
<span style={{ fontSize: 48 }}>!</span>
<span>Failed to load content</span>
</div>
)}
/>Стан в URL
Подивитися демо наживо →LightboxUrlOverlay — окремий компонент, стан відкриття якого живе в адресному рядку. Побудуйте контролер через useOverlayUrlState from @reelkit/react і передайте його як controller: галерея відкривається сама, коли параметр називає слайд, і закривається, коли параметр зникає. Посиланнями можна ділитися, а кнопка «назад» закриває галерею, а не виводить зі сторінки.
Вбудовані клавіші
Слайди можна адресувати вбудованим ключем — розгорніть urlIndexKey (за позицією) або urlStableIdKey (за стабільним id) into the controller — both re-exported from @reelkit/react. See the посібник зі стану в URL та API ядра.
import { useOverlayUrlState, urlIndexKey, urlStableIdKey } from '@reelkit/react';
import { LightboxUrlOverlay } from '@reelkit/react-lightbox';
import { Link } from 'react-router-dom';
const photo = useOverlayUrlState({
param: 'photo',
...urlIndexKey(() => images.length),
});
// Opening is a link — the href is the open action. No open flag, no handler:
// the overlay reads the URL and opens itself.
{images.map((image, i) => (
<Link key={image.src} to={`?photo=${i}`}>
<img src={image.src} />
</Link>
))}
<LightboxUrlOverlay controller={photo} images={images} />Хук приймає один об’єкт опцій і повертає UrlStateController (with set, index, value). Keep it for programmatic control: set — низькорівневий запис, який оверлей робить усередині (зміна слайда та set(null) для закриття). Ним же можна керувати оверлеєм програмно: set(index) відкриває його — так само, як перехід за параметром. Але для відкриття краще звичайне посилання: href можна надіслати, відкрити в новій вкладці, а кнопка «назад» його закриє — і все це без жодного обробника.
Full useOverlayUrlState options (param, adapter, codec, locator): see the довіднику API для React.
LightboxUrlOverlay приймає лише controller (обов’язково), необов’язковий onClose, plus every visual and behavior prop LightboxOverlay takes (images, ariaLabel, transitionFn, the render props, and so on) — but no isOpen.
- Відкриття коштує одного запису в історії. Гортання слайдів замінює його, тож сто свайпів не додають жодного — один крок назад завжди виходить із галереї. «Назад» закриває, а не гортає фотографії.
- Надіслане посилання на кшталт
?photo=3відкриває галерею на цьому слайді. Закриття посилання, що прийшло разом зі сторінкою, прибирає параметр на місці, а не виводить із сайту. - «Назад» закриває лише тоді, коли ви відкрили галерею всередині застосунку — посилання додало запис, тож «назад» повертає до галереї. За надісланим посиланням у новій вкладці історії позаду немає, тож кнопка «назад» виведе із сайту; кнопка закриття або Escape прибирає параметр на місці й лишає вас у галереї. Готові адаптери роутерів ручаються за перехід за посиланням на тій самій сторінці; власний адаптер, який не може сказати, як з’явився запис, теж отримує закриття на місці, і в історії лишаєт ься дублікат сторінки — один крок назад тоді ніби нічого не робить, але нічого й не відкриває.
- Параметр, який не називає жодного слайда — застаріла закладка, змінене вручну значення, — прибирається з URL, а не лишає в адресному рядку слайд, який не відкриється.
У застосунку з роутером передайте адаптер. Прямий запис в історію лишає власне місцеположення роутера застарілим, і наступна навігація втрачає параметр.
import { useReactRouterUrlAdapter } from '@reelkit/react/react-router-url-adapter';
const adapter = useReactRouterUrlAdapter();
const photo = useOverlayUrlState({
param: 'photo',
adapter,
...urlIndexKey(() => images.length),
});
<LightboxUrlOverlay controller={photo} images={images} />Відкриття — це посилання. Оскільки стан відкриття живе в URL, мініатюра — звичайне посилання без обробника кліку, і вся поведінка браузера дістається безкоштовно: відкрити в новій вкладці, скопіювати адресу, побачити підказку при наведенні. У застосунку з роутером беріть посилання роутера, щоб перехід лишався на клієнті.
import { Link } from 'react-router-dom';
// The href is the open action — no onClick, no open flag.
{images.map((image, i) => (
<Link key={image.src} to={`?photo=${i}`}>
<img src={image.src} />
</Link>
))}
<LightboxUrlOverlay controller={photo} images={images} />Для посилань, якими діляться, краще стабільна ідентичність. Індекс адресує за позицією, тож збережений у закладках ?photo=3 відкриє інше зображення, щойно список перевпорядкують. urlStableIdKey адресує за стабільним id, scanning the live list — one call covers the common case.
const photo = useOverlayUrlState({
param: 'photo',
...urlStableIdKey({ items: () => images }),
});
<LightboxUrlOverlay controller={photo} images={images} />Pass hashCodec: base64UrlCodec щоб закодувати id в URL у base64url — оборотне маскування, а не криптографічний хеш.
Адресуєте за іншим полем ( slug), or page an infinite feed with locateAsync, and build the codec/locator самі:
const photo = useOverlayUrlState({
param: 'photo',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => images.findIndex((x) => x.slug === id),
identify: (index) => images[index].slug,
},
});
<LightboxUrlOverlay controller={photo} images={images} />Нескінченні та посторінкові галереї. Синхронний locate відповідає лише за вже завантажені зображення — надіслане посилання на зображення 400 у стрічці, де завантажено 20, нічого не знайде. locateAsync — запасний варіант, що викликається лише коли locate misses.
Shortcut
Keying by the item’s id? Не пишіть кодек і локатор вручну — передайте locateAsync просто в urlStableIdKey({ items, locateAsync }) (він вантажить дані, якщо не знайшов, і повертає індекс). Розгорнутий варіант нижче — для адресації за іншим полем або для повного контролю.
const photo = useOverlayUrlState({
param: 'photo',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => images.findIndex((x) => x.id === id),
identify: (index) => images[index].id,
locateAsync: async (id) => {
const loaded = await loadById(id); // or loadUntil(id) — fetch just that one, or page up to it
if (!loaded) return null; // exhausted — link names no image
setImages(loaded); // commit — the overlay renders from this state
return loaded.findIndex((x) => x.id === id); // wherever it landed
},
},
});
<LightboxUrlOverlay controller={photo} images={images} />- Як саме вантажити — ваша справа: тягніть сторінки поспіль до потрібної або лише одне зображення й додайте його. URL адресує за ідентичністю, а не за позицією, тож
findIndexповерне те місце, де елемент опинився. - While
locateAsyncу процесі, Lightbox лишається закритим, а параметр — недоторканим, тож пряме посилання переживає запит.nullабо відмова прибирає параметр. - Відповідь, що приходить після зміни URL, після закриття або після демонтажу, відкидається — повільний запит не відкриє слайд, якого ніхто не просив.
- Поки триває очікування, нічого не рендериться: цей стан завантаження вже належить сторінці, тож малюйте власний скелетон.
- Тайм-ауту немає — Lightbox не може знати, яка галерея завдовжки. Завершуйте значенням
nullколи сторінки скінчилися, інакше оверлей лишиться закритим назавжди. - Whatever
locateAsyncє оста точним — це індекс щойно завантажених даних, узятий як є, без повторного читанняimages.
Автоперехід
Задайте autoAdvance, щоб галерея після відкриття гралася як слайдшоу. Типово вимкнено, тож наявний Lightbox рендериться й поводиться як раніше.
import { LightboxOverlay } from '@reelkit/react-lightbox';
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
autoAdvance
autoAdvanceInterval={4000}
/>;- Зображення лишаються на екрані
autoAdvanceInterval(типово 5000 мс), відлічуваних із моменту, коли зображення завантажилося, тож повільне зображення ніколи не пропускається, перш ніж його хтось побачив. Для зображення, що не завантажилося, відлік іде від помилки. - Відео програються один раз замість циклу й переходять далі, коли закінчуються. З
useVideoSlideRendererце під’єднано за вас; власнийrenderSlideчитаєautoAdvanceзі своїх пропсів і повідомляє про кінець черезonEnded. - Воно чекає, поки миша лежить на елементі керування (кнопці, посиланні чи підписі), під час свайпу і поки сторінка прихована, а потім продовжує з місця зупинки.
- Ручне перемикання (стрілки, клавіші, колесо, свайп) запускає інтервал нового слайда з нуля; слайдшоу лишається увімкненим.
- На останньому слайді воно зупиняється, якщо не задано
loop. Кнопка відтворення й паузи серед елементів керування показує стан і дає глядачеві його перемкнути.
Власний renderControls отримує isAutoAdvancing і, лише коли autoAdvance увімкнено, onToggleAutoAdvance — рендерте PlayPauseButton, коли він є. Сам таймінг — це createSlideshowController з @reelkit/core; див. посібник з автовідтворення, щоб вбудувати ту саму поведінку у власний плеєр, і API ядра щодо його методів.
Завантаження
Задайте showDownload, щоб додати до елементів керування кнопку завантаження. Вона зберігає активний елемент за його downloadUrl, а якщо його немає — за src, під іменем downloadName, а якщо його немає — під останнім сегментом URL.
import { LightboxOverlay, type LightboxItem } from '@reelkit/react-lightbox';
const images: LightboxItem[] = [
{
src: 'https://cdn.example.com/beach-1600.jpg',
// Save the original rather than the resized preview
downloadUrl: 'https://cdn.example.com/beach-original.jpg',
downloadName: 'beach.jpg',
},
];
<LightboxOverlay
isOpen={index !== null}
images={images}
initialIndex={index ?? 0}
onClose={() => setIndex(null)}
showDownload
/>;Файл завантажується через fetch і зберігається, бо браузер ігнорує атрибут download на посиланні на інше джерело й відкрив би файл замість збереження. Коли fetch не вдається — зазвичай це сервер на іншому джерелі, що не надсилає заголовків CORS, — файл відкривається в новій вкладці, де глядач усе ще може його зберегти. Віддавайте файли з Access-Control-Allow-Origin, щоб зберігати одним кліком. Поки триває завантаження, кнопка вимкнена.
Власний renderControls отримує isDownloading і, лише коли showDownload увімкнено, onDownload — рендерте DownloadButton, коли він є. Помічник — це downloadFile з @reelkit/core; див. посібник із завантаження.
Довідник API
Пропси LightboxOverlay
LightboxOverlayProps
| Prop | Тип | Типове значення | Опис |
|---|---|---|---|
isOpen | boolean | required | Керує видимістю Lightbox. Для стану відкриття, керованого URL, беріть окремий LightboxUrlOverlay — дивіться розділ про стан в URL нижче. |
images | LightboxItem[] | required | Масив зображень для показу |
ariaLabel | string | 'Image gallery' | Доступна назва області діалогу; екранні читачі оголошують її, коли Lightbox відкривається |
initialIndex | number | 0 | Початковий індекс зображення |
transitionFn | TransitionTransformFn | slideTransition | Функція переходу між слайдами. Імпортуйте вбудовану (slideTransition, flipTransition, lightboxFadeTransition, lightboxZoomTransition) або передайте власну. Якщо не задано, використовується slideTransition. |
autoAdvance | boolean | false | Грати галерею як слайдшоу. Див. Автоперехід. |
autoAdvanceInterval | number | 5000 | Скільки кожне зображення лишається на екрані під час слайдшоу, у мілісекундах, з моменту його завантаження |
showDownload | boolean | false | Додає кнопку завантаження для активного елемента. Див. Завантаження. |
apiRef | MutableRefObject<ReelApi> | - | Ref для доступу до API Reel |
renderControls | (props: ControlsRenderProps) => ReactNode | - | Власні елементи керування, замінюють стандартну кнопку закриття, лічильник і перемикач повного екрана |
renderNavigation | (props: NavigationRenderProps) => ReactNode | - | Власна навігація, замінює стандартні стрілки вперед і назад |
renderInfo | (props: InfoRenderProps) => ReactNode | - | Власний інформаційний оверлей, замінює стандартний градієнт із заголовком та описом. Поверніть null, щоб сховати. |
renderSlide | (props: SlideRenderProps) => ReactNode | null | - | Власний рендеринг слайда. Отримує { item, index, size, isActive, autoAdvance, onReady, onWaiting, onError, onEnded }. Поверніть null, щоб лишити стандартний. |
renderLoading | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | Власний індикатор завантаження, замінює стандартний |
renderError | (props: { item: LightboxItem; activeIndex: number }) => ReactNode | - | Власний індикатор помилки, замінює стандартний значок |
Пропси LightboxUrlOverlay
LightboxUrlOverlayProps
Приймає всі візуальні та поведінкові пропси вище, крім isOpen, and replaces it with a controller. initialIndex тут ігнорується — слайд обирає position контролера, тож передане поруч значення перезаписувалося б на кожному відкритті.
| Prop | Тип | Типове значення | Опис |
|---|---|---|---|
controller | UrlStateController | required | Контролер із useOverlayUrlState. Його position вирішує, чи оверлей відкритий і який слайд показує; оверлей записує через нього назад на зміну слайда та на закриття. |
Колбеки
| Prop | Тип | Опис |
|---|---|---|
onClose | () => void | Викликається, коли Lightbox закривається. Обов’язковий у LightboxOverlay (стан відкриття ваш, тож закриття обробляєте ви); необов’язковий у LightboxUrlOverlay, де закриттям керує URL — передавайте лише щоб зреагувати після закриття. |
onSlideChange | (index: number) => void | Викликається після зміни слайда |
Пропси Reel (передані далі)
Ці пропси передаються далі в компонент Reel. Їхній тип — ReelProxyProps, підмножина пропсів Reel, яку оверлей передає далі; типізуйте ним обгортку, що їх прокидає.
| Prop | Тип | Типове значення | Опис |
|---|---|---|---|
loop | boolean | false | Вмикає нескінченний цикл |
enableNavKeys | boolean | true | Вмикає навігацію з клавіатури |
enableWheel | boolean | true | Вмикає навігацію колесом миші |
wheelDebounceMs | number | 200 | Тривалість дебаунсу колеса (мс) |
transitionDuration | number | 300 | Тривалість анімації переходу (мс) |
swipeDistanceFactor | number | 0.12 | Поріг свайпу (0–1) |
swipeToCloseDirection | 'up' | 'down' | 'up' | Напрямок жесту свайпу для закриття на мобільних |
Types
LightboxItem
interface LightboxItem {
src: string;
type?: 'image' | 'video'; // defaults to 'image'
poster?: string; // thumbnail for video items
title?: string;
description?: string;
width?: number;
height?: number;
downloadUrl?: string; // what the download button saves; defaults to src
downloadName?: string; // file name; defaults to the URL's last segment
}ControlsRenderProps
interface ControlsRenderProps {
item: LightboxItem;
activeIndex: number;
count: number;
isFullscreen: boolean;
isAutoAdvancing: boolean;
isDownloading: boolean;
onClose: () => void;
onToggleFullscreen: () => void;
onToggleAutoAdvance?: () => void; // only with autoAdvance
onDownload?: () => void; // only with showDownload
}NavigationRenderProps
interface NavigationRenderProps {
item: LightboxItem;
activeIndex: number;
count: number;
onPrev: () => void;
onNext: () => void;
}SlideRenderProps
interface SlideRenderProps {
item: LightboxItem;
index: number;
size: [number, number];
isActive: boolean;
autoAdvance: boolean; // a video should play once, not loop
onReady: () => void;
onWaiting: () => void;
onError: () => void;
onEnded: () => void; // a video played to its end
}InfoRenderProps
interface InfoRenderProps {
item: LightboxItem;
index: number;
}Sub-Components
Повторно використовувані підкомпоненти для складання власних елементів керування через renderControls.
CloseButton
Стандартна кнопка закриття у вигляді хрестика. Пропси (CloseButtonProps): onClick, необов’язкові className і style.
import { CloseButton } from '@reelkit/react-lightbox';
<CloseButton onClick={onClose} />Counter
Значок лічильника зображень на кшталт «1 / 3». Пропси (CounterProps): currentIndex (від нуля), count, необов’язкові className і style.
import { Counter } from '@reelkit/react-lightbox';
<Counter currentIndex={activeIndex} count={count} />FullscreenButton
Кнопка перемикання повного екрана (іконка Maximize або Minimize). Пропси (FullscreenButtonProps): isFullscreen, onToggle, необов’язкові className і style.
import { FullscreenButton } from '@reelkit/react-lightbox';
<FullscreenButton isFullscreen={isFullscreen} onToggle={onToggleFullscreen} />SoundButton
Кнопка перемикання звуку для слайдів-відео (іконка Volume2 або VolumeX). Автоматично входить у renderControls from useVideoSlideRenderer. Для окремого використання у власних елементах керування доступ до стану звуку — через useSoundState. Пропси (SoundButtonProps): isMuted, onToggle, необов’язкові className і style.
import { SoundButton } from '@reelkit/react-lightbox';
import { useSoundState } from '@reelkit/react';
// Inside a component wrapped in SoundProvider:
function CustomControls({ onClose }) {
const soundState = useSoundState();
return (
<div>
<SoundButton
isMuted={soundState.muted.value}
onToggle={soundState.toggle}
/>
<button onClick={onClose}>Close</button>
</div>
);
}PlayPauseButton
Перемикач слайдшоу (іконка Play або Pause, aria-pressed під час відтворення). Пропси (PlayPauseButtonProps): isPlaying, onToggle, необов’язкові className і style. Стандартні елементи керування показують її, коли autoAdvance увімкнено.
import {
LightboxOverlay,
PlayPauseButton,
CloseButton,
} from '@reelkit/react-lightbox';
<LightboxOverlay
autoAdvance
renderControls={({ isAutoAdvancing, onToggleAutoAdvance, onClose }) => (
<>
{onToggleAutoAdvance && (
<PlayPauseButton
isPlaying={isAutoAdvancing}
onToggle={onToggleAutoAdvance}
/>
)}
<CloseButton onClick={onClose} />
</>
)}
{...props}
/>;DownloadButton
Кнопка завантаження (іконка Download), вимкнена, поки isDownloading. Пропси (DownloadButtonProps): isDownloading, onClick, необов’язкові className і style. Стандартні елементи керування показують її, коли showDownload увімкнено.
import {
LightboxOverlay,
DownloadButton,
CloseButton,
} from '@reelkit/react-lightbox';
<LightboxOverlay
showDownload
renderControls={({ isDownloading, onDownload, onClose }) => (
<>
{onDownload && (
<DownloadButton isDownloading={isDownloading} onClick={onDownload} />
)}
<CloseButton onClick={onClose} />
</>
)}
{...props}
/>;Hooks
useVideoSlideRenderer
Хук для підтримки відео за бажанням. Повертає renderSlide, renderControls, та SoundProvider — загорніть оверлей у SoundProvider і передайте функції рендерингу. Тип результату — UseVideoSlideRendererResult, який також містить hasVideo: true, коли хоча б один елемент має type: 'video'.
Його renderSlide малює відео-елементи через LightboxVideoSlide — слайд-відео, що грає через один спільний елемент <video>, тож звук працює й на iOS, і запам’ятовує позицію кожного відео. Рендерте його самостійно лише з власного renderSlide. Його пропси (LightboxVideoSlideProps): src, poster, isActive, size, slideKey (ключ для збереженої позиції та захопленого кадру), loop (типово true) і колбеки onPlaying, onWaiting, onError, onEnded.
import { useVideoSlideRenderer } from '@reelkit/react-lightbox';
const { renderSlide, renderControls, SoundProvider, hasVideo } =
useVideoSlideRenderer(items);
// SoundProvider — wrap LightboxOverlay in this for mute/unmute support
// renderSlide — pass to LightboxOverlay's renderSlide prop
// renderControls — pass to LightboxOverlay's renderControls prop
// (includes Counter, FullscreenButton, SoundButton, CloseButton)
// hasVideo — true if items contain at least one videouseFullscreen
Хук для керування станом повного екрана з кросбраузерною підтримкою.
import { useRef } from 'react';
import { useFullscreen } from '@reelkit/react';
function CustomLightbox() {
const containerRef = useRef<HTMLDivElement>(null);
const [isFullscreen, requestFullscreen, exitFullscreen, toggleFullscreen] =
useFullscreen({ ref: containerRef });
return (
<div ref={containerRef}>
<button onClick={toggleFullscreen}>
{isFullscreen.value ? 'Exit Fullscreen' : 'Enter Fullscreen'}
</button>
</div>
);
}Переходи
Передайте будь-яку TransitionTransformFn via the transitionFn Якщо імпортувати лише той перехід, який використовуєте, решту збирач прибере через tree-shaking. Типово — slideTransition , коли не задано.
| Function | From | Опис |
|---|---|---|
slideTransition | @reelkit/react-lightbox | Звичайний горизонтальний зсув (типово) |
lightboxFadeTransition | @reelkit/react-lightbox | Плавне перетікання між зображеннями |
flipTransition | @reelkit/react-lightbox | Ефект 3D-перевороту картки |
lightboxZoomTransition | @reelkit/react-lightbox | Наближення від меншого до звичайного розміру |
import {
LightboxOverlay,
lightboxFadeTransition,
} from '@reelkit/react-lightbox';
<LightboxOverlay
isOpen={isOpen}
images={images}
initialIndex={0}
onClose={handleClose}
transitionFn={lightboxFadeTransition}
/>Власна функція переходу
Напишіть власну TransitionTransformFn and pass it via transitionFn. Сигнатура повторює переходи слайдера в ядрі.
import { LightboxOverlay } from '@reelkit/react-lightbox';
import type { TransitionTransformFn } from '@reelkit/react';
const customFade: TransitionTransformFn = (offset, size) => ({
transform: `translate3d(${offset * size[0]}px, 0, 0)`,
opacity: 1 - Math.min(Math.abs(offset), 1),
});
<LightboxOverlay
isOpen={isOpen}
images={images}
transitionFn={customFade}
onClose={() => setIsOpen(false)}
/>Класи CSS
Усі елементи інтерфейсу використовують звичайні класи CSS (не CSS-модулі), які можна перекрити селекторами вищої специфічності в таблиці стилів, підключеній після @reelkit/react-lightbox/styles.css. Для змін кольору, розміру та z-index краще беріть власні властивості CSS, описані в розділі Theming нижче.
| Class | Component | Опис |
|---|---|---|
.rk-lightbox-overlay | Overlay | Кореневий контейнер (повноекранне тло) |
.rk-lightbox-spinner | Overlay | Стандартний індикатор завантаження |
.rk-lightbox-img-error | Overlay | Контейнер стану помилки (зіпсоване зображення чи відео) |
.rk-lightbox-img-error-text | Overlay | Текст стану помилки |
.rk-lightbox-swipe-hint | Overlay | Підказка про свайп на мобільних |
.rk-lightbox-controls-left | Controls | Контейнер елементів керування вгорі ліворуч |
.rk-lightbox-btn | Controls | Кнопки керування (повний екран тощо) |
.rk-lightbox-play-pause | Controls | Кнопка відтворення й паузи слайдшоу (разом із .rk-lightbox-btn) |
.rk-lightbox-download | Controls | Кнопка завантаження (разом із .rk-lightbox-btn) |
.rk-lightbox-close | Controls | Кнопка закриття |
.rk-lightbox-counter | Controls | Значок лічильника зображень |
.rk-lightbox-nav | Навігація | Стрілки навігації (обидві) |
.rk-lightbox-nav-prev | Навігація | Стрілка назад |
.rk-lightbox-nav-next | Навігація | Стрілка вперед |
.rk-lightbox-info | Info | Контейнер заголовка й опису |
.rk-lightbox-title | Info | Заголовок зображення |
.rk-lightbox-description | Info | Опис зображення |
.rk-lightbox-slide | Slide | Контейнер слайда |
.rk-lightbox-img | Slide | Елемент зображення |
.rk-lightbox-video-container | VideoSlide | Контейнер слайда-відео (за бажанням) |
.rk-lightbox-video-element | VideoSlide | Елемент відео (за бажанням) |
.rk-lightbox-video-poster | VideoSlide | Постер відео (за бажанням) |
Theming
Кожен колір, розмір, z-index і перехід живе у власній властивості CSS. Перевизначайте одну чи кілька на :root (або на будь-якому предку Lightbox), щоб змінити тему, не чіпаючи код компонентів.
| Token | Типове значення | Controls |
|---|---|---|
--rk-lightbox-overlay-bg | #000 | Колір повноекранного фону |
--rk-lightbox-overlay-z | 9999 | z-index оверлею |
--rk-lightbox-top-shade-height | 80px | Висота верхнього градієнтного затемнення |
--rk-lightbox-top-shade-bg | linear-gradient(rgba(0,0,0,0.6), transparent) | Колір верхнього градієнтного затемнення |
--rk-lightbox-edge-padding | 16px | Відступ від краю для кнопок закриття, навігації та елементів керування зліва вгорі |
--rk-lightbox-controls-gap | 12px | Відстань між елементами керування зліва вгорі |
--rk-lightbox-transition | 0.2s | Тривалість переходу кнопки під час наведення |
--rk-lightbox-blur | 8px | Радіус розмиття фону для кнопок і плашок |
--rk-lightbox-btn-bg | rgba(0, 0, 0, 0.5) | Типовий фон кнопок закриття, навігації та малих кнопок |
--rk-lightbox-btn-bg-hover | rgba(255, 255, 255, 0.2) | Фон кнопок закриття, навігації та малих кнопок під час наведення |
--rk-lightbox-btn-fg | #fff | Колір іконок кнопок закриття, навігації та малих кнопок |
--rk-lightbox-btn-size | 36px | Розмір малої кнопки (перемикач повноекранного режиму тощо) |
--rk-lightbox-btn-disabled-opacity | 0.5 | Непрозорість вимкненої малої кнопки (під час завантаження) |
--rk-lightbox-close-size | 40px | Розмір кнопки закриття |
--rk-lightbox-nav-size | 48px | Розмір стрілок «назад» і «далі» |
--rk-lightbox-nav-opacity | 0.7 | Непрозорість стрілок «назад» і «далі» у спокої |
--rk-lightbox-counter-fg | #fff | Колір тексту лічильника |
--rk-lightbox-counter-bg | rgba(0, 0, 0, 0.5) | Фон плашки лічильника |
--rk-lightbox-counter-size | 14px | Розмір шрифту лічильника |
--rk-lightbox-counter-padding | 6px 12px | Внутрішній відступ плашки лічильника |
--rk-lightbox-counter-radius | 20px | Радіус заокруглення плашки лічильника |
--rk-lightbox-spinner-size | 28px | Типова ширина й висота спінера |
--rk-lightbox-spinner-track | rgba(255, 255, 255, 0.2) | Колір доріжки спінера |
--rk-lightbox-spinner-fg | #fff | Колір індикатора спінера |
--rk-lightbox-spinner-duration | 0.8s | Тривалість обертання спінера |
--rk-lightbox-error-fg | rgba(255, 255, 255, 0.4) | Колір іконки й тексту помилки |
--rk-lightbox-error-text-size | 13px | Розмір шрифту повідомлення про помилку |
--rk-lightbox-info-bg | linear-gradient(transparent, rgba(0,0,0,0.8)) | Градієнт затемнення під підписом |
--rk-lightbox-info-padding | 24px | Внутрішній відступ підпису |
--rk-lightbox-title-size | 18px | Розмір шрифту заголовка |
--rk-lightbox-description-size | 14px | Розмір шрифту опису |
--rk-lightbox-info-fg | #fff | Колір тексту підпису |
--rk-lightbox-hint-fg | rgba(255, 255, 255, 0.5) | Колір тексту підказки про свайп |
--rk-lightbox-hint-bg | rgba(0, 0, 0, 0.3) | Фон плашки підказки про свайп |
--rk-lightbox-hint-duration | 3s | Загальна тривалість появи та згасання підказки про свайп |
--rk-lightbox-video-bg | #000 | Фон смуг навколо <video> |