react-video-seek-slider
Version:
A lightweight, dependency-free React video seek slider component with YouTube-like functionality, time codes, and video preview support
1 lines • 27.4 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../src/utils/getPositionPercent.ts","../src/utils/isInRange.ts","../src/utils/getTimeScale.ts","../src/components/timeCodeItem.tsx","../src/utils/positionToMs.ts","../src/utils/getEndTimeByIndex.ts","../src/components/timeCodes.tsx","../src/utils/getHoverTimePosition.ts","../src/utils/secondsToTime.ts","../src/utils/timeToTimeString.ts","../src/components/hoverTimeWithPreview.tsx","../src/components/thumb.tsx","../src/index.tsx"],"sourcesContent":["export function getPositionPercent(max: number, current: number): number {\n const divider = max || -1; // prevent division by zero\n return (current * 100) / divider;\n}\n","export const isInRange = (time: number, start: number, end: number): boolean =>\n time >= start && time <= end;\n","import { isInRange } from './isInRange';\n\nexport function getTimeScale(\n currentTime: number,\n startTime: number,\n endTime: number,\n isTimePassed: boolean\n): number {\n const isActiveTime = isInRange(currentTime, startTime, endTime);\n const timeDiff = endTime - startTime;\n const timeDiffWithCurrent = currentTime - startTime;\n const currentScalePercent = isActiveTime ? timeDiffWithCurrent / timeDiff : 0;\n\n return isTimePassed ? 1 : currentScalePercent;\n}\n","import React, { memo } from 'react';\nimport { getPositionPercent } from '../utils/getPositionPercent';\nimport { getTimeScale } from '../utils/getTimeScale';\n\nexport interface TimeCode {\n fromMs: number;\n description: string;\n}\n\nexport interface Props {\n currentTime: number;\n seekHoverTime: number;\n bufferTime: number;\n startTime: number;\n endTime: number;\n maxTime: number;\n label?: string;\n isTimePassed?: boolean;\n isBufferPassed?: boolean;\n isHoverPassed?: boolean;\n onHover?: (label: string) => void;\n withGap?: boolean;\n}\n\nexport const TimeCodeItem: React.FC<Props> = memo(\n ({\n label = '',\n startTime,\n maxTime,\n endTime,\n currentTime,\n seekHoverTime,\n bufferTime,\n isTimePassed = false,\n isBufferPassed = false,\n isHoverPassed = false,\n onHover = () => undefined,\n withGap,\n }) => {\n const positionPercent = getPositionPercent(maxTime, startTime);\n const timeDiff = endTime - startTime;\n const widthPercent = (timeDiff / maxTime) * 100;\n const mainClassName = `main${withGap ? ' with-gap' : ''}`;\n\n const currentTimeScale = getTimeScale(\n currentTime,\n startTime,\n endTime,\n isTimePassed\n );\n\n const seekHoverTimeScale = getTimeScale(\n seekHoverTime,\n startTime,\n endTime,\n isHoverPassed\n );\n\n const bufferTimeScale = getTimeScale(\n bufferTime,\n startTime,\n endTime,\n isBufferPassed\n );\n\n const handleMouseMove = (): void => onHover(label);\n\n return (\n <div\n className={mainClassName}\n onMouseMove={handleMouseMove}\n style={{\n width: `${widthPercent}%`,\n left: `${positionPercent}%`,\n }}\n >\n <div\n className=\"inner-seek-block buffered\"\n data-test-id=\"test-buffered\"\n style={{ transform: `scaleX(${bufferTimeScale})` }}\n />\n\n <div\n className=\"inner-seek-block seek-hover\"\n data-test-id=\"test-seek-hover\"\n style={{ transform: `scaleX(${seekHoverTimeScale})` }}\n />\n\n <div\n className=\"inner-seek-block connect\"\n style={{ transform: `scaleX(${currentTimeScale})` }}\n />\n </div>\n );\n }\n);\n","export function positionToMs(\n max: number,\n position: number,\n trackWidth: number\n): number {\n const percent = (position * 100) / trackWidth;\n return Math.floor(+(percent * (max / 100)));\n}\n","import { TimeCode } from '../components/timeCodeItem';\n\nexport const getEndTimeByIndex = (\n timeCodes: TimeCode[],\n index: number,\n max: number\n): number => (index + 1 < timeCodes.length ? timeCodes[index + 1].fromMs : max);\n","import { useCallback, useEffect } from 'react';\nimport { isInRange } from '../utils/isInRange';\nimport { positionToMs } from '../utils/positionToMs';\nimport { getEndTimeByIndex } from '../utils/getEndTimeByIndex';\nimport { TimeCode, TimeCodeItem } from './timeCodeItem';\n\nexport interface Props {\n max: number;\n currentTime: number;\n bufferTime: number;\n seekHoverPosition: number;\n timeCodes: TimeCode[] | undefined;\n trackWidth: number;\n mobileSeeking: boolean;\n label: string;\n setLabel: React.Dispatch<React.SetStateAction<string>>;\n}\n\nexport const TimeCodes: React.FC<Props> = ({\n max = 1000,\n currentTime = 0,\n bufferTime = 0,\n seekHoverPosition = 0,\n timeCodes,\n trackWidth,\n mobileSeeking,\n label,\n setLabel,\n}) => {\n const hoverTimeValue = positionToMs(max, seekHoverPosition, trackWidth);\n\n const handleLabelChange = useCallback(\n (currentLabel: string) => {\n if (label !== currentLabel) {\n setLabel(currentLabel);\n }\n },\n [label]\n );\n\n useEffect(() => {\n if (!mobileSeeking) {\n return;\n }\n\n const currentCode = timeCodes?.find(({ fromMs }, index) => {\n const endTime = getEndTimeByIndex(timeCodes, index, max);\n\n return isInRange(currentTime, fromMs, endTime);\n });\n\n if (currentCode?.description !== label) {\n setLabel(currentCode?.description || '');\n }\n }, [currentTime, label, max, timeCodes]);\n\n return (\n <>\n {timeCodes?.map(({ fromMs, description }, index) => {\n const endTime = getEndTimeByIndex(timeCodes, index, max);\n\n const isTimePassed = endTime <= currentTime;\n const isBufferPassed = endTime <= bufferTime;\n const isHoverPassed = endTime <= hoverTimeValue;\n\n let inRange = isInRange(currentTime, fromMs, endTime);\n const newCurrentTime = isTimePassed || !inRange ? 0 : currentTime;\n\n inRange = isInRange(bufferTime, fromMs, endTime);\n const newBufferTime = isBufferPassed || !inRange ? 0 : bufferTime;\n\n inRange = isInRange(hoverTimeValue, fromMs, endTime);\n const newHoverTime = isHoverPassed || !inRange ? 0 : hoverTimeValue;\n\n return (\n <TimeCodeItem\n key={fromMs}\n label={description}\n maxTime={max}\n startTime={fromMs}\n endTime={endTime}\n isTimePassed={isTimePassed}\n isBufferPassed={isBufferPassed}\n isHoverPassed={isHoverPassed}\n currentTime={newCurrentTime}\n bufferTime={newBufferTime}\n seekHoverTime={newHoverTime}\n onHover={handleLabelChange}\n withGap={true}\n />\n );\n })}\n </>\n );\n};\n","export function getHoverTimePosition(\n seekHoverPosition: number,\n hoverTimeElement: HTMLDivElement | null,\n trackWidth: number,\n limitTimeTooltipBySides: boolean\n): { transform: string } {\n let position = 0;\n\n if (hoverTimeElement) {\n position = seekHoverPosition - hoverTimeElement.offsetWidth / 2;\n\n if (limitTimeTooltipBySides) {\n if (position < 0) {\n position = 0;\n } else if (position + hoverTimeElement.offsetWidth > trackWidth) {\n position = trackWidth - hoverTimeElement.offsetWidth;\n }\n }\n }\n\n return { transform: `translateX(${position}px)` };\n}\n","export interface Time {\n hh: string;\n mm: string;\n ss: string;\n}\n\nexport function millisecondsToTime(ms: number, offset = 0): Time {\n const roundedSeconds = Math.round(ms / 1000 + offset);\n\n const hours: number = Math.floor(roundedSeconds / 3600);\n const divirsForMinutes: number = roundedSeconds % 3600;\n const minutes: number = Math.floor(divirsForMinutes / 60);\n const sec: number = Math.ceil(divirsForMinutes % 60);\n\n return {\n hh: hours.toString(),\n mm: minutes < 10 ? `0${minutes}` : minutes.toString(),\n ss: sec < 10 ? `0${sec}` : sec.toString(),\n };\n}\n","import { millisecondsToTime } from './secondsToTime';\n\nexport function timeToTimeString(\n max: number,\n seekHoverTime: number,\n offset = 0,\n minutesPrefix = '',\n secondsPrefix = ''\n): string {\n const times = millisecondsToTime(seekHoverTime, offset);\n\n if (max + offset < 60 * 1000) {\n return secondsPrefix + times.ss;\n }\n\n if (max + offset < 3600 * 1000) {\n return `${minutesPrefix + times.mm}:${times.ss}`;\n }\n\n return `${times.hh}:${times.mm}:${times.ss}`;\n}\n","import { useRef } from 'react';\nimport { getHoverTimePosition } from '../utils/getHoverTimePosition';\nimport { timeToTimeString } from '../utils/timeToTimeString';\n\ninterface Props {\n max: number;\n hoverTimeValue: number;\n trackWidth: number;\n seekHoverPosition: number;\n offset: number;\n isThumbActive: boolean;\n limitTimeTooltipBySides: boolean;\n label: string;\n secondsPrefix?: string;\n minutesPrefix?: string;\n getPreviewScreenUrl?: (hoverTimeValue: number) => string;\n}\n\nexport const HoverTimeWithPreview: React.FC<Props> = ({\n max,\n hoverTimeValue,\n offset,\n trackWidth,\n seekHoverPosition,\n isThumbActive,\n limitTimeTooltipBySides,\n label,\n minutesPrefix,\n secondsPrefix,\n getPreviewScreenUrl,\n}) => {\n const hoverTimeElement = useRef<HTMLDivElement>(null);\n const hoverTimeClassName = isThumbActive ? 'hover-time active' : 'hover-time';\n\n const hoverTimePosition = getHoverTimePosition(\n seekHoverPosition,\n hoverTimeElement?.current,\n trackWidth,\n limitTimeTooltipBySides\n );\n\n const hoverTimeString = timeToTimeString(\n max,\n hoverTimeValue,\n offset,\n minutesPrefix,\n secondsPrefix\n );\n\n return (\n <div\n className={hoverTimeClassName}\n style={hoverTimePosition}\n ref={hoverTimeElement}\n data-testid=\"hover-time\"\n >\n {isThumbActive && getPreviewScreenUrl && (\n <div\n className=\"preview-screen\"\n style={{\n backgroundImage: `url(${getPreviewScreenUrl(hoverTimeValue)})`,\n }}\n />\n )}\n {label && <div>{label}</div>}\n {hoverTimeString}\n </div>\n );\n};\n","interface Props {\n max: number;\n currentTime: number;\n isThumbActive: boolean;\n}\n\nexport const Thumb: React.FC<Props> = ({ max, currentTime, isThumbActive }) => {\n const getThumbHandlerPosition = (): { left: string } => {\n const thumbConstantOffset = -6;\n const leftPosition = (currentTime / max) * 100;\n\n return {\n left: `calc(${leftPosition}% + ${thumbConstantOffset}px)`,\n };\n };\n\n return (\n <div\n className={isThumbActive ? 'thumb active' : 'thumb active'}\n data-testid=\"testThumb\"\n style={getThumbHandlerPosition()}\n >\n <div className=\"handler\" />\n </div>\n );\n};\n","import './ui-video-seek-slider.scss';\nimport { useEffect, useRef, useState } from 'react';\nimport { TimeCodeItem } from './components/timeCodeItem';\nimport { isInRange } from './utils/isInRange';\nimport { positionToMs } from './utils/positionToMs';\nimport { getEndTimeByIndex } from './utils/getEndTimeByIndex';\nimport { TimeCodes } from './components/timeCodes';\nimport { HoverTimeWithPreview } from './components/hoverTimeWithPreview';\nimport { Thumb } from './components/thumb';\n\nexport interface TimeCode {\n fromMs: number;\n description: string;\n}\n\nexport interface Props {\n max: number;\n currentTime: number;\n bufferTime?: number;\n offset?: number;\n timeCodes?: TimeCode[];\n hideThumbTooltip?: boolean;\n limitTimeTooltipBySides?: boolean;\n secondsPrefix?: string;\n minutesPrefix?: string;\n onChange: (time: number, offsetTime: number) => void;\n getPreviewScreenUrl?: (hoverTimeValue: number) => string;\n}\n\nexport const VideoSeekSlider: React.FC<Props> = ({\n max = 1000,\n currentTime = 0,\n bufferTime = 0,\n hideThumbTooltip = false,\n offset = 0,\n secondsPrefix = '',\n minutesPrefix = '',\n limitTimeTooltipBySides = true,\n timeCodes,\n onChange = () => undefined,\n getPreviewScreenUrl,\n}) => {\n const [seekHoverPosition, setSeekHoverPosition] = useState(0);\n const [label, setLabel] = useState('');\n const seeking = useRef(false);\n const mobileSeeking = useRef(false);\n const trackElement = useRef<HTMLDivElement>(null);\n\n const trackWidth = trackElement.current?.offsetWidth || 0;\n const isThumbActive = seekHoverPosition > 0 || seeking.current;\n const hoverTimeValue = positionToMs(max, seekHoverPosition, trackWidth);\n\n const changeCurrentTimePosition = (pageX: number): void => {\n const clientRect = trackElement.current?.getBoundingClientRect();\n const left = clientRect?.left || 0;\n const width = clientRect?.width || 0;\n\n let position = pageX - left;\n position = position < 0 ? 0 : position;\n position = position > width ? width : position;\n\n const percent = (position * 100) / width;\n const time = +(percent * (max / 100)).toFixed(0);\n\n setSeekHoverPosition(position);\n onChange(time, time + offset);\n };\n\n const handleTouchSeeking = (event: TouchEvent): void => {\n event.preventDefault();\n event.stopPropagation();\n\n if (!mobileSeeking.current) {\n return;\n }\n\n const { changedTouches } = event;\n\n let pageX = changedTouches?.[changedTouches.length - 1]?.pageX || 0;\n\n pageX = pageX < 0 ? 0 : pageX;\n\n changeCurrentTimePosition(pageX);\n };\n\n const handleSeeking = (event: MouseEvent): void => {\n if (seeking.current) {\n changeCurrentTimePosition(event.pageX);\n }\n };\n\n const handleTrackHover = (\n clear: boolean,\n event: React.MouseEvent<HTMLDivElement, MouseEvent>\n ): void => {\n const left = trackElement.current?.getBoundingClientRect().left || 0;\n const position = clear ? 0 : event.pageX - left;\n\n setSeekHoverPosition(position);\n };\n\n const setMobileSeeking = (state = true): void => {\n mobileSeeking.current = state;\n setSeekHoverPosition(state ? seekHoverPosition : 0);\n };\n\n const setSeeking = (state: boolean, event: MouseEvent): void => {\n event.preventDefault();\n\n handleSeeking(event);\n seeking.current = state;\n\n setSeekHoverPosition(state ? seekHoverPosition : 0);\n };\n\n const mouseSeekingHandler = (event: MouseEvent): void => {\n setSeeking(false, event);\n };\n\n const mobileTouchSeekingHandler = (): void => {\n setMobileSeeking(false);\n };\n\n useEffect(() => {\n if (!mobileSeeking.current) {\n return;\n }\n\n const currentCode = timeCodes?.find(({ fromMs }, index) => {\n const endTime = getEndTimeByIndex(timeCodes, index, max);\n\n return isInRange(currentTime, fromMs, endTime);\n });\n\n if (currentCode?.description !== label) {\n setLabel(currentCode?.description || '');\n }\n }, [currentTime, label, max, timeCodes]);\n\n useEffect(() => {\n window.addEventListener('mousemove', handleSeeking);\n window.addEventListener('mouseup', mouseSeekingHandler);\n window.addEventListener('touchmove', handleTouchSeeking);\n window.addEventListener('touchend', mobileTouchSeekingHandler);\n\n return () => {\n window.removeEventListener('mousemove', handleSeeking);\n window.removeEventListener('mouseup', mouseSeekingHandler);\n window.removeEventListener('touchmove', handleTouchSeeking);\n window.removeEventListener('touchend', mobileTouchSeekingHandler);\n };\n }, [max, offset, trackWidth]);\n\n return (\n <div className=\"ui-video-seek-slider\">\n <div\n className={isThumbActive ? 'track active' : 'track'}\n ref={trackElement}\n onMouseMove={(event) => handleTrackHover(false, event)}\n onMouseLeave={(event) => handleTrackHover(true, event)}\n onMouseDown={(event) => setSeeking(true, event as any)}\n onTouchStart={() => setMobileSeeking(true)}\n data-testid=\"main-track\"\n >\n {Boolean(timeCodes?.length) && (\n <TimeCodes\n currentTime={currentTime}\n max={max}\n bufferTime={bufferTime}\n seekHoverPosition={seekHoverPosition}\n timeCodes={timeCodes}\n mobileSeeking={mobileSeeking.current}\n trackWidth={trackWidth}\n label={label}\n setLabel={setLabel}\n />\n )}\n\n {!timeCodes && (\n <TimeCodeItem\n maxTime={max}\n startTime={0}\n endTime={max}\n currentTime={currentTime}\n bufferTime={bufferTime}\n seekHoverTime={hoverTimeValue}\n />\n )}\n </div>\n\n {!hideThumbTooltip && (\n <HoverTimeWithPreview\n max={max}\n hoverTimeValue={hoverTimeValue}\n isThumbActive={isThumbActive}\n label={label}\n limitTimeTooltipBySides={limitTimeTooltipBySides}\n offset={offset}\n seekHoverPosition={seekHoverPosition}\n trackWidth={trackWidth}\n getPreviewScreenUrl={getPreviewScreenUrl}\n minutesPrefix={minutesPrefix}\n secondsPrefix={secondsPrefix}\n />\n )}\n\n <Thumb\n max={max}\n currentTime={currentTime}\n isThumbActive={isThumbActive}\n />\n </div>\n );\n};\n"],"names":["getPositionPercent","max","current","divider","isInRange","time","start","end","getTimeScale","currentTime","startTime","endTime","isTimePassed","isActiveTime","timeDiff","timeDiffWithCurrent","currentScalePercent","TimeCodeItem","memo","label","maxTime","seekHoverTime","bufferTime","isBufferPassed","isHoverPassed","onHover","withGap","positionPercent","widthPercent","mainClassName","currentTimeScale","seekHoverTimeScale","bufferTimeScale","jsxs","jsx","positionToMs","position","trackWidth","percent","getEndTimeByIndex","timeCodes","index","TimeCodes","seekHoverPosition","mobileSeeking","setLabel","hoverTimeValue","handleLabelChange","useCallback","currentLabel","useEffect","currentCode","fromMs","Fragment","description","inRange","newCurrentTime","newBufferTime","getHoverTimePosition","hoverTimeElement","limitTimeTooltipBySides","millisecondsToTime","ms","offset","roundedSeconds","hours","divirsForMinutes","minutes","sec","timeToTimeString","minutesPrefix","secondsPrefix","times","HoverTimeWithPreview","isThumbActive","getPreviewScreenUrl","useRef","hoverTimeClassName","hoverTimePosition","hoverTimeString","Thumb","VideoSeekSlider","hideThumbTooltip","onChange","setSeekHoverPosition","useState","seeking","trackElement","changeCurrentTimePosition","pageX","clientRect","left","width","handleTouchSeeking","event","changedTouches","handleSeeking","handleTrackHover","clear","setMobileSeeking","state","setSeeking","mouseSeekingHandler","mobileTouchSeekingHandler"],"mappings":"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"}