parevo-interactive-video
Version:
Framework-agnostic interactive video library with quiz overlays for React, Vue, and vanilla JS
125 lines (121 loc) • 5.4 kB
JavaScript
import { jsx, jsxs } from 'react/jsx-runtime';
import { useRef, useState, useCallback, useEffect } from 'react';
import videojs from 'video.js';
const QuizOverlay = ({ question, onAnswer, isVisible }) => {
if (!isVisible)
return null;
return (jsx("div", { className: "interactive-video-quiz-overlay", children: jsxs("div", { className: "quiz-content", children: [jsx("h3", { className: "quiz-question", children: question.question }), jsx("div", { className: "quiz-options", children: question.options.map((option) => (jsx("button", { className: "quiz-option", onClick: () => onAnswer(option.id), children: option.text }, option.id))) })] }) }));
};
const InteractiveVideo = ({ src, config, className = '', width = 640, height = 360, autoplay = false, muted = false, controls = true, onQuestionAnswered, onVideoEnd }) => {
const videoRef = useRef(null);
const playerRef = useRef(null);
const containerRef = useRef(null);
const [currentQuestion, setCurrentQuestion] = useState(null);
const [showQuiz, setShowQuiz] = useState(false);
const [answeredQuestions, setAnsweredQuestions] = useState(new Set());
const [isPlayerReady, setIsPlayerReady] = useState(false);
const handleAnswer = useCallback((optionId) => {
if (!currentQuestion)
return;
const selectedOption = currentQuestion.options.find(opt => opt.id === optionId);
if (!selectedOption)
return;
const isCorrect = selectedOption.isCorrect;
setAnsweredQuestions(prev => new Set(prev).add(currentQuestion.id));
setShowQuiz(false);
setCurrentQuestion(null);
onQuestionAnswered?.(currentQuestion.id, optionId, isCorrect);
if (playerRef.current) {
if (isCorrect) {
playerRef.current.play();
}
else {
playerRef.current.currentTime(currentQuestion.rewindTime);
playerRef.current.play();
}
}
}, [currentQuestion, onQuestionAnswered]);
const checkForQuestions = useCallback((currentTime) => {
if (showQuiz || !isPlayerReady)
return;
const triggerQuestion = config.questions.find(q => !answeredQuestions.has(q.id) &&
currentTime >= q.triggerTime &&
currentTime < q.triggerTime + 1);
if (triggerQuestion) {
setCurrentQuestion(triggerQuestion);
setShowQuiz(true);
playerRef.current?.pause();
}
}, [config.questions, answeredQuestions, showQuiz, isPlayerReady]);
// Video player'ı initialize et
useEffect(() => {
if (!videoRef.current)
return;
// Eğer player zaten varsa ve aynı src'yi kullanıyorsa, tekrar initialize etme
if (playerRef.current) {
const currentSrc = playerRef.current.src();
if (currentSrc === src) {
return;
}
// Farklı src ise dispose et
try {
playerRef.current.dispose();
}
catch (error) {
console.warn('Error disposing existing player:', error);
}
playerRef.current = null;
}
const initPlayer = () => {
if (!videoRef.current || playerRef.current)
return;
try {
const player = videojs(videoRef.current, {
width,
height,
autoplay,
muted,
controls,
sources: [{
src,
type: 'video/mp4'
}]
});
playerRef.current = player;
player.ready(() => {
setIsPlayerReady(true);
});
player.on('timeupdate', () => {
const currentTime = player.currentTime();
if (currentTime !== undefined) {
checkForQuestions(currentTime);
}
});
player.on('ended', () => {
onVideoEnd?.();
});
}
catch (error) {
console.error('Error initializing video player:', error);
}
};
// Bir frame bekle ki DOM kesinlikle hazır olsun
const timeoutId = setTimeout(initPlayer, 0);
return () => {
clearTimeout(timeoutId);
if (playerRef.current) {
try {
playerRef.current.dispose();
}
catch (error) {
// Sessizce handle et
}
playerRef.current = null;
}
setIsPlayerReady(false);
};
}, [src, width, height, autoplay, muted, controls, checkForQuestions, onVideoEnd]);
return (jsxs("div", { ref: containerRef, className: `interactive-video-container ${className}`, style: { position: 'relative' }, children: [jsx("video", { ref: videoRef, className: "video-js vjs-default-skin", "data-setup": "{}", style: { width, height }, preload: "auto" }), jsx(QuizOverlay, { question: currentQuestion, onAnswer: handleAnswer, isVisible: showQuiz })] }));
};
export { InteractiveVideo, QuizOverlay };
//# sourceMappingURL=index.esm.js.map