UNPKG

@react-av/editor

Version:

Editor Timeline Components built on React AV.

137 lines 10.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TimelineHeader = TimelineHeader; const jsx_runtime_1 = require("react/jsx-runtime"); const Media = __importStar(require("@react-av/core")); const react_1 = require("react"); const controls_1 = require("@react-av/controls"); const vtt_core_1 = require("@react-av/vtt-core"); const use_resize_observer_1 = __importDefault(require("use-resize-observer")); const TimelineOverflowContainer_1 = require("./TimelineOverflowContainer"); const TimelineEditor_1 = require("./TimelineEditor"); function parseTimestampString(timestamp) { const [hms, ms] = timestamp.split("."); if (!hms) { if (ms) { const msInt = parseInt(ms); if (Number.isNaN(msInt)) { return 0; } return msInt / 1000; } return 0; } let timeUnits = hms.split(":"); if (timeUnits.length > 3) { // use only the last 3 timeUnits = timeUnits.slice(timeUnits.length - 3); } if (timeUnits.length < 3) { // pad with 0s timeUnits = new Array(3 - timeUnits.length).fill("0").concat(timeUnits); } const [hours, minutes, seconds] = timeUnits.map((unit) => parseInt(unit)); const msInt = ms ? parseInt(ms) : 0; if (Number.isNaN(hours) || Number.isNaN(minutes) || Number.isNaN(seconds) || Number.isNaN(msInt)) return 0; return hours * 3600 + minutes * 60 + seconds + msInt / 1000; } function TimelineHeader({ styling }) { const { timelineInterval: interval } = (0, TimelineEditor_1.useTimelineEditorContext)(); const [currentTime, setCurrentTime] = Media.useMediaCurrentTimeFine(); const [internalTime, setInternalTime] = (0, react_1.useState)(false); const [, setPlaying] = Media.useMediaPlaying(); const duration = Media.useMediaDuration(); const [anchor, setAnchor] = (0, react_1.useState)(0); const [anchorTime, setAnchorTime] = (0, react_1.useState)(0); const [dragging, setDragging] = (0, react_1.useState)(false); const timelineIntervalCount = (0, react_1.useMemo)(() => { if (!duration) return 0; return Math.ceil(duration / interval); }, [duration, interval]); const { width: trackWidth, ref: trackRef } = (0, use_resize_observer_1.default)(); const { width: containerWidth, ref: containerRef } = (0, use_resize_observer_1.default)(); const { width: indicatorWidth, ref: indicatorRef } = (0, use_resize_observer_1.default)(); const { indicators, intervalWidth } = (0, react_1.useMemo)(() => { const indicators = []; const includeHours = Boolean(duration && duration > 3600); for (let i = 0; i < timelineIntervalCount; i++) { indicators.push((0, jsx_runtime_1.jsx)("div", { style: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampIndicator) === 'string' ? {} : styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampIndicator, className: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampIndicator) === 'string' ? styling.timelineHeaderTimestampIndicator : undefined, children: (0, controls_1.toTimestampString)(i * interval, includeHours) }, i)); } const finalIntervalTime = (timelineIntervalCount - 1) * interval; const finalIntervalWidth = Math.abs(((duration || 0) - finalIntervalTime) / interval * 8); if (duration) { indicators.push((0, jsx_runtime_1.jsx)("div", { style: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampIndicator) === 'string' ? {} : styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampIndicator, className: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampIndicator) === 'string' ? styling.timelineHeaderTimestampIndicator : undefined, ref: indicatorRef, children: (0, controls_1.toTimestampString)(duration, includeHours) }, timelineIntervalCount)); } // the second last indicator must be made trasparent if (indicators.length > 1) { indicators[indicators.length - 2] = (0, react_1.cloneElement)(indicators[indicators.length - 2], { style: Object.assign(Object.assign({}, indicators[indicators.length - 2].props.style), { color: 'transparent' }) }); } return { indicators, intervalWidth: finalIntervalWidth }; }, [duration, interval, timelineIntervalCount, styling]); return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampInputContainer) === 'string' ? {} : styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampInputContainer, className: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampInputContainer) === 'string' ? styling.timelineHeaderTimestampInputContainer : undefined, children: (0, jsx_runtime_1.jsx)("input", { className: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampInput) === 'string' ? styling.timelineHeaderTimestampInput : undefined, style: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampInput) === 'string' ? {} : styling === null || styling === void 0 ? void 0 : styling.timelineHeaderTimestampInput, type: "text", "aria-label": "Timestamp", value: internalTime !== false ? internalTime : (0, vtt_core_1.toTimestampString)(currentTime), onChange: e => { setInternalTime(e.target.value.replace(/[^0-9:.]/g, "")); setPlaying(false); setCurrentTime(parseTimestampString(e.target.value)); }, onFocus: () => setPlaying(false), onBlur: () => setInternalTime(false) }) }), (0, jsx_runtime_1.jsxs)(TimelineOverflowContainer_1.TimelineOverflowContainer, { componentRole: "timeline-header", ref: containerRef, onMouseDown: e => { if (e.button !== 0) return; setPlaying(false); setAnchor(e.clientX); setAnchorTime(currentTime); setDragging(true); }, onTouchStart: e => { const touch = e.touches[0]; if (!touch) return; setPlaying(false); setAnchor(touch.clientX); setAnchorTime(currentTime); setDragging(true); }, onMouseMove: e => { if (!indicatorWidth || !dragging) return; const delta = anchor - e.clientX; const timeDelta = (delta / (indicatorWidth + 16)) * interval; setCurrentTime(anchorTime + timeDelta); }, onTouchMove: e => { const touch = e.touches[0]; if (!touch || !indicatorWidth) return; const delta = anchor - touch.clientX; const timeDelta = (delta / (indicatorWidth + 16)) * interval; setCurrentTime(anchorTime + timeDelta); }, onMouseUp: () => setDragging(false), onMouseLeave: () => setDragging(false), onMouseEnter: () => setDragging(false), onTouchEnd: () => setDragging(false), style: { cursor: dragging ? 'grabbing' : 'grab' }, children: [(0, jsx_runtime_1.jsx)("div", { style: Object.assign(Object.assign({}, (typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderPlayhead) === 'string' ? {} : styling === null || styling === void 0 ? void 0 : styling.timelineHeaderPlayhead)), { left: '50%', transform: `translateX(-50%)`, position: 'absolute', zIndex: 2 }), className: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderPlayhead) === 'string' ? styling.timelineHeaderPlayhead : undefined }), (0, jsx_runtime_1.jsx)("div", { style: Object.assign(Object.assign({ '--time-indicator-count': timelineIntervalCount + 1, '--time-indicator-final-width': duration ? `${intervalWidth}rem` : undefined, transform: `translateX(calc(${(containerWidth !== null && containerWidth !== void 0 ? containerWidth : 0) / 2}px - ${(currentTime / duration) * (trackWidth !== null && trackWidth !== void 0 ? trackWidth : 0)}px + ${(currentTime / duration) * 8}rem))` }, (typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderIndicatorReel) === 'string' ? {} : styling === null || styling === void 0 ? void 0 : styling.timelineHeaderIndicatorReel)), { display: 'grid', height: '100%', width: 'min-content', gridTemplateColumns: 'repeat(calc(var(--time-indicator-count, 2) - 2), 8rem) var(--time-indicator-final-width, 8rem) 8rem' }), className: typeof (styling === null || styling === void 0 ? void 0 : styling.timelineHeaderIndicatorReel) === 'string' ? styling.timelineHeaderIndicatorReel : undefined, ref: trackRef, children: indicators })] })] }); } //# sourceMappingURL=TimelineHeader.js.map