import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { RegionHelpers } from './Region'; import { Timeline } from './Timeline'; import './TimelineOverview.scss'; interface TimelineOverviewProps { totalLength: number; visibleLength: number; visibleStart: number; currentBarX: number; isAuthoring: boolean; parent: Timeline; changeCurrentBarX: (pixel: number) => void; movePanX: (pixel: number) => any; time: number; tickSpacing: number; tickIncrement: number; } @observer export class TimelineOverview extends React.Component { @observable private _visibleRef = React.createRef(); @observable private _scrubberRef = React.createRef(); @observable private authoringContainer = React.createRef(); @observable private playbackContainer = React.createRef(); @observable private overviewBarWidth: number = 0; @observable private playbarWidth: number = 0; @observable private activeOverviewWidth: number = 0; @observable private _authoringReaction?: IReactionDisposer = undefined; @observable private visibleTime: number = 0; @observable private currentX: number = 0; @observable private visibleStart: number = 0; private readonly DEFAULT_HEIGHT = 50; private readonly DEFAULT_WIDTH = 300; componentDidMount() { this.setOverviewWidth(); this._authoringReaction = reaction( () => this.props.isAuthoring, () => { if (!this.props.isAuthoring) { runInAction(() => { this.setOverviewWidth(); }); } } ); } componentWillUnmount = () => { this._authoringReaction && this._authoringReaction(); }; @action setOverviewWidth() { const width1 = this.authoringContainer.current?.clientWidth; const width2 = this.playbackContainer.current?.clientWidth; if (width1 && width1 !== 0) this.overviewBarWidth = width1; if (width2 && width2 !== 0) this.playbarWidth = width2; if (this.props.isAuthoring) { this.activeOverviewWidth = this.overviewBarWidth; } else { this.activeOverviewWidth = this.playbarWidth; } } @action onPointerDown = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); document.removeEventListener('pointermove', this.onPanX); document.removeEventListener('pointerup', this.onPointerUp); document.addEventListener('pointermove', this.onPanX); document.addEventListener('pointerup', this.onPointerUp); }; @action onPanX = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); const movX = (this.props.visibleStart / this.props.totalLength) * this.DEFAULT_WIDTH + e.movementX; this.props.movePanX((movX / this.DEFAULT_WIDTH) * this.props.totalLength); }; @action onPointerUp = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); document.removeEventListener('pointermove', this.onPanX); document.removeEventListener('pointerup', this.onPointerUp); }; @action onScrubberDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); document.removeEventListener('pointermove', this.onScrubberMove); document.removeEventListener('pointerup', this.onScrubberUp); document.addEventListener('pointermove', this.onScrubberMove); document.addEventListener('pointerup', this.onScrubberUp); }; @action onScrubberMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); const scrubberRef = this._scrubberRef.current!; const left = scrubberRef.getBoundingClientRect().left; const offsetX = Math.round(e.clientX - left); this.props.changeCurrentBarX((offsetX / this.activeOverviewWidth) * this.props.totalLength + this.props.currentBarX); }; @action onScrubberUp = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); document.removeEventListener('pointermove', this.onScrubberMove); document.removeEventListener('pointerup', this.onScrubberUp); }; @action getTimes() { const vis = RegionHelpers.convertPixelTime(this.props.visibleLength, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); const x = RegionHelpers.convertPixelTime(this.props.currentBarX, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); const start = RegionHelpers.convertPixelTime(this.props.visibleStart, 'mili', 'time', this.props.tickSpacing, this.props.tickIncrement); this.visibleTime = vis; this.currentX = x; this.visibleStart = start; } render() { this.setOverviewWidth(); this.getTimes(); const percentVisible = this.visibleTime / this.props.time; const visibleBarWidth = percentVisible * this.activeOverviewWidth; const percentScrubberStart = this.currentX / this.props.time; let scrubberStart = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth; if (scrubberStart > this.activeOverviewWidth) scrubberStart = this.activeOverviewWidth; const percentBarStart = this.visibleStart / this.props.time; const barStart = percentBarStart * this.activeOverviewWidth; let playWidth = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth; if (playWidth > this.activeOverviewWidth) playWidth = this.activeOverviewWidth; const timeline = this.props.isAuthoring ? [
,
, ] : [
,
, ]; return (
{timeline}
); } }