aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/views/nodes/Keyframe.tsx18
-rw-r--r--src/client/views/nodes/Timeline.scss83
-rw-r--r--src/client/views/nodes/Timeline.tsx123
-rw-r--r--src/client/views/nodes/Track.scss15
-rw-r--r--src/client/views/nodes/Track.tsx111
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>
);
}