aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/nodes/Keyframe.scss1
-rw-r--r--src/client/views/nodes/Keyframe.tsx103
-rw-r--r--src/client/views/nodes/Timeline.scss29
-rw-r--r--src/client/views/nodes/Timeline.tsx161
-rw-r--r--src/client/views/nodes/Track.tsx24
5 files changed, 180 insertions, 138 deletions
diff --git a/src/client/views/nodes/Keyframe.scss b/src/client/views/nodes/Keyframe.scss
index 44850cd64..19a61bde1 100644
--- a/src/client/views/nodes/Keyframe.scss
+++ b/src/client/views/nodes/Keyframe.scss
@@ -78,7 +78,6 @@
background-color:white;
border:3px solid green;
z-index: 1000;
- pointer-events: none;
position:absolute;
}
diff --git a/src/client/views/nodes/Keyframe.tsx b/src/client/views/nodes/Keyframe.tsx
index 263df0fd5..10ae2f427 100644
--- a/src/client/views/nodes/Keyframe.tsx
+++ b/src/client/views/nodes/Keyframe.tsx
@@ -10,28 +10,18 @@ import { List } from "../../../new_fields/List";
import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema";
import { any } from "bluebird";
import { FlyoutProps } from "./Timeline";
-import { exportDefaultSpecifier } from "babel-types";
enum Direction {
left = "left",
right = "right"
}
-interface IProp {
+interface IProps {
node: Doc;
RegionData: Doc;
setFlyout:(props:FlyoutProps) => any;
}
-
-const KeyframeDataSchema = createSchema({
- time: defaultSpec("number", 0),
- key: Doc
-});
-type KeyframeData = makeInterface<[typeof KeyframeDataSchema]>;
-const KeyframeData = makeInterface(KeyframeDataSchema);
-
-
const RegionDataSchema = createSchema({
position: defaultSpec("number", 0),
duration: defaultSpec("number", 0),
@@ -42,18 +32,14 @@ export const RegionData = makeInterface(RegionDataSchema);
@observer
-export class Keyframe extends React.Component<IProp> {
+export class Keyframe extends React.Component<IProps> {
- @observable private _display: string = "none";
- @observable private _bar = React.createRef<HTMLDivElement>();
- @observable private _keyframes: number[] = [];
- @observable private position: number = 0;
+ @observable private _bar = React.createRef<HTMLDivElement>();
@observable private fadein: number = 0;
@observable private fadeout: number = 0;
@action
componentDidMount() {
-
// need edge case here when keyframe data already exists when loading.....................;
}
@@ -88,7 +74,6 @@ export class Keyframe extends React.Component<IProp> {
TK.key = Doc.MakeCopy(this.props.node);
this.regiondata.keyframes!.push(TK);
}
-
}
@action
@@ -102,20 +87,12 @@ export class Keyframe extends React.Component<IProp> {
document.addEventListener("pointerup", (e: PointerEvent) => {
document.removeEventListener("pointermove", this.onBarPointerMove);
});
- } else if(mouse.which === 3) {
- e.preventDefault();
- e.stopPropagation();
- let bar = this._bar.current!;
- this.props.setFlyout({x:this.regiondata.position + 130, y: bar.getBoundingClientRect().bottom,display:"block", time: this.regiondata.position, duration:this.regiondata.duration});
- let removeFlyout = (e:PointerEvent) => {
- if (e.which === 1){
- console.log("wut");
- this.props.setFlyout({display:"none"});
- document.removeEventListener("pointerdown", removeFlyout);
- }
- };
- document.addEventListener("pointerdown", removeFlyout);
- }
+ }// else if(mouse.which === 3) {
+ // e.preventDefault();
+ // e.stopPropagation();
+ // let bar = this._bar.current!;
+ // this.props.setFlyout({x:e.clientX, y: e.clientY,display:"block", time: this.regiondata.position, duration:this.regiondata.duration});
+ // }
}
@action
@@ -169,7 +146,6 @@ export class Keyframe extends React.Component<IProp> {
document.addEventListener("pointermove", this.onDragResizeLeft);
document.addEventListener("pointerup", () => {
document.removeEventListener("pointermove", this.onDragResizeLeft);
-
});
}
@@ -180,7 +156,6 @@ export class Keyframe extends React.Component<IProp> {
document.addEventListener("pointermove", this.onDragResizeRight);
document.addEventListener("pointerup", () => {
document.removeEventListener("pointermove", this.onDragResizeRight);
-
});
}
@@ -193,10 +168,6 @@ export class Keyframe extends React.Component<IProp> {
let offset = e.clientX - barX;
this.regiondata.duration -= offset;
this.regiondata.position += offset;
- this.regiondata.keyframes!.forEach(kf => {
- kf = kf as Doc;
- kf.time = NumCast(kf.time) + offset;
- });
}
@@ -207,11 +178,10 @@ export class Keyframe extends React.Component<IProp> {
let bar = this._bar.current!;
let barX = bar.getBoundingClientRect().right;
let offset = e.clientX - barX;
- console.log(offset);
this.regiondata.duration += offset;
}
- createDivider = (type?: string): JSX.Element => {
+ createDivider = (type?: Direction): JSX.Element => {
if (type === "left") {
return <div className="divider" style={{ right: "0px" }}></div>;
} else if (type === "right") {
@@ -224,33 +194,48 @@ export class Keyframe extends React.Component<IProp> {
createKeyframe = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
- let mouse = e.nativeEvent;
+ let bar = this._bar.current!;
+ let offset = e.clientX - bar.getBoundingClientRect().left;
let position = NumCast(this.regiondata.position);
- this._keyframes.push(mouse.offsetX);
- this.makeKeyData(position + mouse.offsetX);
+ this.makeKeyData(position + offset);
}
+ @action
+ moveKeyframe = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ }
render() {
return (
<div>
- <div className="bar" ref={this._bar} style={{ transform: `translate(${this.regiondata.position}px)`, width: `${this.regiondata.duration}px` }}
- onPointerDown={this.onBarPointerDown}
- onDoubleClick={this.createKeyframe}>
- <div className="leftResize" onPointerDown={this.onResizeLeft} ></div>
- <div className="rightResize" onPointerDown={this.onResizeRight}></div>
- <div className="fadeLeft" style={{ width: `${20}px` }}>{this.createDivider("left")}</div>
- <div className="fadeRight" style={{ width: `${20}px` }}>{this.createDivider("right")}</div>
- {this._keyframes.map(kf => {
- return <div className="keyframe" style={{ left: `${kf}px` }}>
- {this.createDivider()}
- <div className="keyframeCircle"></div>
- </div>;
- })}
- {this.createDivider("left")}
- {this.createDivider("right")}
- </div>
+ <div className="bar" ref={this._bar} style={{ transform: `translate(${this.regiondata.position}px)`, width: `${this.regiondata.duration}px` }}
+ onPointerDown={this.onBarPointerDown}
+ onDoubleClick={this.createKeyframe}
+ onContextMenu={action((e:React.MouseEvent)=>{
+ let mouse = e.nativeEvent;
+ if (mouse.which === 3){
+ this.props.setFlyout({x:e.clientX, y: e.clientY, display:"block"});
+ } else {
+ this.props.setFlyout({display:"block"});
+ }
+ })}>
+ <div className="leftResize" onPointerDown={this.onResizeLeft} ></div>
+ <div className="rightResize" onPointerDown={this.onResizeRight}></div>
+ <div className="fadeLeft" style={{ width: `${20}px` }}>{this.createDivider(Direction.left)}</div>
+ <div className="fadeRight" style={{ width: `${20}px` }}>{this.createDivider(Direction.right)}</div>
+ {this.regiondata.keyframes!.map(kf => {
+ kf = kf as Doc;
+ return <div className="keyframe" style={{ left: `${NumCast(kf.time) - this.regiondata.position}px` }}>
+ {this.createDivider()}
+ <div className="keyframeCircle" onPointerDown={this.moveKeyframe}></div>
+ </div>;
+ })}
+ {this.createDivider(Direction.left)}
+ {this.createDivider(Direction.right)}
+ </div>
</div>
);
}
diff --git a/src/client/views/nodes/Timeline.scss b/src/client/views/nodes/Timeline.scss
index 44b6cf03e..a416cbb1c 100644
--- a/src/client/views/nodes/Timeline.scss
+++ b/src/client/views/nodes/Timeline.scss
@@ -1,5 +1,11 @@
@import "./../globalCssVariables.scss";
+.minimize{
+ position:relative;
+ z-index: 1000;
+ height: 30px;
+ width: 100px;
+}
.flyout-container{
background-color: transparent;
position:absolute;
@@ -15,17 +21,22 @@
width: 100%;
height: 100%;
}
- div{
- color: white;
- position:absolute;
- top:30px;
- left:0px;
+ .input-container{
+ position: absolute;
+ right:0px;
+ top: 30px;
+ width: 70px;
+ input{
+ width: 100%;
+ }
}
- input{
+ .text-container{
position:absolute;
- width: 40px;
- right:0px;
+ top:30px;
+ left:0px;
+ color:white
}
+
}
@@ -35,6 +46,8 @@
position:absolute;
background-color: $light-color-secondary;
box-shadow: 0px 10px 20px;
+ transition: transform 1000ms ease-in-out;
+
.toolbox{
position:absolute;
width: 100%;
diff --git a/src/client/views/nodes/Timeline.tsx b/src/client/views/nodes/Timeline.tsx
index 8050830fa..fd235fad3 100644
--- a/src/client/views/nodes/Timeline.tsx
+++ b/src/client/views/nodes/Timeline.tsx
@@ -12,16 +12,18 @@ 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 { faCircle, faPlayCircle, faBackward, faForward, faGripLines } from "@fortawesome/free-solid-svg-icons";
import { DocumentContentsView } from "./DocumentContentsView";
+import { ContextMenuProps } from "../ContextMenuItem";
+import { ContextMenu } from "../ContextMenu";
-export interface FlyoutProps{
- x?: number;
- y?: number;
- display?:string;
- time?:number;
- duration?:number;
+export interface FlyoutProps {
+ x?: number;
+ y?: number;
+ display?: string;
+ time?: number;
+ duration?: number;
}
@@ -31,11 +33,12 @@ export class Timeline extends CollectionSubView(Document) {
private readonly MIN_CONTAINER_HEIGHT: number = 205;
private readonly MAX_CONTAINER_HEIGHT: number = 800;
- @observable private _tickSpacing = 50;
+ @observable private _tickSpacing = 50;
@observable private _scrubberbox = React.createRef<HTMLDivElement>();
@observable private _trackbox = React.createRef<HTMLDivElement>();
@observable private _titleContainer = React.createRef<HTMLDivElement>();
+ @observable private _timelineContainer = React.createRef<HTMLDivElement>();
@observable private _currentBarX: number = 0;
@observable private _windSpeed: number = 1;
@observable private _isPlaying: boolean = false;
@@ -47,8 +50,9 @@ export class Timeline extends CollectionSubView(Document) {
@observable private _infoContainer = React.createRef<HTMLDivElement>();
@observable private _ticks: number[] = [];
- @observable private flyoutInfo:FlyoutProps = {x:0, y:0,display:"none"};
+ @observable private flyoutInfo: FlyoutProps = { x: 0, y: 0, display: "none" };
+ private block = false;
@action
componentDidMount() {
@@ -73,6 +77,22 @@ export class Timeline extends CollectionSubView(Document) {
this._ticks.push(i);
i += 1000;
}
+ document.addEventListener("pointerdown", this.closeFlyout);
+ }
+
+ @action
+ onFlyoutDown = (e: React.PointerEvent) => {
+ this.flyoutInfo.display = "block";
+ this.block = true;
+ }
+
+ @action
+ closeFlyout = (e: PointerEvent) => {
+ if (this.block) {
+ this.block = false;
+ return;
+ }
+ this.flyoutInfo.display = "none";
}
@action
@@ -80,7 +100,7 @@ export class Timeline extends CollectionSubView(Document) {
this._time = 100001;
}
componentWillUnmount() {
-
+ document.removeEventListener("pointerdown", this.closeFlyout);
}
//for playing
@@ -211,75 +231,100 @@ export class Timeline extends CollectionSubView(Document) {
}
}
+ @observable private _isMinimized = false;
@action
minimize = (e: React.MouseEvent) => {
- this._containerHeight = 0;
+ e.preventDefault();
+ e.stopPropagation();
+ let timelineContainer = this._timelineContainer.current!;
+ if (this._isMinimized) {
+ this._isMinimized = false;
+ timelineContainer.style.transform = `translate(0px, 0px)`;
+ } else {
+ this._isMinimized = true;
+ timelineContainer.style.transform = `translate(0px, ${- this._containerHeight - 30}px)`;
+ }
}
@action
- getFlyout = (props: FlyoutProps) => {
- for(const[k, v] of Object.entries(props)){
- (this.flyoutInfo as any)[k] = v;
+ getFlyout = (props: FlyoutProps) => {
+ for (const [k, v] of Object.entries(props)) {
+ (this.flyoutInfo as any)[k] = v;
}
-
- }
+ }
- @action
- onFlyoutDown = (e: React.PointerEvent) => {
- console.log("clicked!");
- this.flyoutInfo.display = "block";
+ timelineContextMenu = (e: React.MouseEvent): void => {
+ let subitems: ContextMenuProps[] = [];
+ let timelineContainer = this._timelineContainer.current!;
+ subitems.push({ description: "Pin to Top", event: action(() => { timelineContainer.style.transform = "translate(0px, 0px)"; }), icon: "pinterest" });
+ subitems.push({
+ description: "Pin to Bottom", event: action(() => {
+ timelineContainer.style.transform = `translate(0px, ${e.pageY - this._containerHeight}px)`;
+ }), icon: "pinterest"
+ });
+ ContextMenu.Instance.addItem({ description: "Timeline Funcs...", subitems: subitems });
}
render() {
return (
- <div className="timeline-container" style={{ height: `${this._containerHeight}px` }}>
- <div className="flyout-container" style={{transform: `translate(${this.flyoutInfo.x}px, ${this.flyoutInfo.y}px)`, display:this.flyoutInfo.display}} onPointerDown={this.onFlyoutDown}>
- <FontAwesomeIcon className="flyout" icon="comment-alt" color="grey"/>
- <div>
- <p>Time:</p><input type="text" placeholder={`${Math.round(this.flyoutInfo.time! / this._tickSpacing * 1000)}ms`}/>
- <p>Duration:</p><input type="text" placeholder={`${Math.round(this.flyoutInfo.duration! / this._tickSpacing * 1000)}ms`}/>
- <p>Fade-in</p>
- <p>Fade-out</p>
+ <div>
+ <button className="minimize" onClick={this.minimize}>Minimize</button>
+ <div className="timeline-container" style={{ height: `${this._containerHeight}px` }} ref={this._timelineContainer} onContextMenu={this.timelineContextMenu}>
+ <div className="flyout-container" style={{ transform: `translate(${this.flyoutInfo.x}px, ${this.flyoutInfo.y}px)`, display: this.flyoutInfo.display }} onPointerDown={this.onFlyoutDown}>
+ <FontAwesomeIcon className="flyout" icon="comment-alt" color="grey" />
+ <div className="text-container">
+ <p>Time:</p>
+ <p>Duration:</p>
+ <p>Fade-in</p>
+ <p>Fade-out</p>
+ </div>
+ <div className="input-container">
+ <input type="text" placeholder={`${Math.round(this.flyoutInfo.time! / this._tickSpacing * 1000)}ms`} />
+ <input type="text" placeholder={`${Math.round(this.flyoutInfo.duration! / this._tickSpacing * 1000)}ms`} />
+ <input type="text" placeholder={`${Math.round(this.flyoutInfo.time! / this._tickSpacing * 1000)}ms`} />
+ <input type="text" placeholder={`${Math.round(this.flyoutInfo.duration! / this._tickSpacing * 1000)}ms`} />
+ </div>
</div>
- </div>
- <div className="toolbox">
- <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}>
-
- <div className="scrubberbox" ref={this._scrubberbox} onClick={this.onScrubberClick}>
- {this._ticks.map(element => {
- return <div className="tick" style={{ transform: `translate(${element/1000 * this._tickSpacing}px)`, position: "absolute", pointerEvents: "none" }}> <p>{this.toTime(element)}</p></div>;
- })}
+ <div className="toolbox">
+ <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="scrubber" onPointerDown={this.onScrubberDown} style={{ transform: `translate(${this._currentBarX}px)` }}>
- <div className="scrubberhead"></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 / 1000 * this._tickSpacing}px)`, position: "absolute", pointerEvents: "none" }}> <p>{this.toTime(element)}</p></div>;
+ })}
+ </div>
+ <div className="scrubber" onPointerDown={this.onScrubberDown} style={{ transform: `translate(${this._currentBarX}px)` }}>
+ <div className="scrubberhead"></div>
+ </div>
+ <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} setFlyout={this.getFlyout} />;
+ })}
+ </div>
</div>
- <div className="trackbox" ref={this._trackbox} onPointerDown={this.onPanDown}>
+ <div className="title-container" ref={this._titleContainer}>
{this._nodes.map(doc => {
- return <Track node={(doc as any).value() as Doc} currentBarX={this._currentBarX} setFlyout={this.getFlyout} />;
+ 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>
- <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.tsx b/src/client/views/nodes/Track.tsx
index 94c0b5563..cf613a827 100644
--- a/src/client/views/nodes/Track.tsx
+++ b/src/client/views/nodes/Track.tsx
@@ -73,16 +73,20 @@ export class Track extends React.Component<IProps> {
let leftkf: (Doc | undefined) = this.calcMinLeft(region!);
let rightkf: (Doc | undefined) = this.calcMinRight(region!);
+
if (leftkf && rightkf) {
this.interpolate(leftkf, rightkf);
} else if (leftkf) {
console.log("left exists");
+ console.log(leftkf.time);
this._keys.forEach(k => {
let data = leftkf!.key as Doc;
this.props.node[k] = data[k];
});
} else if (rightkf) {
+
console.log("right exists");
+ console.log(rightkf.time);
this._keys.forEach(k => {
let data = rightkf!.key as Doc;
this.props.node[k] = data[k];
@@ -98,17 +102,15 @@ export class Track extends React.Component<IProps> {
*/
@action
calcMinLeft = (region: Doc): (Doc | undefined) => { //returns the time of the closet keyframe to the left
- let leftKf: Doc = new Doc();
- leftKf.time = Infinity;
+ let leftKf:(Doc| undefined) = undefined;
+ let time:number = 0;
(region.keyframes! as List<Doc>).forEach((kf) => {
kf = kf as Doc;
- if (NumCast(kf.time) < this.props.currentBarX && NumCast(leftKf.time) > NumCast(kf.time)) {
+ if (NumCast(kf.time) < this.props.currentBarX && NumCast(kf.time) >= NumCast(time)) {
leftKf = kf;
+ time = NumCast(kf.time);
}
});
- if (NumCast(leftKf.time) === Infinity) {
- return undefined;
- }
return leftKf;
}
@@ -119,17 +121,15 @@ export class Track extends React.Component<IProps> {
*/
@action
calcMinRight = (region: Doc): (Doc | undefined) => { //returns the time of the closest keyframe to the right
- let rightKf: Doc = new Doc();
- rightKf.time = Infinity;
+ let rightKf: (Doc|undefined) = undefined;
+ let time:number = Infinity;
(region.keyframes! as List<Doc>).forEach((kf) => {
kf = kf as Doc;
- if (NumCast(kf.time) > this.props.currentBarX && NumCast(rightKf.time) > NumCast(kf.time)) {
+ if (NumCast(kf.time) > this.props.currentBarX && NumCast(kf.time) <= NumCast(time)) {
rightKf = kf;
+ time = NumCast(kf.time);
}
});
- if (NumCast(rightKf.time) === Infinity) {
- return undefined;
- }
return rightKf;
}