diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/Keyframe.tsx | 18 | ||||
-rw-r--r-- | src/client/views/nodes/Timeline.scss | 83 | ||||
-rw-r--r-- | src/client/views/nodes/Timeline.tsx | 123 | ||||
-rw-r--r-- | src/client/views/nodes/Track.scss | 15 | ||||
-rw-r--r-- | src/client/views/nodes/Track.tsx | 111 |
5 files changed, 186 insertions, 164 deletions
diff --git a/src/client/views/nodes/Keyframe.tsx b/src/client/views/nodes/Keyframe.tsx index 7f4f9ab3b..2ed63a66e 100644 --- a/src/client/views/nodes/Keyframe.tsx +++ b/src/client/views/nodes/Keyframe.tsx @@ -15,12 +15,14 @@ import { DocumentView } from "./DocumentView"; import { anchorPoints, Flyout } from "../TemplateMenu"; import { LinkMenu } from "./LinkMenu"; import { faCircle } from "@fortawesome/free-solid-svg-icons"; +import { node } from "prop-types"; interface IProp { node: Doc; + currentBarX: number; } @observer @@ -37,12 +39,16 @@ export class Keyframe extends React.Component<IProp> { @action componentDidMount() { - let dv:DocumentView = DocumentManager.Instance.getDocumentView(this.props.node!)!; - this._data = new Doc(); - this._position = this.props.node.currentBarX as number; - this._data.duration = 200; - this._data.start = this._position - (this._duration/2); - this._data.end = this._position + (this._duration/2); + this._position = this.props.node.position as number; + reaction (() => this.props.currentBarX, () => { + console.log("reaction triggered!"); + if (this.props.currentBarX !== this._position){ + this.props.node.hidden = true; + } else { + this.props.node.hidden = false; + } + }); + } componentWillUnmount() { diff --git a/src/client/views/nodes/Timeline.scss b/src/client/views/nodes/Timeline.scss index 36f51edf2..b74be1fee 100644 --- a/src/client/views/nodes/Timeline.scss +++ b/src/client/views/nodes/Timeline.scss @@ -2,30 +2,43 @@ .timeline-container{ width:100%; - height:200px; + height:300px; position:absolute; - background-color:$intermediate-color; + background-color: $light-color-secondary; + box-shadow: 0px 10px 20px; .toolbox{ - width: 200px; + position:absolute; + width: 100%; + top: 10px; + left: 20px; div{ + float:left; + margin-left: 10px; position:relative; - display:inline-block; + .overview{ + width: 200px; + height: 100%; + background-color: black; + position:absolute; + } } } - .info-container{ - top: 10px; - margin-left: 10%; - position:relative; - height: 80%; - width: 80%; - overflow-x: hidden; - + .info-container{ + margin-top: 50px; + right:20px; + position:absolute; + height: calc(100% - 100px); + width: calc(100% - 140px); + overflow: hidden; + padding:0px; + // box-shadow: 0px 10px 20px grey inset; + .scrubberbox{ - position:relative; + position:absolute; background-color: transparent; height: 30px; width:100%; - + .tick{ height:100%; width: 1px; @@ -34,30 +47,60 @@ } } .scrubber{ - height: 100px; + top:30px; + height: 100%; width: 2px; position:absolute; z-index: 1001; - background-color:black ; + background-color:black; .scrubberhead{ + top: -30px; height: 30px; width: 30px; + background-color:transparent; + border-radius: 50%; + border: 5px solid black; left: -15px; - //top: -40px; position:absolute; } } .trackbox{ top: 30px; - height:calc(100%); + height:calc(100% - 30px); width:100%; border:1px; - overflow:auto; + overflow:hidden; background-color:white; position:absolute; } + + } + .title-container{ + margin-top: 80px; + margin-left: 20px; + height: calc(100% - 100px - 30px); + width: 100px; + background-color:white; + overflow: hidden; + .datapane{ + top:0px; + width: 100px; + height: 75px; + border: 1px solid $dark-color; + background-color: $intermediate-color; + color: white; + position:relative; + float:left; + border-style:solid; + } + } + .resize{ + bottom: 5px; + position:absolute; + height: 30px; + width: 50px; + left: calc(50% - 25px); } - }
\ No newline at end of file diff --git a/src/client/views/nodes/Timeline.tsx b/src/client/views/nodes/Timeline.tsx index 90907861f..a06ca43eb 100644 --- a/src/client/views/nodes/Timeline.tsx +++ b/src/client/views/nodes/Timeline.tsx @@ -5,34 +5,38 @@ 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 } from "mobx"; +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 } from "@fortawesome/free-solid-svg-icons"; +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<HTMLDivElement>() + + @observable private _scrubberbox = React.createRef<HTMLDivElement>(); @observable private _trackbox = React.createRef<HTMLDivElement>(); + @observable private _titleContainer = React.createRef<HTMLDivElement>(); @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<Doc> = new List<Doc>(); @observable private _time = 100000; //DEFAULT @observable private _infoContainer = React.createRef<HTMLDivElement>(); - @observable private _panX = 0; @observable private _ticks: number[] = []; - private _reactionDisposers: IReactionDisposer[] = []; @action @@ -46,23 +50,24 @@ export class Timeline extends CollectionSubView(Document){ let childrenList = ((children[Self] as any).__fields) as List<Doc>; this._nodes = (childrenList) as List<Doc>; + 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 - - let boxWidth = scrubber.getBoundingClientRect().width; for (let i = 0; i < this._time; ) { this._ticks.push(i); i += 1000; - } + } } + @action componentDidUpdate(){ - let infoContainer = this._infoContainer.current!; - let trackbox = this._trackbox.current!; - this._boxLength = infoContainer.scrollWidth; - trackbox.style.width = `${this._boxLength}`; + this._time = 100001; } - componentWillUnmount(){ } @@ -80,8 +85,6 @@ export class Timeline extends CollectionSubView(Document){ @action changeCurrentX = () => { - console.log(this._currentBarX); - console.log(this._boxLength); if (this._currentBarX === this._boxLength && this._isPlaying) { this._currentBarX = 0; } @@ -110,14 +113,10 @@ export class Timeline extends CollectionSubView(Document){ onScrubberDown = (e:React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let scrubberbox = this._scrubberbox.current!; - //let left = scrubberbox.getBoundingClientRect().left; - - //let offsetX = Math.round(e.clientX - left); - let mouse = e.nativeEvent; - this._currentBarX = mouse.offsetX; document.addEventListener("pointermove", this.onScrubberMove); - document.addEventListener("pointerup", this.onScrubberFinished); + document.addEventListener("pointerup", () => { + document.removeEventListener("pointermove", this.onScrubberMove); + }); } @action @@ -130,13 +129,17 @@ export class Timeline extends CollectionSubView(Document){ this._currentBarX = offsetX; } - onScrubberFinished = (e: PointerEvent) => { + @action + onScrubberClick = (e:React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); let scrubberbox = this._scrubberbox.current!; - document.removeEventListener("pointermove", this.onScrubberMove); + 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); @@ -155,7 +158,9 @@ export class Timeline extends CollectionSubView(Document){ e.preventDefault(); e.stopPropagation(); document.addEventListener("pointermove", this.onPanMove); - document.addEventListener("pointerup", this.onPanUp); + document.addEventListener("pointerup", () => { + document.removeEventListener("pointermove", this.onPanMove); + }); } @action @@ -163,35 +168,77 @@ export class Timeline extends CollectionSubView(Document){ e.preventDefault(); e.stopPropagation(); let infoContainer = this._infoContainer.current!; - infoContainer.scrollLeft = infoContainer.scrollLeft - e.movementX; + 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 - onPanUp = (e:PointerEvent) => { + onResizeDown = (e:React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - document.removeEventListener("pointermove", this.onPanMove); + 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 ( - <div className="timeline-container"> + <div className="timeline-container" style={{height:`${this._containerHeight}px`}}> <div className="toolbox"> - <div onClick={this.windBackward}> <FontAwesomeIcon icon={faBackward} size="lg"/> </div> - <div onClick={this.onPlay}> <FontAwesomeIcon icon={faPlayCircle} size="lg"/> </div> - <div onClick={this.windForward}> <FontAwesomeIcon icon={faForward} size="lg"/> </div> + <div onClick={this.windBackward}> <FontAwesomeIcon icon={faBackward} size="2x"/> </div> + <div onClick={this.onPlay}> <FontAwesomeIcon icon={faPlayCircle} size="2x"/> </div> + <div onClick={this.windForward}> <FontAwesomeIcon icon={faForward} size="2x"/> </div> + <div> + <p>Timeline Overview</p> + <div className="overview"></div> + </div> </div> - <div className="info-container" ref ={this._infoContainer} onPointerDown={this.onPanDown}> - <div className="scrubberbox" ref ={this._scrubberbox}> - {this._ticks.map(element => {return <div className="tick" style={{transform:`translate(${element / 20}px)`, position:"absolute"}}> <p>{this.toTime(element)}</p></div>})} + <div className="info-container" ref ={this._infoContainer}> + <div className="scrubberbox" ref ={this._scrubberbox} onClick={this.onScrubberClick}> + {this._ticks.map(element => {return <div className="tick" style={{transform:`translate(${element / 20}px)`, position:"absolute", pointerEvent:"none"}}> <p>{this.toTime(element)}</p></div>})} </div> <div className="scrubber" onPointerDown = {this.onScrubberDown} style={{transform:`translate(${this._currentBarX}px)`}}> - <FontAwesomeIcon className="scrubberhead" icon={faCircle}/>; + <div className="scrubberhead"></div> </div> - <div className="trackbox" ref={this._trackbox}> - {this._nodes.map(doc => {return <Track node={(doc as any).value() as Doc}/>;})} + <div className="trackbox" ref={this._trackbox} onPointerDown={this.onPanDown}> + {this._nodes.map(doc => {return <Track node={(doc as any).value() as Doc} currentBarX = {this._currentBarX}/>;})} </div> + </div> + <div className="title-container" ref={this._titleContainer}> + {this._nodes.map(doc => {return <div className="datapane"> + <p>{((doc as any).value() as Doc).title}</p> + </div>;})} </div> + <div onPointerDown ={this.onResizeDown}> + <FontAwesomeIcon className="resize" icon={faGripLines} /> + </div> + </div> ); } diff --git a/src/client/views/nodes/Track.scss b/src/client/views/nodes/Track.scss index 74dc7b5a7..c8d56edf6 100644 --- a/src/client/views/nodes/Track.scss +++ b/src/client/views/nodes/Track.scss @@ -1,25 +1,14 @@ @import "./../globalCssVariables.scss"; .track-container{ - - .datapane{ - top:0px; - width: 100px; - height: 75px; - background-color: $light-color-secondary; - position:relative; - float:left; - border-style:solid; - } .track { .inner { top:0px; - float:right; height: 75px; - width: calc(100% - 100px); + width: calc(100%); background-color: $light-color; - border-style:solid; + border: 1px solid $dark-color; position:relative; } } diff --git a/src/client/views/nodes/Track.tsx b/src/client/views/nodes/Track.tsx index a4c12c996..855ccedd5 100644 --- a/src/client/views/nodes/Track.tsx +++ b/src/client/views/nodes/Track.tsx @@ -45,6 +45,7 @@ const TimeAndPosition = makeInterface(TimeAndPositionSchema); interface props{ node: Doc; + currentBarX: number; } @observer @@ -308,7 +309,22 @@ export class Track extends React.Component<props> { // console.log(data); // }); - this.props.node.currentBarX = this._currentBarX; + + reaction (() => this.props.currentBarX, () => { + console.log("react"); + this._data.forEach((datum) => { + if (this.props.currentBarX >= (datum.begin as number) && this.props.currentBarX <= (datum.end as number)){ + this.props.node.hidden = false; + } else { + this.props.node.hidden = true; + } + }); + // if (this.props.currentBarX !== this._position){ + // this.props.node.hidden = true; + // } else { + // this.props.node.hidden = false; + // } + }); } /** @@ -319,108 +335,29 @@ export class Track extends React.Component<props> { this._reactionDisposers = []; } - /** - * Displays yellow bars per node when selected - */ - displayKeyFrames = (doc: Doc) => { - let views: (JSX.Element | null)[] = []; - DocListCast(this._data).forEach((node, i) => { - if (node === doc) { - //set it to views - views = DocListCast(this._keyframes[i]).map(tp => { - const timeandpos = TimeAndPosition(tp); - let time = timeandpos.time; - let bar = <Keyframe position={time}></Keyframe>; - return bar; - }); - } - }); - return views; - } - - /** - * when user lifts the pointer. Removes pointer move event and no longer tracks green bar moving - * @param e react pointer event - */ - @action - onInnerPointerUp = (e:React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - if (this._inner.current) { - // if (!this._isPlaying) { - // this._barMoved = false; - // } - this._inner.current.removeEventListener("pointermove", this.onInnerPointerMove); - } - } - - /** - * called when user clicks on a certain part of the inner. This will move the green bar to that position. - * @param e react pointer event - */ - @action - onInnerPointerDown = (e:React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - if (this._inner.current) { - this._barMoved = true; - // let mouse = e.nativeEvent; - // let offsetX = Math.round(mouse.offsetX); - let left = this._inner.current.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left); - //this._currentBarX = offsetX; - this._inner.current.removeEventListener("pointermove", this.onInnerPointerMove); - this._inner.current.addEventListener("pointermove", this.onInnerPointerMove); - this.timeChange(this._currentBarX); - - - } - } - - /** - * Called when you drag the green bar across the inner div. - * @param e pointer event - */ - @action - onInnerPointerMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - this._barMoved = true; - if (this._inner.current) { - let left = this._inner.current.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left); - this._currentBarX = offsetX; - this.timeChange(this._currentBarX); - } - } - @observable private _keyframes: JSX.Element[] = []; - + @observable private _data: Doc[] = []; @action onInnerDoubleClick = (e: React.MouseEvent) => { let inner = this._inner.current!; let left = inner.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left); - this.props.node.currentBarX = offsetX; - this._keyframes.push(<Keyframe node={this.props.node}/>); - - + this.props.node.position = offsetX; + let datum = new Doc(); + datum.begin = offsetX; + datum.end = offsetX + 200; + this._data.push(datum); + this._keyframes.push(<Keyframe node={this.props.node} currentBarX={this.props.currentBarX}/>); } render() { return ( <div className="track-container"> - <div className="datapane"> - <p>{this.props.node.title}</p> - </div> <div className="track"> <div className="inner" ref={this._inner} onDoubleClick={this.onInnerDoubleClick}> {this._keyframes.map((element)=> element)} </div> </div> - {/* <button onClick={this.onRecord}>Record</button> - <input placeholder={this._currentBarX.toString()} ref={this._timeInput} onKeyDown={this.onTimeEntered} ></input> - <button onClick={this.onPlay} ref={this._playButton}> Play </button>*/} </div> ); } |