@react-av/editor
Version:
Editor Timeline Components built on React AV.
137 lines • 10.4 kB
JavaScript
;
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