Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@reelkit/react-reel-player

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@reelkit/react-reel-player - npm Package Compare versions

Comparing version
0.2.1
to
0.3.0
+1
-1
dist/index.css

@@ -1,1 +0,1 @@

.rk-video-slide-container{position:relative;display:flex;align-items:center;justify-content:center;background-color:#000;overflow:hidden}.rk-video-slide-element{width:100%;height:100%;object-position:center}.rk-video-slide-element::-internal-media-controls-overlay-cast-button{display:none}.rk-video-slide-poster{position:absolute;inset:0;width:100%;height:100%;object-position:center;z-index:2;opacity:0;transition:opacity .3s ease;pointer-events:none}.rk-video-slide-poster.rk-visible{opacity:1}.rk-video-slide-loader{position:absolute;inset:0;z-index:3;pointer-events:none;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.15) 50%,transparent 100%);background-size:200% 100%;animation:rk-video-slide-wave 1.5s ease-in-out infinite;opacity:0;transition:opacity .3s ease}.rk-video-slide-loader.rk-visible{opacity:1}@keyframes rk-video-slide-wave{0%{background-position:200% 0}to{background-position:-200% 0}}.rk-nested-nav{display:none;position:absolute;top:50%;transform:translateY(-50%);z-index:10;width:36px;height:36px;border:none;background:#00000080;color:#fff;border-radius:50%;cursor:pointer;align-items:center;justify-content:center;opacity:.7;transition:opacity .2s,background-color .2s;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.rk-nested-nav:hover{background:#fff3;opacity:1}.rk-nested-nav-prev{left:12px}.rk-nested-nav-next{right:12px}@media(min-width:768px){.rk-nested-nav{display:flex}}.rk-reel-slide-overlay{position:absolute;bottom:0;left:0;right:0;padding:48px 16px 16px;background:linear-gradient(transparent,#000000b3);pointer-events:none;z-index:5;display:flex;flex-direction:column;gap:8px}.rk-reel-slide-overlay-author{display:flex;align-items:center;gap:8px}.rk-reel-slide-overlay-avatar{width:32px;height:32px;border-radius:50%;object-fit:cover}.rk-reel-slide-overlay-name{color:#fff;font-size:14px;font-weight:600}.rk-reel-slide-overlay-description{color:#ffffffe6;font-size:13px;line-height:1.4;margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rk-reel-slide-overlay-likes{display:flex;align-items:center;gap:6px;color:#fffc;font-size:13px}.rk-reel-overlay{position:fixed;inset:0;background:#000;z-index:1000;display:flex;justify-content:center;align-items:center;touch-action:none}.rk-reel-container{position:relative;overflow:hidden;touch-action:none}.rk-player-nav-arrows{display:none;position:absolute;right:16px;top:50%;transform:translateY(-50%);flex-direction:column;gap:8px;z-index:10}@media(min-width:768px){.rk-player-nav-arrows{display:flex}}.rk-reel-slide-wrapper{position:relative;overflow:hidden}
.rk-video-slide-container{position:relative;display:flex;align-items:center;justify-content:center;background-color:#000;overflow:hidden}.rk-video-slide-element{width:100%;height:100%;object-position:center}.rk-video-slide-element::-internal-media-controls-overlay-cast-button{display:none}.rk-video-slide-poster{position:absolute;inset:0;width:100%;height:100%;object-position:center;z-index:2;opacity:0;transition:opacity .3s ease;pointer-events:none}.rk-video-slide-poster.rk-visible{opacity:1}.rk-nested-nav{display:none;position:absolute;top:50%;transform:translateY(-50%);z-index:10;width:36px;height:36px;border:none;background:#00000080;color:#fff;border-radius:50%;cursor:pointer;align-items:center;justify-content:center;opacity:.7;transition:opacity .2s,background-color .2s;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.rk-nested-nav:hover{background:#fff3;opacity:1}.rk-nested-nav-prev{left:12px}.rk-nested-nav-next{right:12px}@media(min-width:768px){.rk-nested-nav{display:flex}}.rk-reel-slide-overlay{position:absolute;bottom:0;left:0;right:0;padding:48px 16px 16px;background:linear-gradient(transparent,#000000b3);pointer-events:none;z-index:5;display:flex;flex-direction:column;gap:8px}.rk-reel-slide-overlay-author{display:flex;align-items:center;gap:8px}.rk-reel-slide-overlay-avatar{width:32px;height:32px;border-radius:50%;object-fit:cover}.rk-reel-slide-overlay-name{color:#fff;font-size:14px;font-weight:600}.rk-reel-slide-overlay-description{color:#ffffffe6;font-size:13px;line-height:1.4;margin:0;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rk-reel-slide-overlay-likes{display:flex;align-items:center;gap:6px;color:#fffc;font-size:13px}.rk-reel-overlay{position:fixed;inset:0;background:#000;z-index:1000;display:flex;justify-content:center;align-items:center;touch-action:none;overscroll-behavior:none}.rk-reel-container{position:relative;overflow:hidden;touch-action:none}.rk-player-nav-arrows{display:none;position:absolute;right:16px;top:50%;transform:translateY(-50%);flex-direction:column;gap:8px;z-index:10}@media(min-width:768px){.rk-player-nav-arrows{display:flex}}.rk-reel-slide-wrapper{position:relative;overflow:hidden}.rk-reel-loader{position:absolute;inset:0;z-index:5;pointer-events:none;background:linear-gradient(90deg,transparent 25%,rgba(255,255,255,.12) 50%,transparent 75%);background-size:300% 100%;animation:rk-reel-wave 1.8s ease-in-out infinite}@keyframes rk-reel-wave{0%{background-position:100% 0;opacity:0}15%{opacity:1}85%{opacity:1}to{background-position:-100% 0;opacity:0}}.rk-media-error{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:10;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;color:#fff6}.rk-media-error-text{font-size:13px;letter-spacing:.02em}

@@ -62,4 +62,2 @@ /**

export type { SlideOverlayProps } from './lib/SlideOverlay';
export { SoundProvider } from './lib/SoundState';
export type { SoundState } from './lib/SoundState';
export { useSoundState } from './lib/useSoundState';
export { SoundProvider, useSoundState, type SoundController, } from '@reelkit/react';

@@ -1,38 +0,22 @@

import { jsx as t, jsxs as k, Fragment as q } from "react/jsx-runtime";
import { createContext as fe, useState as $, useContext as ge, useRef as E, useLayoutEffect as pe, useEffect as M, useCallback as d } from "react";
import { createPortal as ye } from "react-dom";
import { ChevronLeft as me, ChevronRight as be, X as ke, VolumeX as we, Volume2 as Ce, Heart as Ne, ChevronUp as Pe, ChevronDown as Re } from "lucide-react";
import { createSignal as O, createSharedVideo as Se, noop as te, captureFrame as Ie, Observe as H, Reel as ne, ReelIndicator as xe, useBodyLock as Ee } from "@reelkit/react";
const re = fe(null), Le = ({ children: e }) => {
const n = $(() => {
const i = O(!0), s = O(!1);
return {
muted: i,
disabled: s,
toggle: () => {
i.value = !i.value;
}
};
})[0];
return /* @__PURE__ */ t(re.Provider, { value: n, children: e });
}, G = () => {
const e = ge(re);
if (!e)
throw new Error("useSoundState must be used within SoundProvider");
return e;
}, oe = ({
src: e,
size: n,
className: i,
style: s,
imgClassName: p,
imgStyle: y,
imageProps: u
}) => /* @__PURE__ */ t(
import { jsx as e, jsxs as P, Fragment as V } from "react/jsx-runtime";
import { useRef as A, useState as ee, useEffect as q, useLayoutEffect as ve } from "react";
import { createPortal as ge } from "react-dom";
import { ChevronLeft as fe, ChevronRight as pe, X as me, VolumeX as ye, Volume2 as be, Heart as Ce, ImageOff as ke, ChevronUp as Se, ChevronDown as Ne } from "lucide-react";
import { useSoundState as ne, createSignal as U, createDisposableList as ae, observeMediaLoading as Re, observeDomEvent as Z, noop as le, captureFrame as se, syncMutedToVideo as we, Observe as K, createSharedVideo as xe, Reel as de, ReelIndicator as Ie, SoundProvider as Pe, createContentLoadingController as De, useBodyLock as Ee, reaction as ze, createContentPreloader as Me } from "@reelkit/react";
import { SoundProvider as Ye, useSoundState as Ze } from "@reelkit/react";
const ce = ({
src: n,
size: u,
className: h,
style: t,
imgClassName: D,
imgStyle: N,
imageProps: s
}) => /* @__PURE__ */ e(
"div",
{
className: i,
className: h,
style: {
width: n[0],
height: n[1],
width: u[0],
height: u[1],
display: "flex",

@@ -43,5 +27,5 @@ alignItems: "center",

overflow: "hidden",
...s
...t
},
children: /* @__PURE__ */ t(
children: /* @__PURE__ */ e(
"img",

@@ -51,5 +35,5 @@ {

loading: "lazy",
...u,
src: e,
className: p,
...s,
src: n,
className: D,
style: {

@@ -60,3 +44,6 @@ width: "100%",

objectPosition: "center",
...y
...N
},
onError: (i) => {
i.target.style.display = "none", s?.onError?.(i);
}

@@ -66,118 +53,174 @@ }

}
), B = Se({
), O = xe({
className: "rk-video-slide-element",
disableRemotePlayback: !0,
disablePictureInPicture: !0
}), De = typeof window < "u" ? pe : M, ie = ({
src: e,
poster: n,
aspectRatio: i,
size: s,
isActive: p,
isInnerActive: y = !0,
slideKey: u,
onVideoRef: l,
className: w,
style: a
}), Ae = typeof window < "u" ? ve : q, ue = ({
src: n,
poster: u,
aspectRatio: h,
size: t,
isActive: D,
isInnerActive: N = !0,
slideKey: s,
onVideoRef: i,
onReady: b,
onWaiting: C,
onError: M,
className: R,
style: c
}) => {
const v = E(null), C = G(), N = p && y, m = i < 1, [h, L] = $(
() => [O(!1), O(!0)]
)[0];
return De(() => {
if (!N || !v.current) return;
const o = B.getVideo(), z = v.current;
h.value = !0, L.value = !0;
const g = () => h.value = !1, j = () => h.value = !0, c = () => {
h.value = !1, L.value = !1;
};
o.addEventListener("canplay", g), o.addEventListener("waiting", j), o.addEventListener("playing", c), o.src = e, o.muted = C.muted.value;
const T = B.playbackPositions.get(u);
return o.currentTime = T ?? 0, o.style.objectFit = m ? "cover" : "contain", z.appendChild(o), l && l(o), o.play().catch(te), () => {
o.removeEventListener("canplay", g), o.removeEventListener("waiting", j), o.removeEventListener("playing", c), B.playbackPositions.set(u, o.currentTime);
const P = Ie(o);
P && B.capturedFrames.set(u, P), o.parentNode === z && z.removeChild(o), l && l(null), h.value = !1;
};
}, [N, e, m, u, l]), M(() => {
if (!N) return;
const o = B.getVideo();
return o && (o.muted = C.muted.value), C.muted.observe(() => {
o && (o.muted = C.muted.value);
});
}, [N]), /* @__PURE__ */ k(
const L = A(null), _ = ne(), f = D && N, k = h < 1, [g] = ee(() => U(!0)), w = A({ onReady: b, onWaiting: C, onError: M });
return w.current = { onReady: b, onWaiting: C, onError: M }, Ae(() => {
if (!f || !L.current) return;
const l = O.getVideo(), x = L.current, W = ae();
g.value = !0, W.push(
Re(l, {
onReady: () => {
w.current.onReady?.();
},
onWaiting: () => {
w.current.onWaiting?.();
},
onPlaying: () => {
g.value = !1;
}
}),
Z(l, "error", () => {
l.error && w.current.onError?.();
})
), l.src = n, l.muted = _.muted.value;
const X = O.playbackPositions.get(s);
return l.currentTime = X ?? 0, l.style.objectFit = k ? "cover" : "contain", l.dataset.slideKey = s, x.appendChild(l), i && i(l), l.play().catch(le), W.push(() => {
O.playbackPositions.set(s, l.currentTime);
const o = se(l);
o && O.capturedFrames.set(s, o), l.parentNode === x && x.removeChild(l), i && i(null);
}), W.dispose;
}, [f, n, k, s, i]), q(() => {
if (f)
return we(_, O.getVideo());
}, [f]), /* @__PURE__ */ e(
"div",
{
ref: v,
className: w ? `rk-video-slide-container ${w}` : "rk-video-slide-container",
ref: L,
className: R ? `rk-video-slide-container ${R}` : "rk-video-slide-container",
style: {
width: s[0],
height: s[1],
...a
width: t[0],
height: t[1],
...c
},
children: [
/* @__PURE__ */ t(H, { signals: [L], children: () => {
const o = B.capturedFrames.get(u) ?? n;
return o ? /* @__PURE__ */ t(
"img",
{
src: o,
alt: "",
className: `rk-video-slide-poster ${!N || L.value ? "rk-visible" : ""}`,
style: {
objectFit: m ? "cover" : "contain"
}
children: /* @__PURE__ */ e(K, { signals: [g], children: () => {
const l = O.capturedFrames.get(s) ?? u;
return l ? /* @__PURE__ */ e(
"img",
{
src: l,
alt: "",
className: `rk-video-slide-poster ${!f || g.value ? "rk-visible" : ""}`,
style: {
objectFit: k ? "cover" : "contain"
},
onError: (x) => {
x.target.style.display = "none";
}
) : null;
} }),
/* @__PURE__ */ t(H, { signals: [h], children: () => /* @__PURE__ */ t(
"div",
{
className: `rk-video-slide-loader ${h.value ? "rk-visible" : ""}`
}
) })
]
) : null;
} })
}
);
}, ze = ({
media: e,
isParentActive: n,
size: i,
contentId: s,
innerSliderRef: p,
enableWheel: y,
onVideoRef: u,
onActiveMediaTypeChange: l,
renderNavigation: w,
renderNestedSlide: a
}) => {
const [v, C] = $(0), N = E(v);
N.current = v;
const m = E(null), h = E(null);
M(() => (p.current = m.current, () => {
p.current = null;
}), [p]), M(() => {
n && l && l(e[v].type);
}, [n, l, e, v]);
const L = d(
(c) => {
h.current = c, u && u(c);
},
[u]
), o = d(() => {
h.current && !h.current.paused && h.current.pause();
}, []), z = d(
(c) => {
C(c), l && l(e[c].type);
},
[e, l]
), g = d(() => {
m.current?.prev();
}, []), j = d(() => {
m.current?.next();
}, []);
return /* @__PURE__ */ k(
}, Le = (n) => {
const { media: u, isParentActive: h, size: t, innerSliderRef: D, enableWheel: N } = n, s = A(n);
s.current = n;
const i = A(null), b = A(null), [
{
indexSignal: C,
handleBeforeChange: M,
handleAfterChange: R,
handlePrev: c,
handleNext: L,
itemBuilder: _
}
] = ee(() => {
const f = U(0), k = (g) => {
b.current = g, s.current.onVideoRef?.(g);
};
return {
indexSignal: f,
handleBeforeChange: () => {
b.current && !b.current.paused && b.current.pause();
},
handleAfterChange: (g) => {
f.value = g;
const {
media: w,
onActiveMediaTypeChange: l,
onReady: x
} = s.current;
l?.(w[g].type), w[g].type === "image" && x?.();
},
handlePrev: () => {
i.current?.prev();
},
handleNext: () => {
i.current?.next();
},
itemBuilder: (g, w, l) => {
const {
media: x,
isParentActive: W,
contentId: X,
onReady: o,
onWaiting: p,
onError: d,
renderNestedSlide: y
} = s.current, a = x[g], v = g === f.value, E = `${X}:${a.id}`, I = v ? k : void 0, z = a.type === "video" ? /* @__PURE__ */ e(
ue,
{
src: a.src,
poster: a.poster,
aspectRatio: a.aspectRatio,
size: l,
isActive: W,
isInnerActive: v,
slideKey: E,
onVideoRef: I,
onReady: v ? o : void 0,
onWaiting: v ? p : void 0,
onError: v ? d : void 0
}
) : /* @__PURE__ */ e(
ce,
{
src: a.src,
size: l,
imageProps: v ? { onLoad: o, onError: d } : void 0
}
);
return y ? y({
item: s.current.contentItem,
media: a,
index: g,
size: l,
isActive: W,
isInnerActive: v,
slideKey: E,
onVideoRef: I,
onReady: v ? o : void 0,
onWaiting: v ? p : void 0,
onError: v ? d : void 0,
defaultContent: z
}) : z;
}
};
});
return q(() => (D.current = i.current, () => {
D.current = null;
}), [D]), q(() => {
h && s.current.onActiveMediaTypeChange?.(u[C.value].type);
}, [h]), /* @__PURE__ */ P(
"div",
{
style: {
width: i[0],
height: i[1],
width: t[0],
height: t[1],
position: "relative",

@@ -187,135 +230,135 @@ backgroundColor: "#000"

children: [
/* @__PURE__ */ t(
ne,
/* @__PURE__ */ e(
de,
{
count: e.length,
size: i,
count: u.length,
size: t,
direction: "horizontal",
loop: !1,
useNavKeys: !0,
enableWheel: y,
apiRef: m,
beforeChange: o,
afterChange: z,
className: n ? "rk-nested-active" : void 0,
itemBuilder: (c, T, P) => {
const R = e[c], D = N.current === c, A = `${s}:${R.id}`, S = D ? L : void 0, F = R.type === "video" ? /* @__PURE__ */ t(
ie,
{
src: R.src,
poster: R.poster,
aspectRatio: R.aspectRatio,
size: P,
isActive: n,
isInnerActive: D,
slideKey: A,
onVideoRef: S
}
) : /* @__PURE__ */ t(oe, { src: R.src, size: P });
return a ? a({
item: R,
index: c,
size: P,
isActive: n,
isInnerActive: D,
slideKey: A,
onVideoRef: S,
defaultContent: F
}) : F;
}
enableNavKeys: !0,
enableWheel: N,
apiRef: i,
beforeChange: M,
afterChange: R,
className: h ? "rk-nested-active" : void 0,
itemBuilder: _
}
),
e.length > 1 && /* @__PURE__ */ t(
"div",
{
style: {
position: "absolute",
bottom: 16,
left: "50%",
transform: "translateX(-50%)",
zIndex: 10
},
children: /* @__PURE__ */ t(
xe,
/* @__PURE__ */ e(K, { signals: [C], children: () => {
const f = C.value, { media: k, renderNavigation: g } = s.current;
return /* @__PURE__ */ P(V, { children: [
k.length > 1 && /* @__PURE__ */ e(
"div",
{
count: e.length,
active: v,
direction: "horizontal",
visible: 5,
radius: 3,
gap: 4,
activeColor: "#fff",
inactiveColor: "rgba(255,255,255,0.4)",
onDotClick: (c) => m.current?.goTo(c, !0)
style: {
position: "absolute",
bottom: 16,
left: "50%",
transform: "translateX(-50%)",
zIndex: 10
},
children: /* @__PURE__ */ e(
Ie,
{
count: k.length,
active: f,
direction: "horizontal",
visible: 5,
radius: 3,
gap: 4,
activeColor: "#fff",
inactiveColor: "rgba(255,255,255,0.4)",
onDotClick: (w) => i.current?.goTo(w, !0)
}
)
}
)
}
),
e.length > 1 && (w ? w({
onPrev: g,
onNext: j,
activeIndex: v,
count: e.length
}) : /* @__PURE__ */ k(q, { children: [
v > 0 && /* @__PURE__ */ t(
"button",
{
className: "rk-nested-nav rk-nested-nav-prev",
onClick: g,
"aria-label": "Previous",
children: /* @__PURE__ */ t(me, { size: 24 })
}
),
v < e.length - 1 && /* @__PURE__ */ t(
"button",
{
className: "rk-nested-nav rk-nested-nav-next",
onClick: j,
"aria-label": "Next",
children: /* @__PURE__ */ t(be, { size: 24 })
}
)
] }))
),
k.length > 1 && (g ? g({
item: s.current.contentItem,
media: k[f],
onPrev: c,
onNext: L,
activeIndex: f,
count: k.length
}) : /* @__PURE__ */ P(V, { children: [
f > 0 && /* @__PURE__ */ e(
"button",
{
className: "rk-nested-nav rk-nested-nav-prev",
onClick: c,
"aria-label": "Previous",
children: /* @__PURE__ */ e(fe, { size: 24 })
}
),
f < k.length - 1 && /* @__PURE__ */ e(
"button",
{
className: "rk-nested-nav rk-nested-nav-next",
onClick: L,
"aria-label": "Next",
children: /* @__PURE__ */ e(pe, { size: 24 })
}
)
] }))
] });
} })
]
}
);
}, je = ({
content: e,
isActive: n,
size: i,
innerSliderRef: s,
enableWheel: p,
onVideoRef: y,
onActiveMediaTypeChange: u,
renderNestedNavigation: l,
renderNestedSlide: w
}, We = ({
content: n,
isActive: u,
size: h,
innerSliderRef: t,
enableWheel: D,
onVideoRef: N,
onActiveMediaTypeChange: s,
onReady: i,
onWaiting: b,
onError: C,
renderNestedNavigation: M,
renderNestedSlide: R
}) => {
const { media: a } = e;
return a.length === 1 && a[0].type === "image" ? /* @__PURE__ */ t(oe, { src: a[0].src, size: i }) : a.length === 1 && a[0].type === "video" ? /* @__PURE__ */ t(
ie,
const { media: c } = n;
return c.length === 1 && c[0].type === "image" ? /* @__PURE__ */ e(
ce,
{
src: a[0].src,
poster: a[0].poster,
aspectRatio: a[0].aspectRatio,
size: i,
isActive: n,
slideKey: e.id,
onVideoRef: y
src: c[0].src,
size: h,
imageProps: { onLoad: i, onError: C }
}
) : /* @__PURE__ */ t(
ze,
) : c.length === 1 && c[0].type === "video" ? /* @__PURE__ */ e(
ue,
{
media: a,
isParentActive: n,
size: i,
contentId: e.id,
innerSliderRef: s,
enableWheel: p,
onVideoRef: y,
onActiveMediaTypeChange: u,
renderNavigation: l,
renderNestedSlide: w
src: c[0].src,
poster: c[0].poster,
aspectRatio: c[0].aspectRatio,
size: h,
isActive: u,
slideKey: n.id,
onVideoRef: N,
onReady: i,
onWaiting: b,
onError: C
}
) : /* @__PURE__ */ e(
Le,
{
media: c,
contentItem: n,
isParentActive: u,
size: h,
contentId: n.id,
innerSliderRef: t,
enableWheel: D,
onVideoRef: N,
onActiveMediaTypeChange: s,
onReady: i,
onWaiting: b,
onError: C,
renderNavigation: M,
renderNestedSlide: R
}
);
}, se = {
}, he = {
width: 44,

@@ -332,13 +375,13 @@ height: 44,

transition: "background-color 0.2s"
}, Ae = ({
onClick: e,
className: n = "rk-player-close-btn",
style: i
}) => /* @__PURE__ */ t(
}, Fe = ({
onClick: n,
className: u = "rk-player-close-btn",
style: h
}) => /* @__PURE__ */ e(
"button",
{
onClick: e,
className: n,
onClick: n,
className: u,
style: {
...se,
...he,
position: "absolute",

@@ -348,20 +391,20 @@ top: 16,

zIndex: 10,
...i
...h
},
"aria-label": "Close",
children: /* @__PURE__ */ t(ke, { size: 24 })
children: /* @__PURE__ */ e(me, { size: 24 })
}
), Fe = ({
disabled: e = !1,
className: n = "rk-player-sound-btn",
style: i
), Te = ({
disabled: n = !1,
className: u = "rk-player-sound-btn",
style: h
}) => {
const s = G();
return /* @__PURE__ */ t(H, { signals: [s.muted, s.disabled], children: () => s.disabled.value ? null : /* @__PURE__ */ t(
const t = ne();
return /* @__PURE__ */ e(K, { signals: [t.muted, t.disabled], children: () => t.disabled.value ? null : /* @__PURE__ */ e(
"button",
{
onClick: e ? void 0 : s.toggle,
className: n,
onClick: n ? void 0 : t.toggle,
className: u,
style: {
...se,
...he,
position: "absolute",

@@ -371,190 +414,171 @@ bottom: 16,

zIndex: 10,
opacity: e ? 0.4 : 1,
cursor: e ? "default" : "pointer",
opacity: n ? 0.4 : 1,
cursor: n ? "default" : "pointer",
transition: "opacity 0.2s, background-color 0.2s",
...i
...h
},
"aria-label": s.muted.value ? "Unmute" : "Mute",
"aria-disabled": e,
children: s.muted.value ? /* @__PURE__ */ t(we, { size: 22 }) : /* @__PURE__ */ t(Ce, { size: 22 })
"aria-label": t.muted.value ? "Unmute" : "Mute",
"aria-disabled": n,
children: t.muted.value ? /* @__PURE__ */ e(ye, { size: 22 }) : /* @__PURE__ */ e(be, { size: 22 })
}
) });
}, Be = ({
onClose: e,
showSound: n = !1,
soundDisabled: i = !1
}) => /* @__PURE__ */ k(q, { children: [
/* @__PURE__ */ t(Ae, { onClick: e }),
n && /* @__PURE__ */ t(Fe, { disabled: i })
onClose: n,
showSound: u = !1,
soundDisabled: h = !1
}) => /* @__PURE__ */ P(V, { children: [
/* @__PURE__ */ e(Fe, { onClick: n }),
u && /* @__PURE__ */ e(Te, { disabled: h })
] });
function $e(e) {
return e >= 1e6 ? `${(e / 1e6).toFixed(1).replace(/\.0$/, "")}M` : e >= 1e3 ? `${(e / 1e3).toFixed(1).replace(/\.0$/, "")}K` : String(e);
function Ve(n) {
return n >= 1e6 ? `${(n / 1e6).toFixed(1).replace(/\.0$/, "")}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}K` : String(n);
}
const Me = ({
author: e,
description: n,
likes: i
}) => !e && !n && i == null ? null : /* @__PURE__ */ k("div", { className: "rk-reel-slide-overlay", children: [
e && /* @__PURE__ */ k("div", { className: "rk-reel-slide-overlay-author", children: [
/* @__PURE__ */ t(
const je = ({
author: n,
description: u,
likes: h
}) => !n && !u && h == null ? null : /* @__PURE__ */ P("div", { className: "rk-reel-slide-overlay", children: [
n && /* @__PURE__ */ P("div", { className: "rk-reel-slide-overlay-author", children: [
/* @__PURE__ */ e(
"img",
{
className: "rk-reel-slide-overlay-avatar",
src: e.avatar,
alt: e.name
src: n.avatar,
alt: n.name
}
),
/* @__PURE__ */ t("span", { className: "rk-reel-slide-overlay-name", children: e.name })
/* @__PURE__ */ e("span", { className: "rk-reel-slide-overlay-name", children: n.name })
] }),
n && /* @__PURE__ */ t("p", { className: "rk-reel-slide-overlay-description", children: n }),
i != null && /* @__PURE__ */ k("div", { className: "rk-reel-slide-overlay-likes", children: [
/* @__PURE__ */ t(Ne, { size: 16 }),
/* @__PURE__ */ t("span", { children: $e(i) })
u && /* @__PURE__ */ e("p", { className: "rk-reel-slide-overlay-description", children: u }),
h != null && /* @__PURE__ */ P("div", { className: "rk-reel-slide-overlay-likes", children: [
/* @__PURE__ */ e(Ce, { size: 16 }),
/* @__PURE__ */ e("span", { children: Ve(h) })
] })
] }), Oe = 9 / 16, Te = 768;
function Ve({
onClose: e,
content: n,
initialIndex: i = 0,
onSlideChange: s,
apiRef: p,
renderSlideOverlay: y,
renderSlide: u,
renderControls: l,
renderNavigation: w,
renderNestedNavigation: a,
renderNestedSlide: v,
aspectRatio: C = Oe,
transitionDuration: N,
swipeDistanceFactor: m,
loop: h = !1,
useNavKeys: L = !0,
enableWheel: o = !0,
wheelDebounceMs: z
}) {
const [g, j] = $(i), c = E(g);
c.current = g;
const [T, P] = $(null), R = E(null), D = p ?? R, A = E(null), S = E(null), F = E(!1), K = G(), U = d(() => {
if (typeof window > "u") return [0, 0];
const r = window.innerWidth, I = window.innerHeight;
if (r < Te)
return [r, I];
let f = I * C, b = I;
return f > r && (f = r, b = r / C), [f, b];
}, [C]), [X] = $(
() => O(U())
);
M(() => {
const r = () => {
X.value = U(), D.current?.adjust();
] }), $e = 9 / 16, Oe = 768, Q = 2, $ = Me({ maxCacheSize: 1e3 });
function Ke(n) {
const { initialIndex: u = 0, apiRef: h } = n, t = A(n);
t.current = n;
const D = A(null), N = h ?? D, s = A(null), i = A(null), b = A(!1), C = ne(), [
{
sizeSignal: M,
loadingCtrl: R,
indexSignal: c,
innerMediaTypeSignal: L,
getSize: _,
handleBeforeChange: f,
handleAfterChange: k,
handleSlideDragStart: g,
handleSlideDragEnd: w,
handleSlideDragCanceled: l,
handlePrev: x,
handleNext: W,
itemBuilder: X
}
] = ee(() => {
const o = () => {
if (typeof window > "u") return [0, 0];
const r = t.current.aspectRatio ?? $e, m = window.innerWidth, S = window.innerHeight;
if (m < Oe)
return [m, S];
let T = S * r, F = S;
return T > m && (T = m, F = m / r), [T, F];
}, p = U(o()), d = De(!0, u), y = U(u), a = U(null), v = (r) => t.current.content[r]?.media.some((S) => S.type === "video") ?? !1, E = (r) => {
i.current = r;
}, I = (r) => {
a.value = r;
}, z = (r, m, S) => {
const { renderSlideOverlay: T } = t.current;
if (T)
return T(r, m, S);
const F = r, G = "author" in r ? F.author : void 0, Y = "description" in r ? F.description : void 0, J = "likes" in r ? F.likes : void 0;
return /* @__PURE__ */ e(je, { author: G, description: Y, likes: J });
};
return window.addEventListener("resize", r), () => {
window.removeEventListener("resize", r);
};
}, [U]), M(() => {
const r = (I) => {
I.key === "Escape" && e();
};
return window.addEventListener("keydown", r), () => window.removeEventListener("keydown", r);
}, [e]), Ee(!0);
const J = d((r) => {
S.current = r;
}, []), W = d(
(r) => n[r]?.media.some((f) => f.type === "video") ?? !1,
[n]
), le = d(() => {
K.disabled.value = !0;
}, []), ae = d(
(r) => {
j(r), P(null), W(r) && (K.disabled.value = !1), s?.(r);
},
[W, s]
), Q = d((r) => {
P(r);
}, []), ce = d(
(r) => n[r]?.media.length > 1,
[n]
)(g) && T === "image", de = d(() => {
A.current?.unobserve(), S.current && !S.current.paused && (S.current.pause(), F.current = !0);
}, []), ue = d(() => {
A.current?.observe();
}, []), ve = d(() => {
F.current && S.current && S.current.play().catch(te), F.current = !1;
}, []), Y = d(() => {
D.current?.prev();
}, []), Z = d(() => {
D.current?.next();
}, []), he = (r, I, f) => {
if (y)
return y(r, I, f);
const b = r, x = "author" in r ? b.author : void 0, V = "description" in r ? b.description : void 0, _ = "likes" in r ? b.likes : void 0;
return /* @__PURE__ */ t(Me, { author: x, description: V, likes: _ });
}, ee = /* @__PURE__ */ k("div", { className: "rk-reel-overlay", children: [
/* @__PURE__ */ k("div", { className: "rk-reel-container", children: [
/* @__PURE__ */ t(H, { signals: [X], children: () => /* @__PURE__ */ t(
ne,
{
count: n.length,
size: X.value,
direction: "vertical",
loop: h,
useNavKeys: L,
enableWheel: o,
wheelDebounceMs: z,
transitionDuration: N,
swipeDistanceFactor: m,
initialIndex: i,
apiRef: D,
beforeChange: le,
afterChange: ae,
onSlideDragStart: de,
onSlideDragEnd: ue,
onSlideDragCanceled: ve,
itemBuilder: (r, I, f) => {
const b = n[r], x = c.current === r, V = /* @__PURE__ */ k(q, { children: [
/* @__PURE__ */ t(
je,
{
content: b,
isActive: x,
size: f,
innerSliderRef: A,
enableWheel: o,
onVideoRef: x ? J : void 0,
onActiveMediaTypeChange: x ? Q : void 0,
renderNestedNavigation: a,
renderNestedSlide: v
}
),
he(b, r, x)
] });
if (u) {
const _ = u({
item: b,
index: r,
size: f,
isActive: x,
slideKey: b.id,
onVideoRef: x ? J : void 0,
innerSliderRef: A,
onActiveMediaTypeChange: x ? Q : void 0,
renderNestedNavigation: a,
enableWheel: o,
defaultContent: V
});
if (_ !== null)
return /* @__PURE__ */ t(
"div",
{
className: "rk-reel-slide-wrapper",
style: {
width: f[0],
height: f[1],
position: "relative"
},
children: _
}
);
return {
sizeSignal: p,
loadingCtrl: d,
indexSignal: y,
innerMediaTypeSignal: a,
getSize: o,
handleVideoRef: E,
handleActiveMediaTypeChange: I,
handleBeforeChange: () => {
if (C.disabled.value = !0, i.current) {
i.current.pause();
const r = i.current.dataset.slideKey, m = se(i.current);
r && m && O.capturedFrames.set(r, m);
}
},
handleAfterChange: (r) => {
d.setActiveIndex(r);
const m = t.current.content[r]?.media[0]?.src;
m && $.isErrored(m) ? d.onError(r) : m && $.isLoaded(m) && d.onReady(r), a.value = null, v(r) && (C.disabled.value = !1), y.value = r, t.current.onSlideChange?.(r);
},
handleSlideDragStart: () => {
s.current?.unobserve(), i.current && !i.current.paused && (i.current.pause(), b.current = !0);
},
handleSlideDragEnd: () => {
s.current?.observe();
},
handleSlideDragCanceled: () => {
b.current && i.current && i.current.play().catch(le), b.current = !1;
},
handlePrev: () => {
N.current?.prev();
},
handleNext: () => {
N.current?.next();
},
itemBuilder: (r, m, S) => {
const {
content: T,
renderSlide: F,
renderNestedNavigation: G,
renderNestedSlide: Y,
enableWheel: J = !0
} = t.current, H = T[r], j = r === y.value, re = () => {
d.onReady(r);
const B = H.media[0]?.src;
B && $.markLoaded(B);
}, te = () => d.onWaiting(r), ie = () => {
const B = H.media[0]?.src;
B && $.markErrored(B), d.onError(r);
}, oe = /* @__PURE__ */ P(V, { children: [
/* @__PURE__ */ e(
We,
{
content: H,
isActive: j,
size: S,
innerSliderRef: s,
enableWheel: J,
onVideoRef: j ? E : void 0,
onReady: re,
onWaiting: te,
onError: ie,
onActiveMediaTypeChange: j ? I : void 0,
renderNestedNavigation: G,
renderNestedSlide: Y
}
return /* @__PURE__ */ t(
),
z(H, r, j)
] });
if (F) {
const B = F({
item: H,
index: r,
size: S,
isActive: j,
slideKey: H.id,
onVideoRef: j ? E : void 0,
innerSliderRef: s,
onActiveMediaTypeChange: j ? I : void 0,
renderNestedNavigation: G,
enableWheel: J,
defaultContent: oe,
onReady: re,
onWaiting: te,
onError: ie
});
if (B !== null)
return /* @__PURE__ */ e(
"div",

@@ -564,83 +588,197 @@ {

style: {
width: f[0],
height: f[1],
width: S[0],
height: S[1],
position: "relative"
},
children: V
children: B
}
);
}
return /* @__PURE__ */ e(
"div",
{
className: "rk-reel-slide-wrapper",
style: {
width: S[0],
height: S[1],
position: "relative"
},
children: oe
}
}
) }),
l ? l({ onClose: e, soundState: K, activeIndex: g, content: n }) : /* @__PURE__ */ t(
Be,
{
onClose: e,
showSound: W(g),
soundDisabled: ce
}
)
);
}
};
});
return Ee(!0), q(() => {
const o = ae(), p = () => {
const a = t.current.content, v = c.value, E = v - Q < 0 ? 0 : v - Q, I = v + Q > a.length - 1 ? a.length - 1 : v + Q;
for (let z = E; z <= I; z++)
if (z !== v)
for (const r of a[z].media)
r.type === "video" ? r.poster && $.preload(r.poster, "image") : $.preload(r.src, "image");
};
o.push(
Z(window, "resize", () => {
M.value = _(), N.current?.adjust();
}),
Z(window, "keydown", (a) => {
a.key === "Escape" && t.current.onClose();
}),
ze(() => [c], p)
), p();
const d = c.value, y = t.current.content[d]?.media[0]?.src;
return y && o.push(
$.onLoaded(y, () => R.onReady(d))
), o.dispose;
}, []), ge(
/* @__PURE__ */ P("div", { className: "rk-reel-overlay", children: [
/* @__PURE__ */ P("div", { className: "rk-reel-container", children: [
/* @__PURE__ */ e(K, { signals: [M], children: () => {
const {
content: o,
loop: p = !1,
enableNavKeys: d = !0,
enableWheel: y = !0,
wheelDebounceMs: a,
transitionDuration: v,
swipeDistanceFactor: E,
initialIndex: I = 0
} = t.current;
return /* @__PURE__ */ e(
de,
{
count: o.length,
size: M.value,
direction: "vertical",
loop: p,
enableNavKeys: d,
enableWheel: y,
wheelDebounceMs: a,
transitionDuration: v,
swipeDistanceFactor: E,
initialIndex: I,
apiRef: N,
beforeChange: f,
afterChange: k,
onSlideDragStart: g,
onSlideDragEnd: w,
onSlideDragCanceled: l,
itemBuilder: X
}
);
} }),
/* @__PURE__ */ e(
K,
{
signals: [R.isLoading, R.isError, c],
children: () => {
const o = c.value, {
renderLoading: p,
renderError: d,
content: y
} = t.current, a = y[o];
return R.isError.value ? d ? /* @__PURE__ */ e(V, { children: d({ item: a, activeIndex: o }) }) : /* @__PURE__ */ P(
"div",
{
className: "rk-media-error",
role: "img",
"aria-label": "Content unavailable",
children: [
/* @__PURE__ */ e(ke, { size: 48, strokeWidth: 1.5, "aria-hidden": "true" }),
/* @__PURE__ */ e("span", { className: "rk-media-error-text", children: "Content unavailable" })
]
}
) : R.isLoading.value ? p ? /* @__PURE__ */ e(V, { children: p({ item: a, activeIndex: o }) }) : /* @__PURE__ */ e("div", { className: "rk-reel-loader" }) : null;
}
}
),
/* @__PURE__ */ e(K, { signals: [c, L], children: () => {
const o = c.value, p = L.value, {
renderControls: d,
onClose: y,
content: a
} = t.current, v = a[o]?.media.some((z) => z.type === "video") ?? !1, I = a[o]?.media.length > 1 && p === "image";
return d ? /* @__PURE__ */ e(V, { children: d({
item: a[o],
onClose: y,
soundState: C,
activeIndex: o,
content: a
}) }) : /* @__PURE__ */ e(
Be,
{
onClose: y,
showSound: v,
soundDisabled: I
}
);
} })
] }),
/* @__PURE__ */ e(K, { signals: [c], children: () => {
const o = c.value, { renderNavigation: p, content: d } = t.current;
return p ? /* @__PURE__ */ e(V, { children: p({
item: d[o],
onPrev: x,
onNext: W,
activeIndex: o,
count: d.length
}) }) : /* @__PURE__ */ P("div", { className: "rk-player-nav-arrows", children: [
/* @__PURE__ */ e(
"button",
{
onClick: x,
style: {
width: 44,
height: 44,
borderRadius: "50%",
backgroundColor: "rgba(255,255,255,0.1)",
border: "none",
color: "#fff",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
"aria-label": "Previous",
children: /* @__PURE__ */ e(Se, { size: 28 })
}
),
/* @__PURE__ */ e(
"button",
{
onClick: W,
style: {
width: 44,
height: 44,
borderRadius: "50%",
backgroundColor: "rgba(255,255,255,0.1)",
border: "none",
color: "#fff",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
"aria-label": "Next",
children: /* @__PURE__ */ e(Ne, { size: 28 })
}
)
] });
} })
] }),
w ? w({
onPrev: Y,
onNext: Z,
activeIndex: g,
count: n.length
}) : /* @__PURE__ */ k("div", { className: "rk-player-nav-arrows", children: [
/* @__PURE__ */ t(
"button",
{
onClick: Y,
style: {
width: 44,
height: 44,
borderRadius: "50%",
backgroundColor: "rgba(255,255,255,0.1)",
border: "none",
color: "#fff",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
"aria-label": "Previous",
children: /* @__PURE__ */ t(Pe, { size: 28 })
}
),
/* @__PURE__ */ t(
"button",
{
onClick: Z,
style: {
width: 44,
height: 44,
borderRadius: "50%",
backgroundColor: "rgba(255,255,255,0.1)",
border: "none",
color: "#fff",
cursor: "pointer",
display: "flex",
alignItems: "center",
justifyContent: "center"
},
"aria-label": "Next",
children: /* @__PURE__ */ t(Re, { size: 28 })
}
)
] })
] });
return typeof document > "u" ? ee : ye(ee, document.body);
document.body
);
}
function qe(e) {
return e.isOpen ? /* @__PURE__ */ t(Le, { children: /* @__PURE__ */ t(Ve, { ...e }) }) : null;
function Ge(n) {
return n.isOpen ? /* @__PURE__ */ e(Pe, { children: /* @__PURE__ */ e(Ke, { ...n }) }) : null;
}
export {
Ae as CloseButton,
oe as ImageSlide,
qe as ReelPlayerOverlay,
Me as SlideOverlay,
Fe as SoundButton,
Le as SoundProvider,
ie as VideoSlide,
G as useSoundState
Fe as CloseButton,
ce as ImageSlide,
Ge as ReelPlayerOverlay,
je as SlideOverlay,
Te as SoundButton,
Ye as SoundProvider,
ue as VideoSlide,
Ze as useSoundState
};

@@ -13,2 +13,5 @@ import { default as React, ReactNode } from 'react';

onActiveMediaTypeChange?: (type: 'image' | 'video') => void;
onReady?: () => void;
onWaiting?: () => void;
onError?: () => void;
renderNestedNavigation?: (props: NavigationRenderProps) => ReactNode;

@@ -15,0 +18,0 @@ renderNestedSlide?: (props: NestedSlideRenderProps) => ReactNode;

import { default as React, ReactNode } from 'react';
import { ReelApi } from '@reelkit/react';
import { MediaItem, NavigationRenderProps, NestedSlideRenderProps } from './types';
import { BaseContentItem, MediaItem, NavigationRenderProps, NestedSlideRenderProps } from './types';
/** @internal */
interface NestedSliderProps {
media: MediaItem[];
contentItem: BaseContentItem;
isParentActive: boolean;

@@ -14,2 +15,5 @@ size: [number, number];

onActiveMediaTypeChange?: (type: 'image' | 'video') => void;
onReady?: () => void;
onWaiting?: () => void;
onError?: () => void;
renderNavigation?: (props: NavigationRenderProps) => ReactNode;

@@ -16,0 +20,0 @@ renderNestedSlide?: (props: NestedSlideRenderProps) => ReactNode;

import { default as React } from 'react';
/** Props for the {@link CloseButton} sub-component. */
export interface CloseButtonProps {
/** Callback fired when the button is clicked. */
onClick: () => void;
/** CSS class name. Defaults to `"rk-player-close-btn"`. */

@@ -10,2 +8,4 @@ className?: string;

style?: React.CSSProperties;
/** Callback fired when the button is clicked. */
onClick: () => void;
}

@@ -64,5 +64,5 @@ /**

interface PlayerControlsProps {
onClose: () => void;
showSound?: boolean;
soundDisabled?: boolean;
onClose: () => void;
}

@@ -69,0 +69,0 @@ /**

@@ -9,3 +9,3 @@ import { default as React, ReactNode } from 'react';

*/
export type ReelProxyProps = Pick<ReelProps, 'transitionDuration' | 'swipeDistanceFactor' | 'loop' | 'useNavKeys' | 'enableWheel' | 'wheelDebounceMs'>;
export type ReelProxyProps = Pick<ReelProps, 'transitionDuration' | 'swipeDistanceFactor' | 'loop' | 'enableNavKeys' | 'enableWheel' | 'wheelDebounceMs'>;
/**

@@ -37,4 +37,2 @@ * Props for the {@link ReelPlayerOverlay} component.

isOpen: boolean;
/** Callback to close the overlay. Triggered by close button or Escape key. */
onClose: () => void;
/** Array of content items to display as vertical slides. */

@@ -45,10 +43,12 @@ content: T[];

/**
* Ref to access the Reel API
*/
apiRef?: React.MutableRefObject<ReelApi | null>;
/**
* Callback fired after slide change
*/
onSlideChange?: (index: number) => void;
/** Callback to close the overlay. Triggered by close button or Escape key. */
onClose: () => void;
/**
* Ref to access the Reel API
*/
apiRef?: React.MutableRefObject<ReelApi | null>;
/**
* Custom overlay on top of each slide. Replaces default SlideOverlay.

@@ -86,2 +86,12 @@ * Return `null` to hide the overlay entirely.

renderNestedSlide?: (props: NestedSlideRenderProps) => ReactNode;
/** Custom loading indicator. Replaces default wave loader. */
renderLoading?: (props: {
item: T;
activeIndex: number;
}) => ReactNode;
/** Custom error indicator. Replaces default error icon. */
renderError?: (props: {
item: T;
activeIndex: number;
}) => ReactNode;
}

@@ -88,0 +98,0 @@ /**

import { ReactNode } from 'react';
import { ReelApi } from '@reelkit/react';
import { SoundState } from './SoundState';
import { ReelApi, SoundController } from '@reelkit/react';
/** Supported media types for content items. */

@@ -72,6 +71,6 @@ export type MediaType = 'image' | 'video';

export interface ControlsRenderProps<T extends BaseContentItem> {
/** Callback to close the player overlay. */
onClose: () => void;
/** The currently active content item. */
item: T;
/** Reactive sound state for mute/unmute control. */
soundState: SoundState;
soundState: SoundController;
/** Zero-based index of the currently active slide. */

@@ -81,2 +80,4 @@ activeIndex: number;

content: T[];
/** Callback to close the player overlay. */
onClose: () => void;
}

@@ -133,6 +134,8 @@ /**

slideKey: string;
/** Report the active video element to the player for drag pause/resume. Only provided when `isActive` is true. */
onVideoRef?: (ref: HTMLVideoElement | null) => void;
/** Ref to the inner horizontal slider API, required for drag coordination in multi-media slides. */
innerSliderRef: React.MutableRefObject<ReelApi | null>;
/** Whether wheel navigation is enabled (forwarded to nested sliders). */
enableWheel?: boolean;
/** The default slide content (MediaSlide + overlay). Render this to use default rendering inside your own wrapper. */
defaultContent: ReactNode;
/** Report active media type changes in nested sliders (controls sound button visibility). Only provided when `isActive` is true. */

@@ -142,6 +145,10 @@ onActiveMediaTypeChange?: (type: 'image' | 'video') => void;

renderNestedNavigation?: (props: NavigationRenderProps) => ReactNode;
/** Whether wheel navigation is enabled (forwarded to nested sliders). */
enableWheel?: boolean;
/** The default slide content (MediaSlide + overlay). Render this to use default rendering inside your own wrapper. */
defaultContent: ReactNode;
/** Report the active video element to the player for drag pause/resume. Only provided when `isActive` is true. */
onVideoRef?: (ref: HTMLVideoElement | null) => void;
/** Signal that the slide content has finished loading. Clears the wave loader. */
onReady: () => void;
/** Signal that the slide content is buffering. Shows the wave loader. */
onWaiting: () => void;
/** Signal that the slide content failed to load. */
onError: () => void;
}

@@ -157,3 +164,3 @@ /**

* ```tsx
* renderNestedSlide={({ item, defaultContent }) => (
* renderNestedSlide={({ media, defaultContent }) => (
* <div style={{ borderRadius: 16, overflow: 'hidden' }}>

@@ -166,4 +173,6 @@ * {defaultContent}

export interface NestedSlideRenderProps {
/** The parent content item containing this nested slide. */
item: BaseContentItem;
/** The media item for this nested slide. */
item: MediaItem;
media: MediaItem;
/** Zero-based index within the nested slider. */

@@ -179,6 +188,12 @@ index: number;

slideKey: string;
/** The default slide content (ImageSlide or VideoSlide). Render this to wrap the default with your own styles. */
defaultContent: ReactNode;
/** Report the active video element for drag pause/resume. Only provided when `isInnerActive` is true. */
onVideoRef?: (ref: HTMLVideoElement | null) => void;
/** The default slide content (ImageSlide or VideoSlide). Render this to wrap the default with your own styles. */
defaultContent: ReactNode;
/** Signal that the nested slide content has finished loading. Only provided when `isInnerActive` is true. */
onReady?: () => void;
/** Signal that the nested slide content is buffering. Only provided when `isInnerActive` is true. */
onWaiting?: () => void;
/** Signal that the nested slide content failed to load. Only provided when `isInnerActive` is true. */
onError?: () => void;
}

@@ -192,2 +207,10 @@ /**

export interface NavigationRenderProps {
/** The currently active content item (main navigation). */
item: BaseContentItem;
/** The currently active media item (nested navigation only). */
media?: MediaItem;
/** Zero-based index of the currently active slide. */
activeIndex: number;
/** Total number of slides. */
count: number;
/** Navigate to the previous slide. */

@@ -197,6 +220,2 @@ onPrev: () => void;

onNext: () => void;
/** Zero-based index of the currently active slide. */
activeIndex: number;
/** Total number of slides. */
count: number;
}

@@ -20,4 +20,2 @@ import { default as React, CSSProperties } from 'react';

slideKey: string;
/** Callback to report the shared video element ref to the parent for drag pause/resume. */
onVideoRef?: (ref: HTMLVideoElement | null) => void;
/** Additional CSS class for the root container. */

@@ -27,3 +25,13 @@ className?: string;

style?: CSSProperties;
/** Callback to report the shared video element ref to the parent for drag pause/resume. */
onVideoRef?: (ref: HTMLVideoElement | null) => void;
/** Called when the video is ready to play (buffering complete). */
onReady?: () => void;
/** Called when the video stalls (buffering mid-playback). */
onWaiting?: () => void;
/** Called when the video fails to load or play. */
onError?: () => void;
}
/** @internal */
export declare const shared: import('@reelkit/react').SharedVideoInstance;
/**

@@ -30,0 +38,0 @@ * Renders a single video slide using a shared `<video>` element for iOS

{
"name": "@reelkit/react-reel-player",
"version": "0.2.1",
"version": "0.3.0",
"type": "module",

@@ -56,7 +56,7 @@ "sideEffects": [

"peerDependencies": {
"@reelkit/react": "*",
"react": ">=18",
"react-dom": ">=18",
"@reelkit/react": "^0.3.0",
"react": ">=18.0.0",
"react-dom": ">=18.0.0",
"lucide-react": ">=0.400.0"
}
}
+26
-21

@@ -5,7 +5,8 @@ # @reelkit/react-reel-player

<a href="https://www.npmjs.com/package/@reelkit/react-reel-player"><img src="https://img.shields.io/npm/v/@reelkit/react-reel-player?color=6366f1&label=npm" alt="npm" /></a>
<img src="https://img.shields.io/badge/gzip-3.7%20kB-6366f1" alt="Bundle size" />
<img src="https://img.shields.io/badge/gzip-4.4%20kB-6366f1" alt="Bundle size" />
<img src="https://img.shields.io/badge/coverage-90%25-brightgreen" alt="Coverage" />
<a href="https://github.com/KonstantinKai/reelkit"><img src="https://img.shields.io/github/stars/KonstantinKai/reelkit?style=social" alt="Star on GitHub" /></a>
</p>
Drop-in Instagram Reels / TikTok-style video player for React. Opens as a full-screen overlay with vertical swipe navigation. Handles video autoplay, sound continuity on iOS, and multi-media posts out of the box. ~3.7 kB gzip.
Drop-in Instagram Reels / TikTok-style video player for React. Opens as a full-screen overlay with vertical swipe navigation. Handles video autoplay, sound continuity on iOS, and multi-media posts. ~4.4 kB gzip.

@@ -85,3 +86,3 @@ ## Installation

| `loop` | `boolean` | `false` | Enable infinite loop |
| `useNavKeys` | `boolean` | `true` | Enable keyboard navigation |
| `enableNavKeys` | `boolean` | `true` | Enable keyboard navigation |
| `enableWheel` | `boolean` | `true` | Enable mouse wheel navigation |

@@ -221,20 +222,24 @@ | `wheelDebounceMs` | `number` | `200` | Wheel debounce duration (ms) |

```css
.rk-reel-overlay {
} /* Overlay background */
.rk-reel-container {
} /* Player container */
.rk-player-nav-arrows {
} /* Desktop nav arrows */
.rk-player-close-btn {
} /* Close button */
.rk-player-sound-btn {
} /* Sound toggle */
.rk-video-slide-container {
}
.rk-video-slide-loader {
}
.rk-video-slide-poster {
}
```
| Class | Description |
| ------------------------------------ | --------------------------------- |
| `.rk-reel-overlay` | Overlay background |
| `.rk-reel-container` | Player container |
| `.rk-reel-slide-wrapper` | Slide wrapper |
| `.rk-player-nav-arrows` | Desktop nav arrows container |
| `.rk-player-nav-btn` | Individual nav arrow button |
| `.rk-player-close-btn` | Close button |
| `.rk-player-sound-btn` | Sound toggle |
| `.rk-reel-slide-overlay` | Per-slide overlay (author, likes) |
| `.rk-reel-slide-overlay-author` | Author row |
| `.rk-reel-slide-overlay-avatar` | Author avatar |
| `.rk-reel-slide-overlay-name` | Author name |
| `.rk-reel-slide-overlay-description` | Slide description |
| `.rk-reel-slide-overlay-likes` | Like count |
| `.rk-nested-slider-inner` | Nested horizontal slider |
| `.rk-nested-nav` | Nested nav arrows |
| `.rk-nested-indicator` | Nested slider dot indicator |
| `.rk-video-slide-container` | Video slide wrapper |
| `.rk-video-slide-element` | Video element |
| `.rk-video-slide-poster` | Video poster image |
| `.rk-video-slide-loader` | Video loading indicator |

@@ -241,0 +246,0 @@ ## Documentation

import { default as React } from 'react';
import { Signal } from '@reelkit/react';
/**
* Reactive sound state shared across the reel player.
*
* Provided automatically by `ReelPlayerOverlay` via {@link SoundProvider}.
* Access it in custom controls with the {@link useSoundState} hook.
*
* Both `muted` and `disabled` are reactive {@link Signal} instances — subscribe
* to them with `signal.observe()` or read with `signal.value`.
*/
export interface SoundState {
/** Whether audio is muted. Defaults to `true` (muted). */
muted: Signal<boolean>;
/**
* Whether sound controls should be hidden/disabled.
* Set to `true` when the active slide has no video content.
*/
disabled: Signal<boolean>;
/** Toggles the muted state. */
toggle: () => void;
}
/** @internal */
export declare const SoundContext: React.Context<SoundState | null>;
interface SoundProviderProps {
children: React.ReactNode;
}
/**
* Context provider for {@link SoundState}.
*
* Automatically wraps `ReelPlayerOverlay` content — you don't need to
* render this manually unless building a fully custom player.
*
* Creates a single `SoundState` instance with `muted: true` and
* `disabled: false` as initial values.
*/
export declare const SoundProvider: React.FC<SoundProviderProps>;
export {};
import { SoundState } from './SoundState';
/**
* Hook to access the current {@link SoundState} from context.
*
* Must be called inside a {@link SoundProvider} (automatically provided
* by `ReelPlayerOverlay`). Useful in custom controls built via `renderControls`.
*
* @throws Error if called outside of a `SoundProvider`.
*
* @example
* ```tsx
* const soundState = useSoundState();
* console.log(soundState.muted.value); // true or false
* soundState.toggle(); // flip mute state
* ```
*/
export declare const useSoundState: () => SoundState;