unified-video-framework
Version:
Cross-platform video player framework supporting iOS, Android, Web, Smart TVs (Samsung/LG), Roku, and more
101 lines • 4.57 kB
JavaScript
import React, { useMemo } from 'react';
import { SEGMENT_COLORS } from '../../chapters/types/ChapterTypes.js';
export const ChapterProgress = ({ chapters, progress = 0, buffered = 0, showMarkers = true, markerColors = {}, onMarkerClick, onProgressClick, className = '', style = {}, interactive = true }) => {
const markers = useMemo(() => {
if (!chapters || !showMarkers)
return [];
const colors = { ...SEGMENT_COLORS, ...markerColors };
return chapters.segments
.filter(segment => segment.type !== 'content')
.map(segment => ({
segment,
position: (segment.startTime / chapters.duration) * 100,
color: colors[segment.type] || '#ffffff',
label: segment.title || segment.type
}))
.sort((a, b) => a.position - b.position);
}, [chapters, showMarkers, markerColors]);
const handleProgressClick = (event) => {
if (!interactive || !onProgressClick)
return;
const rect = event.currentTarget.getBoundingClientRect();
const x = event.clientX - rect.left;
const percentage = (x / rect.width) * 100;
onProgressClick(Math.max(0, Math.min(100, percentage)));
};
const handleMarkerClick = (event, segment) => {
event.stopPropagation();
onMarkerClick?.(segment);
};
const progressClasses = [
'uvf-chapter-progress',
interactive ? 'interactive' : '',
className
].filter(Boolean).join(' ');
return (React.createElement("div", { className: progressClasses, style: {
position: 'relative',
width: '100%',
height: '4px',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
borderRadius: '2px',
cursor: interactive ? 'pointer' : 'default',
...style
}, onClick: handleProgressClick },
React.createElement("div", { className: "uvf-chapter-progress-buffered", style: {
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: `${Math.max(0, Math.min(100, buffered))}%`,
backgroundColor: 'rgba(255, 255, 255, 0.4)',
borderRadius: 'inherit',
transition: 'width 0.3s ease'
} }),
React.createElement("div", { className: "uvf-chapter-progress-current", style: {
position: 'absolute',
top: 0,
left: 0,
height: '100%',
width: `${Math.max(0, Math.min(100, progress))}%`,
background: 'linear-gradient(90deg, var(--uvf-accent-1, #ff5722) 0%, var(--uvf-accent-2, #ff8a50) 100%)',
borderRadius: 'inherit',
transition: 'width 0.1s ease'
} }),
markers.map((marker, index) => (React.createElement("div", { key: `${marker.segment.id}-${index}`, className: `uvf-chapter-progress-marker uvf-marker-${marker.segment.type}`, style: {
position: 'absolute',
top: '50%',
left: `${marker.position}%`,
width: '3px',
height: '150%',
backgroundColor: marker.color,
transform: 'translate(-50%, -50%)',
cursor: 'pointer',
borderRadius: '1px',
zIndex: 10,
transition: 'all 0.2s ease'
}, title: `${marker.label} (${formatTime(marker.segment.startTime)})`, onClick: (e) => handleMarkerClick(e, marker.segment), onMouseEnter: (e) => {
const element = e.currentTarget;
element.style.width = '4px';
element.style.height = '200%';
element.style.boxShadow = `0 0 8px ${marker.color}`;
}, onMouseLeave: (e) => {
const element = e.currentTarget;
element.style.width = '3px';
element.style.height = '150%';
element.style.boxShadow = 'none';
} })))));
};
function formatTime(seconds) {
if (!seconds || isNaN(seconds))
return '0:00';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
if (hours > 0) {
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
else {
return `${minutes}:${secs.toString().padStart(2, '0')}`;
}
}
//# sourceMappingURL=ChapterProgress.js.map