Reel Player
@reelkit/react-reel-player से बना, Instagram Reel/TikTok जैसा फ़ुलस्क्रीन वीडियो प्लेयर कंपोनेंट।
फ़ीचर
इंस्टॉलेशन
npm install @reelkit/react-reel-player @reelkit/react lucide-reactstyles import करना न भूलें:
import '@reelkit/react-reel-player/styles.css';आइकन
डिफ़ॉल्ट कंट्रोल आइकन के लिए lucide-react इस्तेमाल करते हैं। अगर आप कोई और आइकन लाइब्रेरी पसंद करते हैं, तो अपने कंट्रोल देने के लिए renderControls और renderNavigation इस्तेमाल करें।
क्विक स्टार्ट
ReelPlayerOverlay कंपोनेंट फ़ुलस्क्रीन प्लेयर overlay रेंडर करता है। ContentItem objects का array पास करें और isOpen से दिखना नियंत्रित करें।
import { useState } from 'react';
import { ReelPlayerOverlay, type ContentItem } from '@reelkit/react-reel-player';
import '@reelkit/react-reel-player/styles.css';
const content: ContentItem[] = [
{
id: '1',
media: [{
id: 'v1',
type: 'video',
src: 'https://example.com/video.mp4',
poster: 'https://example.com/poster.jpg',
aspectRatio: 9 / 16,
}],
author: { name: 'John Doe', avatar: 'https://example.com/avatar.jpg' },
likes: 1234,
description: 'Amazing video!',
},
];
function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button onClick={() => setIsOpen(true)}>Open Player</button>
<ReelPlayerOverlay
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={content}
/>
</>
);
}import { useState } from 'react';
import { ReelPlayerOverlay, type ContentItem } from '@reelkit/react-reel-player';
import '@reelkit/react-reel-player/styles.css';
const content: ContentItem[] = [
{
id: '1',
media: [{
id: 'v1',
type: 'video',
src: '/cdn/samples/videos/video-01.mp4',
poster: '/cdn/samples/videos/video-poster-01.jpg',
aspectRatio: 16 / 9,
}],
author: { name: 'Alex Johnson', avatar: '/cdn/samples/avatars/avatar-01.jpg' },
likes: 1234,
description: 'Amazing sunset vibes',
},
{
id: '2',
media: [{
id: 'img1',
type: 'image',
src: '/cdn/samples/images/image-01.jpg',
aspectRatio: 2 / 3,
}],
author: { name: 'Sarah Miller', avatar: '/cdn/samples/avatars/avatar-02.jpg' },
likes: 5678,
description: 'Nature at its finest',
},
{
id: '3',
media: [{
id: 'v2',
type: 'video',
src: '/cdn/samples/videos/video-04.mp4',
poster: '/cdn/samples/videos/video-poster-04.jpg',
aspectRatio: 16 / 9,
}],
author: { name: 'James Wilson', avatar: '/cdn/samples/avatars/avatar-03.jpg' },
likes: 3456,
description: 'City life adventures',
},
{
id: '4',
media: [
{ id: 'img2', type: 'image', src: '/cdn/samples/images/image-02.jpg', aspectRatio: 2 / 3 },
{ id: 'img3', type: 'image', src: '/cdn/samples/images/image-03.jpg', aspectRatio: 3 / 4 },
],
author: { name: 'Emma Davis', avatar: '/cdn/samples/avatars/avatar-04.jpg' },
likes: 8901,
description: 'Travel moments',
},
{
id: '5',
media: [{
id: 'img4',
type: 'image',
src: '/cdn/samples/images/image-04.jpg',
aspectRatio: 2 / 3,
}],
author: { name: 'Michael Brown', avatar: '/cdn/samples/avatars/avatar-05.jpg' },
likes: 2345,
description: 'Golden hour magic',
},
{
id: '6',
media: [{
id: 'v3',
type: 'video',
src: '/cdn/samples/videos/video-05.mp4',
poster: '/cdn/samples/videos/video-poster-05.jpg',
aspectRatio: 16 / 9,
}],
author: { name: 'Alex Johnson', avatar: '/cdn/samples/avatars/avatar-01.jpg' },
likes: 7890,
description: 'Living the moment',
},
];
export default function App() {
const [isOpen, setIsOpen] = useState(false);
const [initialIndex, setInitialIndex] = useState(0);
return (
<div style={{ padding: 16, background: '#0f172a', minHeight: '100vh' }}>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6 }}>
{content.map((item, i) => (
<button
key={item.id}
onClick={() => { setInitialIndex(i); setIsOpen(true); }}
style={{
position: 'relative', aspectRatio: '9 / 16', borderRadius: 8,
overflow: 'hidden', border: 'none', padding: 0, cursor: 'pointer',
background: '#1e293b',
}}
>
<img
src={item.media[0].poster || item.media[0].src}
style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
/>
</button>
))}
</div>
<ReelPlayerOverlay
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={content}
initialIndex={initialIndex}
/>
</div>
);
}पूरे फ़्लो के लिए, यानी थंबनेल ग्रिड जो प्लेयर खोलता है और दर्शक को वहीं लौटाता है जहाँ वह रुका था, ग्रिड से प्लेयर तक रेसिपी देखें।
कस्टमाइज़ेशन
Generic कंटेंट टाइप
BaseContentItem को extend करके कस्टम डेटा टाइप इस्तेमाल करें:
import { ReelPlayerOverlay, type BaseContentItem } from '@reelkit/react-reel-player';
interface MyItem extends BaseContentItem {
title: string;
username: string;
}
const items: MyItem[] = [
{
id: '1',
media: [{ id: 'v1', type: 'video', src: '/video.mp4', aspectRatio: 9/16 }],
title: 'My Video',
username: '@user',
},
];
<ReelPlayerOverlay<MyItem>
isOpen={isOpen}
onClose={handleClose}
content={items}
renderSlideOverlay={(item) => (
<div style={{ position: 'absolute', bottom: 16, left: 16, color: '#fff' }}>
<strong>{item.username}</strong>
<p>{item.title}</p>
</div>
)}
/>कस्टम स्लाइड overlay
बिल्ट-इन स्लाइड overlay की जगह हर स्लाइड पर अपना कंटेंट दिखाएँ:
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderSlideOverlay={(item, index, isActive) => (
<div style={{
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
padding: 16,
background: 'linear-gradient(transparent, rgba(0,0,0,0.8))',
color: '#fff',
}}>
<h3>{item.author.name}</h3>
<p>{item.description}</p>
<span>Slide {index + 1} {isActive ? '(active)' : ''}</span>
</div>
)}
/>बिना मीडिया वाली स्लाइड
कस्टम कंटेंट (जैसे CTA कार्ड) डालने के लिए renderSlide इस्तेमाल करें। डिफ़ॉल्ट पर लौटने के लिए null लौटाएँ:
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderSlide={({ index, size }) => {
// CTA card on last slide
if (index === content.length - 1) {
return (
<div style={{
width: size[0],
height: size[1],
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(135deg, #667eea, #764ba2)',
color: '#fff',
}}>
<div style={{ textAlign: 'center' }}>
<h2>Follow for more!</h2>
<button>Subscribe</button>
</div>
</div>
);
}
// Fall back to default MediaSlide + overlay
return null;
}}
/>कस्टम कंट्रोल
दोबारा इस्तेमाल होने वाले sub-components को अपनी चीज़ों के साथ जोड़ें:
import {
ReelPlayerOverlay,
CloseButton,
SoundButton,
} from '@reelkit/react-reel-player';
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderControls={({ onClose, content, activeIndex }) => (
<>
<CloseButton onClick={onClose} />
<SoundButton />
<button
onClick={() => share(content[activeIndex])}
style={{
position: 'absolute',
bottom: 60,
right: 16,
zIndex: 10,
}}
>
Share
</button>
</>
)}
/>कस्टम टाइमलाइन
renderTimeline के ज़रिए बिल्ट-इन प्लेबैक बार की जगह अपना scrub UI लगाएँ। Callback सिर्फ़ तब चलता है जब overlay के नियम डिफ़ॉल्ट बार रेंडर करते (वही timeline मोड + timelineMinDurationSeconds logic), इसलिए आपको इसे दोबारा नहीं लिखना पड़ता। नीचे से सटी स्थिति, safe-area padding और टच डिवाइस पर स्लाइड overlay से दूरी पाने के लिए अपने root पर .rk-reel-timeline class दोबारा इस्तेमाल करें।
import { useRef, useEffect } from 'react';
import { ReelPlayerOverlay } from '@reelkit/react-reel-player';
import { Observe } from '@reelkit/react';
function CustomTimelineBar({ timelineState }) {
const trackRef = useRef(null);
useEffect(() => {
if (!trackRef.current) return;
// Pointer + keyboard scrub wiring, same as the built-in bar.
return timelineState.bindInteractions(trackRef.current);
}, [timelineState]);
return (
<div className="rk-reel-timeline" style={{ padding: '0 16px' }}>
<Observe signals={[timelineState.progress, timelineState.currentTime]}>
{() => (
<div
ref={trackRef}
role="slider"
aria-valuenow={timelineState.currentTime.value}
style={{ height: 6, background: 'rgba(255,255,255,0.2)' }}
>
<div style={{
width: `${timelineState.progress.value * 100}%`,
height: '100%',
background: 'linear-gradient(90deg, #6366f1, #ec4899)',
}} />
</div>
)}
</Observe>
</div>
);
}
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
timeline="always"
renderTimeline={({ timelineState }) => (
<CustomTimelineBar timelineState={timelineState} />
)}
/>कस्टम नेविगेशन
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderNavigation={({ onPrev, onNext, activeIndex, count }) => (
<div style={{ position: 'absolute', right: 16, top: '50%', transform: 'translateY(-50%)' }}>
<button onClick={onPrev} disabled={activeIndex === 0}>Up</button>
<span>{activeIndex + 1}/{count}</span>
<button onClick={onNext} disabled={activeIndex === count - 1}>Down</button>
</div>
)}
/>कस्टम nested नेविगेशन
मल्टी-मीडिया स्लाइड (हॉरिज़ॉन्टल carousel) के अंदर बाएँ/दाएँ तीरों की जगह अपना नेविगेशन लगाएँ:
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderNestedNavigation={({ onPrev, onNext, activeIndex, count }) => (
<div style={{
position: 'absolute',
bottom: 48,
left: 0,
right: 0,
display: 'flex',
justifyContent: 'center',
gap: 8,
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>
)}
/>कस्टम nested स्लाइड
renderNestedSlide से मल्टी-मीडिया carousels के अंदर की अलग-अलग स्लाइड कस्टमाइज़ करें। डिफ़ॉल्ट ImageSlide/VideoSlide को लपेटने के लिए props.defaultContent इस्तेमाल करें, या उसे पूरी तरह बदल दें:
// Wrap default slides with rounded corners
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderNestedSlide={({ defaultContent }) => (
<div style={{ borderRadius: 16, overflow: 'hidden' }}>
{defaultContent}
</div>
)}
/>
// Fully custom nested slide for images
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderNestedSlide={({ item, size, isActive, slideKey, onVideoRef, defaultContent }) => {
if (item.type === 'video') return defaultContent; // keep default video
return (
<ImageSlide
src={item.src}
size={size}
imgStyle={{ objectFit: 'contain' }}
style={{ backgroundColor: '#111' }}
/>
);
}}
/>URL स्टेट
लाइव डेमो देखें →ReelPlayerUrlOverlay एक अलग कंपोनेंट है जिसकी खुले होने की स्टेट address bar में रहती है। @reelkit/react के useOverlayUrlState से कंट्रोलर बनाएँ और उसे controller के रूप में दें: जब parameter किसी स्लाइड का नाम लेता है तो प्लेयर खुद खुलता है, और उसके हटते ही बंद हो जाता है। लिंक शेयर किए जा सकते हैं, और back बटन पेज छोड़ने के बजाय प्लेयर बंद करता है।
बिल्ट-इन keys
आप बिल्ट-इन key से स्लाइडों को पहचान सकते हैं — कंट्रोलर में urlIndexKey (स्थिति से) या urlStableIdKey (स्थिर id से) spread करें — दोनों @reelkit/react से भी export होते हैं। URL स्टेट गाइड और Core API देखें।
import { useOverlayUrlState, urlIndexKey, urlStableIdKey } from '@reelkit/react';
import { ReelPlayerUrlOverlay } from '@reelkit/react-reel-player';
import { Link } from 'react-router-dom';
const reel = useOverlayUrlState({
param: 'reel',
...urlIndexKey(() => content.length),
});
// Opening is a link — the overlay reads the URL and opens itself.
{content.map((item, i) => (
<Link key={item.id} to={`?reel=${i}`}>
<img src={getThumbnail(item)} />
</Link>
))}
<ReelPlayerUrlOverlay controller={reel} content={content} />useOverlayUrlState के सारे विकल्प — param, adapter, codec, locator — React API रेफ़रेंस में हैं। पूरी जानकारी React गाइड में है।
- खोलने पर history में एक entry जुड़ती है। फ़ीड स्वाइप करना उसे बदल देता है, इसलिए N स्वाइप कोई entry नहीं जोड़ते और back का एक कदम हमेशा प्लेयर छोड़ देता है। Back बंद करता है; स्लाइड नहीं बदलता।
- Back सिर्फ़ तब बंद करता है जब प्लेयर ऐप के अंदर से खोला गया हो — लिंक ने entry जोड़ी हो। नए tab में सीधे खोले गए शेयर किए गए लिंक के पीछे कोई history नहीं होती, इसलिए ब्राउज़र का back साइट छोड़ देता है; ✕ बटन या Escape parameter को उसी जगह हटाकर पेज पर बना रहता है।
- सीधा लिंक
?reel=3लोड होते ही प्लेयर को उसी स्लाइड पर खोलता है। - किसी स्लाइड का नाम न लेने वाला parameter — पुराना bookmark, हाथ से बदली value — URL से हटा दिया जाता है, ताकि address bar ऐसी स्लाइड का दावा न करता रहे जो खुल ही नहीं सकती।
- डिफ़ॉल्ट रूप से parameter सिर्फ़ वर्टिकल पोस्ट को पहचानता है (
?reel=3)। मल्टी-मीडिया carousel का अंदरूनी मीडिया index भी रखने के लिए दो-अक्ष वाली key चुनें — नीचे देखें।
एक key या दो — URL की गहराई चुनें
वही ReelPlayerUrlOverlay दोनों रूप चलाता है; यह चलते समय कंट्रोलर की स्थिति से फ़र्क पहचानता है, इसलिए कोई mode prop नहीं है। कंट्रोलर बनाते समय key चुनें:
| Key | Wire | क्या रखता है |
|---|---|---|
urlIndexKey(…) | ?reel=3 | सिर्फ़ वर्टिकल पोस्ट। |
urlIndexTwoAxisKey(…) | ?reel=3.2 | पोस्ट और carousel का अंदरूनी मीडिया index। |
दोनों wire जानबूझकर अलग हैं — दो-अक्ष वाली key सख़्ती से बिंदु वाली है (3.0, कभी अकेला 3 नहीं), इसलिए एक-अक्ष वाला साधारण लिंक दूसरे रूप में decode नहीं होता। इसलिए ऐप में key बदलने से पहले शेयर किए गए सारे लिंक बेकार हो जाते हैं। एक रूप चुनें और उसी पर टिके रहें।
import { useOverlayUrlState, urlIndexTwoAxisKey } from '@reelkit/react';
const reel = useOverlayUrlState({
param: 'reel',
...urlIndexTwoAxisKey({
outerCount: () => content.length,
innerCounts: () => content.map((post) => post.media.length),
}),
});
// A link now names both axes: post 3, inner media 2.
<Link to="?reel=3.2">…</Link>
<ReelPlayerUrlOverlay controller={reel} content={content} />Routing वाला ऐप — adapter पास करें। router के पीछे history.pushState लिखने से उसकी location पुरानी पड़ जाती है, और उसका अगला नेविगेशन parameter गिरा देता है:
import { useReactRouterUrlAdapter } from '@reelkit/react/react-router-url-adapter';
const adapter = useReactRouterUrlAdapter();
const reel = useOverlayUrlState({
param: 'reel',
adapter,
...urlIndexKey(() => content.length),
});
<ReelPlayerUrlOverlay controller={reel} content={content} />स्थिर लिंक। Index स्थिति पर आधारित है, इसलिए फ़ीड का क्रम बदलते ही bookmark किया गया ?reel=3 कोई दूसरी पोस्ट खोलता है — फ़ीड के लिए यह अपवाद नहीं, आम बात है। urlStableIdKey हर पोस्ट की स्थिर id से key बनाता है और मौजूदा फ़ीड में ढूँढता है — आम मामले के लिए एक कॉल काफ़ी है।
const reel = useOverlayUrlState({
param: 'reel',
...urlStableIdKey({ items: () => content }),
});URL में id को base64url में encode करने के लिए hashCodec: base64UrlCodec पास करें — वापस पढ़ा जा सकने वाला छिपाव, cryptographic hash नहीं।
किसी दूसरे field (slug) से key बनाएँ, या locateAsync से अनंत फ़ीड के पेज लोड करें, और codec/locator खुद बनाएँ। दो अलग काम: codec पहचान को URL में लिखता है, locator ढूँढता है कि वह पहचान कहाँ है।
const reel = useOverlayUrlState({
param: 'reel',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => content.findIndex((x) => x.id === id),
identify: (index) => content[index].id,
},
});अनंत फ़ीड। locate synchronous है, इसलिए यह सिर्फ़ लोड हो चुकी पोस्ट के लिए जवाब दे सकता है — जिस फ़ीड में 20 पोस्ट लोड हुई हैं, उसकी पोस्ट 400 का शेयर किया गया लिंक खाली लौटता है। locateAsync विकल्प है, जो सिर्फ़ locate के न मिलने पर कॉल होता है।
छोटा रास्ता
आइटम की id से key बना रहे हैं? अपना codec और locator लिखना छोड़ें — locateAsync सीधे urlStableIdKey({ items, locateAsync }) को पास करें (न मिलने पर यह fetch करता है, फिर index लौटाता है)। नीचे का पूरा रूप किसी दूसरे field से key बनाने या पूरे नियंत्रण के लिए है।
const reel = useOverlayUrlState({
param: 'reel',
codec: { decode: (raw) => raw, encode: (id) => id },
locator: {
locate: (id) => content.findIndex((x) => x.id === id),
identify: (index) => content[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 post
setContent(loaded); // commit — the overlay renders from this state
return loaded.findIndex((x) => x.id === id); // wherever it landed
},
},
});locateAsyncके pending रहने तक प्लेयर बंद रहता है और parameter को छुआ नहीं जाता, इसलिए सीधा लिंक fetch के बाद भी बना रह ता है।nullया rejection parameter हटा देता है।- URL आगे बढ़ जाने, बंद होने या unmount के बाद आया जवाब फेंक दिया जाता है — धीमा fetch ऐसी स्लाइड नहीं खोल सकता जो किसी ने माँगी ही नहीं।
- pending रहने के दौरान कुछ भी रेंडर नहीं होता; लोडिंग की वह स्टेट पेज के पास पहले से है, इसलिए अपना skeleton रेंडर करें।
- कोई timeout नहीं है — प्लेयर नहीं जान सकता कि फ़ीड कितनी लंबी है। पेज ख़त्म होने पर
nullसे निपटाएँ, नहीं तो overlay हमेशा बंद रहेगा।
API रेफ़रेंस
ReelPlayerOverlayProps के props
ReelPlayerOverlayProps<T>
ReelPlayerOverlayProps, ReelPlayerOverlayBaseProps (दोनों overlay के साझा दिखावट और व्यवहार वाले props) और ReelPlayerControlledProps (isOpen और onClose) को मिलाकर बनता है। किसी भी overlay को रेंडर करने वाले wrapper को टाइप करने के लिए base टाइप इस्तेमाल करें।
| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
apiRef | MutableRefObject<ReelApi> | - | Reel API तक पहुँचने के लिए ref |
ariaLabel | string | 'Video player' | dialog क्षेत्र का सुलभ लेबल; overlay खुलने पर स्क्रीन रीडर इसे बोलते हैं |
aspectRatio | number | 9/16 (0.5625) | डेस्कटॉप पर प्लेयर कंटेनर का चौड़ाई/ऊँचाई अनुपात। 768px या उससे कम चौड़ाई पर प्लेयर हमेशा पूरा viewport लेता है। शून्य, ऋणात्मक या non-finite मान होने पर 9/16 इस्तेमाल होता है। |
content | T[] | ज़रूरी | कंटेंट आइटम का array (generic, डिफ़ॉल्ट ContentItem) |
initialIndex | number | 0 | शुरुआती स्लाइड index |
initialInnerIndex | number | 0 | अंदरूनी मीडिया index जिस पर खोलना है, सिर्फ़ शुरू में दिखने वाली पोस्ट के लिए — इससे दो-अक्ष वाला URL मल्टी-मीडिया पोस्ट की किसी खास इमेज पर सीधे ले जा सकता है। प्लेयर खुलने और यूज़र के नेविगेट करने के बाद अनदेखा होता है। |
isOpen | boolean | ज़रूरी | overlay का दिखना नियंत्रित करता है। URL से चलने वाली खुली स्टेट के लिए इसकी जगह अलग ReelPlayerUrlOverlay इस्तेमाल करें — नीचे URL स्टेट देखें। |
timeline | 'auto' | 'always' | 'never' | 'auto' | बिल्ट-इन प्लेबैक टाइमलाइन बार दिखाने का नियम। 'auto' सिर्फ़ timelineMinDurationSeconds से लंबे वीडियो पर रेंडर करता है; 'always' जब भी सक्रिय स्लाइड में वीडियो हो; 'never' बिल्ट-इन बार बंद करता है (पूरी तरह कस्टम बार के लिए renderTimeline इस्तेमाल करें)। |
timelineMinDurationSeconds | number | 30 | timeline='auto' में बिल्ट-इन बार दिखाने के लिए वीडियो की न्यूनतम अवधि (सेकंड)। इससे छोटी लूप होने वाली क्लिप पर बार नहीं दिखता। |
renderControls | (props: ControlsRenderProps) => ReactNode | - | कस्टम कंट्रोल, डिफ़ॉल्ट बंद + साउंड बटन की जगह लेते हैं |
renderError | (props: { item: T; activeIndex: number }) => ReactNode | - | कस्टम एरर इंडिकेटर, डिफ़ॉल्ट एरर आइकन की जगह लेता है |
renderLoading | (props: { item: T; activeIndex: number }) => ReactNode | - | कस्टम लोडिंग इंडिकेटर, डिफ़ॉल्ट wave loader की जगह लेता है |
renderNavigation | (props: NavigationRenderProps) => ReactNode | - | कस्टम नेविगेशन, डिफ़ॉल्ट वर्टिकल तीरों की जगह लेता है |
renderNestedNavigation | (props: NavigationRenderProps) => ReactNode | - | nested हॉरिज़ॉन्टल स्लाइडर (मल्टी-मीडिया पोस्ट) का कस्टम नेविगेशन, डिफ़ॉल्ट बाएँ/दाएँ तीरों की जगह लेता है |
renderNestedSlide | (props: NestedSlideRenderProps) => ReactNode | - | nested हॉरिज़ॉन्टल स्लाइडर के आइटम का कस्टम स्लाइड renderer। डिफ़ॉल्ट ImageSlide/VideoSlide को लपेटने या अंदर रखने के लिए props.defaultContent इस्तेमाल करें। renderSlide के उलट, null को डिफ़ॉल्ट पर लौटना नहीं माना जाता। |
renderSlide | (props: SlideRenderProps) => ReactNode | null | - | कस्टम स्लाइड रेंडरिंग। डिफ़ॉल्ट पर लौटने के लिए null लौटाएँ। डिफ़ ॉल्ट स्लाइड को लपेटने या अंदर रखने के लिए props.defaultContent इस्तेमाल करें। |
renderSlideOverlay | (item, index, isActive) => ReactNode | - | हर स्लाइड का कस्टम overlay, डिफ़ॉल्ट SlideOverlay की जगह लेता है। छिपाने के लिए null लौटाएँ। |
renderTimeline | (props: TimelineRenderProps) => ReactNode | - | कस्टम प्लेबैक टाइमलाइन बार। सिर्फ़ तब चलता है जब नियम डिफ़ॉल्ट बार रेंडर करते (वही auto/always/never + timelineMinDurationSeconds logic)। बिल्ट-इन <TimelineBar /> को लपेटने के लिए props.defaultContent इस्तेमाल करें; छिपाने के लिए null लौटाएँ। |
ReelPlayerUrlOverlay के props
ReelPlayerUrlOverlayProps<T>
ऊपर के सारे दिखावट और व्यवहार वाले props लेता है, सिवाय isOpen के, जिसकी जगह controller है: टाइप ReelPlayerOverlayBaseProps और ReelPlayerUrlControlledProps को मिलाकर बनता है। initialIndex अनदेखा होता है — स्लाइड कंट्रोलर की स्थिति चुनती है, इसलिए साथ में दी गई value हर बार खुलने पर बदल दी जाती है।
| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
controller | UrlStateController | required | useOverlayUrlState से मिला कंट्रोलर। इसकी स्थिति तय करती है कि overlay खुला है या नहीं और कौन-सी स्लाइड दिखती है; स्लाइड बदलने और बंद होने पर overlay इसी के ज़रिए वापस लिखता है। |
Callbacks
| Prop | टाइप | विवरण |
|---|---|---|
onClose | () => void | प्लेयर बंद होने पर कॉल होता है। ReelPlayerOverlay पर ज़रूरी (खुली स्टेट आपके पास है, इसलिए बंद करना आपको संभालना होगा); ReelPlayerUrlOverlay पर वैकल्पिक, जहाँ बंद करना URL चलाता है — इसे सिर्फ़ बंद होने के बाद कुछ करने के लिए पास करें। |
onSlideChange | (index: number) => void | स्लाइड बदलने के बाद कॉल होता है |
onInnerSlideChange | (outerIndex: number, innerIndex: number) => void | सक्रिय पोस्ट का अंदरूनी मीडिया index बदलने पर कॉल होता है — मल्टी-मीडिया पोस्ट के अंदर नेविगेशन पर, और बाहरी सक्रियता पर, सक्रिय हुई पोस्ट का मौजूदा अंदरूनी index बताते हुए (एक मीडिया वाली पोस्ट के लिए 0)। |
Reel के props (आगे भेजे गए)
ये props अंदर के Reel कंपोनेंट को आगे भेजे जाते हैं। इनका टाइप ReelProxyProps है, Reel props का वह हिस्सा जो overlay आगे भेजता है; इन्हें आगे भेजने वाले wrapper को टाइप करने के लिए इसका इस्तेमाल करें।
| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
enableNavKeys | boolean | true | कीबोर्ड नेविगेशन चालू करता है |
enableWheel | boolean | true | माउस व्हील से नेविगेशन चालू करता है |
loop | boolean | false | अनंत लूप चालू करता है |
swipeDistanceFactor | number | 0.12 | स्वाइप की सीमा (0-1) |
transitionDuration | number | 300 | ट्रांज़िशन एनिमेशन की अवधि (ms) |
wheelDebounceMs | number | 200 | व्हील debounce की अवधि (ms) |
टाइप
BaseContentItem
generic की सीमा तय करने वाला टाइप। ReelPlayerOverlay के साथ कस्टम डेटा टाइप इस्तेमाल करने के लिए इसे extend करें।
interface BaseContentItem {
id: string;
media: MediaItem[];
}ContentItem
interface ContentItem extends BaseContentItem {
author: {
name: string;
avatar: string;
};
likes: number;
description: string;
}MediaItem
interface MediaItem {
id: string;
type: 'image' | 'video';
src: string;
poster?: string;
aspectRatio: number;
}MediaType
type MediaType = 'image' | 'video';ControlsRenderProps<T>
interface ControlsRenderProps<T extends BaseContentItem> {
onClose: () => void;
soundState: SoundState;
activeIndex: number;
content: T[];
}NavigationRenderProps
interface NavigationRenderProps {
onPrev: () => void;
onNext: () => void;
activeIndex: number;
count: number;
}SlideRenderProps<T>
interface SlideRenderProps<T extends BaseContentItem> {
item: T;
index: number;
size: [number, number];
isActive: boolean;
slideKey: string;
onVideoRef?: (ref: HTMLVideoElement | null) => void;
innerSliderRef: MutableRefObject<ReelApi | null>;
onActiveMediaTypeChange?: (type: 'image' | 'video') => void;
renderNestedNavigation?: (props: NavigationRenderProps) => ReactNode;
enableWheel?: boolean;
defaultContent: ReactNode;
}NestedSlideRenderProps
interface NestedSlideRenderProps {
item: MediaItem;
index: number;
size: [number, number];
isActive: boolean;
isInnerActive: boolean;
slideKey: string;
onVideoRef?: (ref: HTMLVideoElement | null) => void;
defaultContent: ReactNode;
}SlideOverlayProps
interface SlideOverlayProps {
author?: { name: string; avatar: string };
description?: string;
likes?: number;
}ImageSlideProps
interface ImageSlideProps {
src: string;
size: [number, number];
className?: string;
style?: CSSProperties;
imgClassName?: string;
imgStyle?: CSSProperties;
}VideoSlideProps
interface VideoSlideProps {
src: string;
poster?: string;
aspectRatio: number;
size: [number, number];
isActive: boolean;
isInnerActive?: boolean; // default: true
slideKey: string;
onVideoRef?: (ref: HTMLVideoElement | null) => void;
className?: string;
style?: CSSProperties;
}CloseButtonProps
interface CloseButtonProps {
onClick: () => void;
className?: string;
style?: React.CSSProperties;
}SoundButtonProps
interface SoundButtonProps {
disabled?: boolean;
className?: string;
style?: React.CSSProperties;
}TimelineBarProps
interface TimelineBarProps {
className?: string;
style?: React.CSSProperties;
}TimelineRenderProps<T>
interface TimelineRenderProps<T extends BaseContentItem> {
item: T;
activeIndex: number;
timelineState: TimelineController;
defaultContent: ReactNode;
}सब-कंपोनेंट
कस्टम render props में जोड़कर इस्तेमाल करने के लिए export किए गए, दोबारा इस्तेमाल होने वाले हिस्से:
CloseButton
reel प्लेयर की डिफ़ॉल्ट styling वाला अलग बंद करने का बटन। renderControls के अंदर इस्तेमाल करें।
import { CloseButton } from '@reelkit/react-reel-player';
<CloseButton onClick={onClose} />
<CloseButton onClick={onClose} className="my-close-btn" style={{ top: 24, right: 24 }} />SoundButton
अलग साउंड टॉगल। SoundProvider के अंदर होना ज़रूरी है (ReelPlayerOverlay इसे अपने-आप देता है)।
import { SoundButton } from '@reelkit/react-reel-player';
<SoundButton />
<SoundButton disabled className="my-sound-btn" />TimelineBar
डिफ़ॉल्ट प्लेबैक scrub bar। सबसे पास के TimelineProvider (जो ReelPlayerOverlay के अंदर अपने-आप mount होता है) से पढ़ता है और track, buffered ranges, प्रोग्रेस भराव और scrub pill रेंडर करता है। --rk-reel-timeline-* custom properties से थीम करें, या renderTimeline से बदल दें।
import { TimelineBar } from '@reelkit/react-reel-player';
// Inside renderTimeline — wrap or augment the default bar:
<ReelPlayerOverlay
renderTimeline={({ defaultContent }) => (
<>
<MyTimecode />
{defaultContent}
</>
)}
/>
// Or render standalone inside a custom TimelineProvider tree:
<TimelineBar className="my-timeline" />SlideOverlay
लेखक, विवरण और लाइक दिखाने वाला डिफ़ॉल्ट gradient overlay। कंटेंट में ज़रूरी fields होने पर अपने-आप रेंडर होता है। इसे बदलने या छिपाने के लिए renderSlideOverlay इस्तेमाल करें।
import { SlideOverlay } from '@reelkit/react-reel-player';
<SlideOverlay
author={{ name: 'John', avatar: '/avatar.jpg' }}
description="Amazing content"
likes={12500}
/>ImageSlide
lazy loading और डिफ़ॉल्ट object-fit: cover वाली इमेज स्लाइड। अपनी styles के साथ कस्टम इमेज स्लाइड बनाने के लिए renderSlide के अंदर इस्तेमाल करें।
import { ImageSlide } from '@reelkit/react-reel-player';
// Default usage
<ImageSlide src="/photo.jpg" size={[400, 700]} />
// Custom styles
<ImageSlide
src="/photo.jpg"
size={[400, 700]}
className="my-image-slide"
style={{ backgroundColor: '#1a1a1a', borderRadius: 12 }}
imgStyle={{ objectFit: 'contain' }}
/>VideoSlide
iOS पर आवाज़ बनाए रखने के लिए साझा <video> एलिमेंट, poster फ़्रेम, स्थिति याद रखने और लोडिंग इंडिकेटर वाली वीडियो स्लाइड। SoundProvider के अंदर होना ज़रूरी है (ReelPlayerOverlay इसे अपने-आप देता है)।
import { VideoSlide } from '@reelkit/react-reel-player';
<VideoSlide
src="/video.mp4"
poster="/thumb.jpg"
aspectRatio={9 / 16}
size={[400, 700]}
isActive={true}
slideKey="slide-1"
style={{ borderRadius: 12 }}
/>कस्टम स्लाइड बनाना
सारा बिल्ट-इन व्यवहार (autoplay, poster कैप्चर, साउंड sync) बनाए रखते हुए मीडिया रेंडरिंग कस्टमाइज़ करने के लिए ImageSlide / VideoSlide के साथ renderSlide इस्तेमाल करें।
import {
ReelPlayerOverlay,
ImageSlide,
VideoSlide,
} from '@reelkit/react-reel-player';
<ReelPlayerOverlay
isOpen={isOpen}
onClose={handleClose}
content={content}
renderSlide={({ item, size, isActive, slideKey, onVideoRef }) => {
const media = item.media[0];
if (media.type === 'image') {
return (
<ImageSlide
src={media.src}
size={size}
imgStyle={{ objectFit: 'contain' }}
style={{ backgroundColor: '#111' }}
/>
);
}
if (media.type === 'video') {
return (
<VideoSlide
src={media.src}
poster={media.poster}
aspectRatio={media.aspectRatio}
size={size}
isActive={isActive}
slideKey={slideKey}
onVideoRef={onVideoRef}
style={{ borderRadius: 16 }}
/>
);
}
return null; // fallback to default
}}
/>कंटेंट लोडिंग और एरर हैंडलिंग
प्लेयर हर स्लाइड की लोडिंग और एरर स्टेट ट्रैक करता है। कंटेंट लोड होते समय wave loader दिखता है; टूटे मीडिया पर एरर आइकन दिखता है। एरर वाले URL cache हो जाते हैं, इसलिए दोबारा आने पर बिना फिर से कोशिश किए तुरंत एरर दिखता है।
Lifecycle callbacks
renderSlide इस्तेमाल करते समय लोडिंग इंडिकेटर नियंत्रित करने के लिए ये callbacks कॉल करें:
| Callback | कब कॉल करें |
|---|---|
onReady | इमेज लोड हुई या वीडियो चलने लगा। लोडिंग और एरर स्टेट साफ़ करता है। |
onWaiting | वीडियो चलते-चलते buffer हो रहा है। लोडिंग इंडिकेटर दिखाता है। |
onError | कंटेंट लोड नहीं हुआ। एरर overlay दिखाता है और URL को टूटा हुआ cache करता है। |
// Inside renderSlide — wire callbacks to your custom media
renderSlide={({ item, size, isActive, onReady, onWaiting, onError }) => (
<div style={{ width: size[0], height: size[1] }}>
{item.media[0].type === 'image' ? (
<img
src={item.media[0].src}
onLoad={onReady}
onError={onError}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
) : (
<video
src={item.media[0].src}
autoPlay={isActive}
onCanPlay={onReady}
onWaiting={onWaiting}
onError={onError}
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
/>
)}
</div>
)}कस्टम लोडिंग और एरर UI
डिफ़ॉल्ट wave loader और एरर आइकन की जगह अपने कंपोनेंट लगाएँ:
<ReelPlayerOverlay
isOpen={isOpen}
onClose={() => setIsOpen(false)}
content={content}
renderLoading={({ item, activeIndex }) => (
<div style={{
position: 'absolute', inset: 0, zIndex: 10,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: '#fff', fontSize: 14,
}}>
Loading slide {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 media</span>
</div>
)}
/>टाइमलाइन
Overlay सक्रिय वीडियो के ऊपर एक बिल्ट-इन प्लेबैक टाइमलाइन बार रेंडर करता है। TimelineMode टाइप वाला timeline prop रेंडरिंग तय करता है:
'auto'(डिफ़ॉल्ट): तब रेंडर होता है जब सक्रिय मीडिया ऐसा वीडियो हो जिसकी अवधिtimelineMinDurationSeconds(डिफ़ॉल्ट 30) से ज़्यादा हो। एक वीडियो वाली स्लाइड और मल्टी-मीडिया carousels दोनों पर काम करता है; बार सक्रिय nested आइटम के साथ चलता है और इमेज पर छिप जाता है।'always': जब भी सक्रिय स्लाइड में वीडियो हो, रेंडर होता है।'never': कभी रेंडर नहीं होता।renderTimelineसे अपना बार बनाएँ।
<ReelPlayerOverlay
isOpen={isOpen}
onClose={close}
content={items}
timeline="auto"
timelineMinDurationSeconds={30}
/>--rk-reel-timeline-* CSS custom properties (ऊँचाई, रंग, cursor का आकार) से थीम करें। पूरी तरह कस्टम scrub bar, timecode या प्रोग्रेस इंडिकेटर के लिए renderTimeline इस्तेमाल करें; callback को अंदर के TimelineController पर आधारित timelineState मिलता है।
overlay के अंदर रेंडर हुआ कंपोनेंट यही कंट्रोलर ख़ुद भी पढ़ सकता है: useTimelineState() उसे सबसे पास वाले TimelineProvider (जिसे overlay लगाता है) से लौटाता है और उसके बाहर एरर देता है; useTimelineStateOptional() वहाँ null लौटाता है, उन कंपोनेंट के लिए जो प्लेयर के बाहर भी रेंडर हो सकते हैं।
साउंड context
कस्टम implementation के लिए आप साउंड स्टेट तक पहुँच सकते हैं:
import { SoundProvider, useSoundState } from '@reelkit/react';
// ReelPlayerOverlay wraps itself in a SoundProvider.
// Access sound state inside custom controls:
function CustomControls() {
const soundState = useSoundState();
return (
<button onClick={soundState.toggle}>
{soundState.muted.value ? 'Unmute' : 'Mute'}
</button>
);
}CSS क्लास
सारी CSS क्लास साधारण हैं (CSS modules नहीं), इसलिए @reelkit/react-reel-player/styles.css के बाद लोड होने वाली stylesheet में ज़्यादा specificity वाले selectors से इन्हें निशाना बनाया जा सकता है। रंग, आकार और z-index बदलने के लिए नीचे थीमिंग हिस्से में बताई CSS custom properties को प्राथमिकता दें — वे ठीक इसी के लिए बनी हैं।
| क्लास | कंपोनेंट | विवरण |
|---|---|---|
.rk-reel-overlay | Overlay | तय फ़ु लस्क्रीन backdrop (background, z-index) |
.rk-reel-container | Overlay | प्लेयर कंटेनर (position, overflow) |
.rk-reel-loader | Overlay | wave लोडिंग एनिमेशन का overlay |
.rk-reel-media-error | Overlay | एरर स्टेट का overlay (बीच में आइकन + टेक्स्ट) |
.rk-reel-media-error-text | Overlay | एरर संदेश का टेक्स्ट |
.rk-reel-button | Controls | साझा गोल आइकन बटन (बंद, साउंड, नेविगेशन तीर) |
.rk-reel-close-btn | Controls | बंद करने का बटन |
.rk-reel-sound-btn | Controls | साउंड टॉगल बटन |
.rk-reel-nav-arrows | Navigation | सिर्फ़ डेस्कटॉप वाला तीरों का कंटेनर (768px या उससे कम चौड़ाई पर छिपा) |
.rk-reel-nav-button | Navigation | अलग prev/next नेविगेशन तीर |
.rk-reel-slide-wrapper | Slide | मीडिया + overlay के चारों ओर का wrapper |
.rk-reel-slide-overlay | SlideOverlay | gradient overlay का कंटेनर |
.rk-reel-slide-overlay-author | SlideOverlay | लेखक की पंक्ति (avatar + नाम) |
.rk-reel-slide-overlay-avatar | SlideOverlay | लेखक की avatar इमेज |
.rk-reel-slide-overlay-name | SlideOverlay | लेखक के नाम का टेक्स्ट |
.rk-reel-slide-overlay-description | SlideOverlay | विवरण का टेक्स्ट |
.rk-reel-slide-overlay-likes | SlideOverlay | लाइक की पंक्ति (दिल + गिनती) |
.rk-reel-video-container | VideoSlide | वीडियो wrapper (background, overflow) |
.rk-reel-video-element | VideoSlide | <video> एलिमेंट |
.rk-reel-video-poster | VideoSlide | poster इमेज (चलने पर धुंधली होकर गायब) |
.rk-reel-video-poster.rk-visible | VideoSlide | वीडियो रुका/लोड होते समय poster पर लगने वाला स्टेट modifier |
.rk-reel-nested-indicator | NestedSlider | मल्टी-मीडिया स्लाइड के नीचे बिंदु वाला pagination (डेस्कटॉप और टच पर स्थिति अलग) |
.rk-reel-nested-nav | NestedSlider | हॉरिज़ॉन्टल carousel के तीर (768px या उससे कम चौड़ाई पर छिपे) |
.rk-reel-nested-nav-next | NestedSlider | nested अगले तीर की स्थिति |
.rk-reel-nested-nav-prev | NestedSlider | nested पिछले तीर की स्थिति |
.rk-reel-timeline | TimelineBar | scrub bar का wrapper। नीचे से सटी स्थिति, safe-area padding और टच डिवाइस पर स्लाइड overlay से दूरी पाने के लिए कस्टम `renderTimeline` roots पर दोबारा इस्तेमाल करें। |
.rk-reel-timeline-track | TimelineBar | Track (न चला हुआ हिस्सा) |
.rk-reel-timeline-buffered | TimelineBar | buffered हिस्सों की परत |
.rk-reel-timeline-fill | TimelineBar | चले हुए हिस्से का भराव |
.rk-reel-timeline-cursor | TimelineBar | scrub handle की pill (track के ऊपर तैरती) |
थीमिंग
हर रंग, आकार, z-index और ट्रांज़िशन एक CSS custom property में है। कंपोनेंट का source छुए बिना थीम बदलने के लिए :root (या overlay के किसी भी ancestor) पर एक या कई override करें।
| Token | डिफ़ॉल्ट | क्या नियंत्रित करता है |
|---|---|---|
--rk-reel-overlay-bg | #000 | फ़ुलस्क्रीन backdrop का रंग |
--rk-reel-overlay-z | 1000 | Overlay का z-index |
--rk-reel-button-bg | rgba(0, 0, 0, 0.5) | डिफ़ॉल्ट गोल बटन का background |
--rk-reel-button-bg-hover | rgba(255, 255, 255, 0.1) | नेविगेशन तीर का background (और hover की मूल स्टेट) |
--rk-reel-button-bg-hover-strong | rgba(255, 255, 255, 0.2) | नेविगेशन तीर का hover background |
--rk-reel-button-fg | #fff | बटन आइकन का रंग |
--rk-reel-button-size | 44px | बटन की चौड़ाई / ऊँचाई |
--rk-reel-button-radius | 50% | बटन का border-radius |
--rk-reel-ui-z | 10 | बंद / साउंड / नेविगेशन का z-index |
--rk-reel-edge-padding | 16px | बंद / साउंड / नेविगेशन तीरों की किनारे से दूरी |
--rk-reel-nav-gap | 8px | एक के ऊपर एक रखे नेविगेशन तीरों के बीच की दूरी |
--rk-reel-transition | 0.2s | hover ट्रांज़िशन की अवधि |
--rk-reel-loader-color | rgba(255, 255, 255, 0.12) | wave loader के gradient का रंग |
--rk-reel-loader-duration | 1.8s | wave loader एनिमेशन की अवधि |
--rk-reel-error-fg | rgba(255, 255, 255, 0.4) | एरर आइकन और टेक्स्ट का रंग |
--rk-reel-error-text-size | 13px | एरर संदेश का font size |
--rk-reel-slide-overlay-bg | linear-gradient(transparent, rgba(0, 0, 0, 0.7)) | कैप्शन के पीछे का gradient |
--rk-reel-slide-overlay-padding | 48px 16px 16px | कैप्शन की अंदरूनी padding |
--rk-reel-slide-overlay-name-color | #fff | लेखक के नाम का रंग |
--rk-reel-slide-overlay-description-color | rgba(255, 255, 255, 0.9) | विवरण के टेक्स्ट का रंग |
--rk-reel-slide-overlay-likes-color | rgba(255, 255, 255, 0.8) | लाइक वाली पंक्ति के टेक्स्ट का रंग |
--rk-reel-video-bg | #000 | <video> के पीछे letterbox background |
--rk-reel-nested-button-bg | rgba(0, 0, 0, 0.5) | nested तीर का background |
--rk-reel-nested-button-bg-hover | rgba(255, 255, 255, 0.2) | nested तीर का hover background |
--rk-reel-nested-button-size | 36px | nested तीर का आकार |
--rk-reel-nested-edge-padding | 12px | nested तीर की किनारे से दूरी |
--rk-reel-timeline-track | rgba(255, 255, 255, 0.22) | Track का background (न चला हुआ हिस्सा) |
--rk-reel-timeline-buffered | rgba(255, 255, 255, 0.4) | buffered हिस्सों का रंग |
--rk-reel-timeline-fill | #fff | चले हुए हिस्से के भराव का रंग |
--rk-reel-timeline-cursor | #fff | scrub handle की pill का रंग |
--rk-reel-timeline-height | 3px | आराम की हालत में track की ऊँचाई |
--rk-reel-timeline-height-active | 6px | hover / फ़ोकस / scrub पर track की ऊँचाई |
--rk-reel-timeline-cursor-width | 10px | आराम की हालत में scrub pill की चौड़ाई |
--rk-reel-timeline-cursor-width-active | 14px | scrub करते समय scrub pill की चौड़ाई |
--rk-reel-timeline-cursor-height | 24px | आराम की हालत में scrub pill की ऊँचाई |
--rk-reel-timeline-cursor-height-active | 32px | scrub करते स मय scrub pill की ऊँचाई |
--rk-reel-timeline-hitbox | 16px | track के ऊपर pointer के लिए अतिरिक्त छूने की जगह |
--rk-reel-timeline-transition | 0.15s ease-out | track + pill के बढ़ने/घटने का एनिमेशन |
--rk-reel-timeline-z | 11 | टाइमलाइन का z-index (डिफ़ॉल्ट UI परत के ऊपर) |
नीचे का snippet @reelkit/react-reel-player/styles.css के बाद लोड होने वाली stylesheet में डालें।
/* Brand the reel-player overlay */
:root {
--rk-reel-overlay-bg: #0f172a;
--rk-reel-button-bg: rgba(99, 102, 241, 0.65);
--rk-reel-button-bg-hover-strong: rgba(168, 85, 247, 0.85);
--rk-reel-edge-padding: 24px;
--rk-reel-button-size: 52px;
/* Timeline bar: brand-matched, beefier on desktop */
--rk-reel-timeline-track: rgba(99, 102, 241, 0.25);
--rk-reel-timeline-buffered: rgba(168, 85, 247, 0.45);
--rk-reel-timeline-fill: #a855f7;
--rk-reel-timeline-cursor: #a855f7;
--rk-reel-timeline-height: 4px;
--rk-reel-timeline-height-active: 8px;
--rk-reel-timeline-cursor-width-active: 18px;
--rk-reel-timeline-transition: 0.2s ease-out;
}सुलभता
Overlay का root एक modal dialog है (role="dialog", aria-modal="true")। स्क्रीन रीडर की घोषणा बदलने के लिए ariaLabel सेट करें; डिफ़ॉल्ट "Video player" है। हर स्लाइड पर role="group", aria-roledescription="slide" और aria-label="Slide N of M" होते हैं, इसलिए स्वाइप करने पर क्रम में स्थिति बोली जाती है।
Overlay खुलने पर फ़ोकस पकड़ता है और बंद होने पर उसे trigger पर लौटा देता है। Tab और Shift+Tab अंदर के फ़ोकस होने वाले एलिमेंट में घूमते हैं; बाहर निकला फ़ोकस (बाहर क्लिक, प्रोग्राम से फ़ोकस) वापस खींच लिया जाता है। यह @reelkit/core के captureFocusForReturn और createFocusTrap से बना है।
कीबोर्ड शॉर्टकट
| कुंजी | क्रिया |
|---|---|
ArrowUp | पिछली स्लाइड |
ArrowDown | अगली स्लाइड |
ArrowLeft | पिछला मीडिया (nested स्लाइडर में) |
ArrowRight | अगला मीडिया (nested स्लाइडर में) |
Escape | प्लेयर बंद करें |