import * as React from "react"; import * as ReactDOM from "react-dom"; import "./Timeline.scss"; import { CollectionSubView } from "../collections/CollectionSubView"; import { Document, listSpec, createSchema, makeInterface, defaultSpec } from "../../../new_fields/Schema"; import { observer} from "mobx-react"; import { Track } from "./Track"; import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS, Reaction, IObservableObject } from "mobx"; import { Cast } from "../../../new_fields/Types"; import { SelectionManager } from "../../util/SelectionManager"; import { List } from "../../../new_fields/List"; import { Self } from "../../../new_fields/FieldSymbols"; import { Doc, DocListCast } from "../../../new_fields/Doc"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCircle, faPlayCircle, faBackward, faForward, faGripLines } from "@fortawesome/free-solid-svg-icons"; import { DocumentContentsView } from "./DocumentContentsView"; @observer export class Timeline extends CollectionSubView(Document){ private readonly DEFAULT_CONTAINER_HEIGHT:number = 300; private readonly MIN_CONTAINER_HEIGHT:number = 205; private readonly MAX_CONTAINER_HEIGHT:number = 800; @observable private _scrubberbox = React.createRef(); @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @observable private _currentBarX:number = 0; @observable private _windSpeed:number = 1; @observable private _isPlaying:boolean = false; @observable private _boxLength:number = 0; @observable private _containerHeight:number = this.DEFAULT_CONTAINER_HEIGHT; @observable private _nodes:List = new List(); @observable private _time = 100000; //DEFAULT @observable private _infoContainer = React.createRef(); @observable private _ticks: number[] = []; @action componentDidMount(){ let scrubber = this._scrubberbox.current!; this._boxLength = scrubber.getBoundingClientRect().width; let children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)); if (!children) { return; } let childrenList = ((children[Self] as any).__fields) as List; this._nodes = (childrenList) as List; reaction( () => this._time, time => { let infoContainer = this._infoContainer.current!; let trackbox = this._trackbox.current!; this._boxLength = infoContainer.scrollWidth; trackbox.style.width = `${this._boxLength}`; }); //check if this is a video frame for (let i = 0; i < this._time; ) { this._ticks.push(i); i += 1000; } } @action componentDidUpdate(){ this._time = 100001; } componentWillUnmount(){ } //for playing @action onPlay = async (e: React.MouseEvent) => { if (this._isPlaying) { this._isPlaying = false; } else { this._isPlaying = true; this.changeCurrentX(); } } @action changeCurrentX = () => { if (this._currentBarX === this._boxLength && this._isPlaying) { this._currentBarX = 0; } if (this._currentBarX <= this._boxLength && this._isPlaying) { this._currentBarX = this._currentBarX + this._windSpeed; setTimeout(this.changeCurrentX, 15); } } @action windForward = (e: React.MouseEvent) => { if (this._windSpeed < 64) { //max speed is 32 this._windSpeed = this._windSpeed * 2; } } @action windBackward = (e: React.MouseEvent) => { if (this._windSpeed > 1 / 16) { // min speed is 1/8 this._windSpeed = this._windSpeed / 2; } } //for scrubber action @action onScrubberDown = (e:React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); document.addEventListener("pointermove", this.onScrubberMove); document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onScrubberMove); }); } @action onScrubberMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); let scrubberbox = this._scrubberbox.current!; let left = scrubberbox.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left); this._currentBarX = offsetX; } @action onScrubberClick = (e:React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); let scrubberbox = this._scrubberbox.current!; let offset = scrubberbox.scrollLeft + e.clientX - scrubberbox.getBoundingClientRect().left; this._currentBarX = offset; ; } @action toTime = (time:number):string => { const inSeconds = time / 1000; let min:(string|number) = Math.floor(inSeconds / 60); let sec:(string|number) = inSeconds % 60; if (Math.floor(sec/10) === 0){ sec = "0" + sec; } return `${min}:${sec}`; } @action onPanDown = (e:React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); document.addEventListener("pointermove", this.onPanMove); document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onPanMove); }); } @action onPanMove = (e:PointerEvent) => { e.preventDefault(); e.stopPropagation(); let infoContainer = this._infoContainer.current!; let trackbox = this._trackbox.current!; let titleContainer = this._titleContainer.current!; infoContainer.scrollLeft = infoContainer.scrollLeft - e.movementX; trackbox.scrollTop = trackbox.scrollTop - e.movementY; titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; } @action onResizeDown = (e:React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); document.addEventListener("pointermove", this.onResizeMove); document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onResizeMove); }); } @action onResizeMove = (e:PointerEvent) => { e.preventDefault(); e.stopPropagation(); let offset = e.clientY - this._containerHeight; if (this._containerHeight + offset <= this.MIN_CONTAINER_HEIGHT ){ this._containerHeight = this.MIN_CONTAINER_HEIGHT; } else if (this._containerHeight + offset >= this.MAX_CONTAINER_HEIGHT){ this._containerHeight = this.MAX_CONTAINER_HEIGHT; } else { this._containerHeight += offset; } } @action minimize = (e:React.MouseEvent) => { this._containerHeight = 0; } render(){ return (

Timeline Overview

{this._ticks.map(element => {return

{this.toTime(element)}

})}
{this._nodes.map(doc => {return ;})}
{this._nodes.map(doc => {return

{((doc as any).value() as Doc).title}

;})}
); } }