diff options
author | andrewdkim <adkim414@gmail.com> | 2019-06-28 16:40:33 -0400 |
---|---|---|
committer | andrewdkim <adkim414@gmail.com> | 2019-06-28 16:40:33 -0400 |
commit | 3c735287ec92664e368649fea63ccd85210b5fc5 (patch) | |
tree | 817a3c10f4babc3d72978761e153e4e47ceebf5b | |
parent | 5c4f8dbe2a2506b998f80a0cf17e1044d0af7405 (diff) |
flyout
-rw-r--r-- | src/client/views/nodes/Keyframe.scss | 4 | ||||
-rw-r--r-- | src/client/views/nodes/Keyframe.tsx | 364 | ||||
-rw-r--r-- | src/client/views/nodes/Timeline.scss | 13 | ||||
-rw-r--r-- | src/client/views/nodes/Timeline.tsx | 265 | ||||
-rw-r--r-- | src/client/views/nodes/Track.tsx | 257 |
5 files changed, 473 insertions, 430 deletions
diff --git a/src/client/views/nodes/Keyframe.scss b/src/client/views/nodes/Keyframe.scss index 2410dceaf..44850cd64 100644 --- a/src/client/views/nodes/Keyframe.scss +++ b/src/client/views/nodes/Keyframe.scss @@ -82,4 +82,8 @@ position:absolute; } + } + + + diff --git a/src/client/views/nodes/Keyframe.tsx b/src/client/views/nodes/Keyframe.tsx index 76955da99..ed1f3b03a 100644 --- a/src/client/views/nodes/Keyframe.tsx +++ b/src/client/views/nodes/Keyframe.tsx @@ -1,87 +1,81 @@ import * as React from "react"; -import * as ReactDOM from "react-dom"; import "./Keyframe.scss"; -import "./../globalCssVariables.scss"; -import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS } from "mobx"; +import "./Timeline.scss"; +import "./../globalCssVariables.scss"; +import { observer, Observer } from "mobx-react"; +import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS, isComputedProp } from "mobx"; import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { auto } from "async"; import { Cast, FieldValue, StrCast, NumCast } from "../../../new_fields/Types"; -import { StandardLonghandProperties } from "csstype"; -import { runInThisContext } from "vm"; -import { DateField } from "../../../new_fields/DateField"; -import { DocumentManager } from "../../util/DocumentManager"; -import { DocumentView } from "./DocumentView"; -import { anchorPoints, Flyout } from "../TemplateMenu"; -import { LinkMenu } from "./LinkMenu"; -import { faCircle } from "@fortawesome/free-solid-svg-icons"; -import { node, number } from "prop-types"; import { List } from "../../../new_fields/List"; import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema"; -import { CollectionBaseView } from "../collections/CollectionBaseView"; -import { notDeepEqual } from "assert"; -import { DocUtils, Docs } from "../../documents/Documents"; -import { DragLinksAsDocuments } from "../../util/DragManager"; - - - +enum Direction { + left = "left", + right = "right" +} interface IProp { node: Doc; - keyframedata: Doc; + RegionData: Doc; + // routine: (innerFunc: () => void, args: any) => void; } -const KeyDataSchema = createSchema({}); - -const TimeAndKeyDataSchema = createSchema({ +const KeyframeDataSchema = createSchema({ time: defaultSpec("number", 0), key: Doc }); - -type KeyData = makeInterface<[typeof KeyDataSchema]>; -type TimeAndKey = makeInterface<[typeof TimeAndKeyDataSchema]>; -const KeyData = makeInterface(KeyDataSchema); -const TimeAndKey = makeInterface(TimeAndKeyDataSchema); +type KeyframeData = makeInterface<[typeof KeyframeDataSchema]>; +const KeyframeData = makeInterface(KeyframeDataSchema); -const KeyframeDataSchema = createSchema({ +const RegionDataSchema = createSchema({ position: defaultSpec("number", 0), duration: defaultSpec("number", 0), - kfs: listSpec(Doc) -}); - -type KeyframeData = makeInterface<[typeof KeyframeDataSchema]>; -export const KeyframeData = makeInterface(KeyframeDataSchema); - + keyframes: listSpec(Doc) +}); +type RegionData = makeInterface<[typeof RegionDataSchema]>; +export const RegionData = makeInterface(RegionDataSchema); -@observer -export class Keyframe extends React.Component<IProp> { +interface FlyoutInfo { + position: number; + fadeIn: number; + fadeOut: number; +} - @observable private _display:string = "none"; - @observable private _duration:number = 200; - @observable private _bar = React.createRef<HTMLDivElement>(); - @observable private _keyframes:number[] = []; +const FlyoutObj = { + position: 0, + fadeIn: 0, + fadeOut: 1 +}; - private _reactionDisposers: IReactionDisposer[] = []; - private _selectionManagerChanged?: IReactionDisposer; +export var FlyoutInfoContext = React.createContext<FlyoutInfo>(FlyoutObj); +@observer +export class Keyframe extends React.Component<IProp> { + @observable private _display: string = "none"; + @observable private _bar = React.createRef<HTMLDivElement>(); + @observable private _keyframes: number[] = []; + @observable private position: number = 0; + @observable private fadein: number = 0; + @observable private fadeout: number = 0; @action componentDidMount() { - + // need edge case here when keyframe data already exists when loading.....................; - // let keyframes = Cast(this.props.node.keyframes, listSpec(Doc)) as List<Doc>; - // if keyframes.indexOf() + this.fadein = 100; + FlyoutInfoContext = React.createContext<FlyoutInfo>({position: this.position, + fadeIn: this.fadein, + fadeOut: this.fadeout}); } componentWillUnmount() { - + } - + // @action // onPointerEnter = (e: React.PointerEvent) => { // e.preventDefault(); @@ -96,138 +90,168 @@ export class Keyframe extends React.Component<IProp> { // //this._display = "none"; // } + + @computed + private get regiondata() { + let index = this.regions.indexOf(this.props.RegionData); + return RegionData(this.regions[index] as Doc); + } + @computed - private get keyframes() { - return Cast(this.props.node.keyframes, listSpec(Doc)) as List<Doc>; + private get regions() { + return Cast(this.props.node.regions, listSpec(Doc)) as List<Doc>; } - @action + + + @action makeKeyData = (kfpos: number) => { //Kfpos is mouse offsetX, representing time - let hasData = false; - this.kfd.kfs!.forEach(TK => { //TK is TimeAndKey - TK = TK as Doc; - if (TK.time === kfpos){ - hasData = true; + let hasData = false; + this.regiondata.keyframes!.forEach(TK => { //TK is TimeAndKey + TK = TK as Doc; + if (TK.time === kfpos) { + hasData = true; } - }); - if (!hasData){ - let TK:Doc = new Doc(); - TK.time = kfpos; - TK.key = Doc.MakeCopy(this.props.node); - this.kfd.kfs!.push(TK); - } - + }); + if (!hasData) { + let TK: Doc = new Doc(); + TK.time = kfpos; + TK.key = Doc.MakeCopy(this.props.node); + this.regiondata.keyframes!.push(TK); + } + } - @computed - private get kfd(){ - let index = this.keyframes.indexOf(this.props.keyframedata); - return KeyframeData(this.keyframes[index] as Doc); - } - @action + @action onBarPointerDown = (e: React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.addEventListener("pointermove", this.onBarPointerMove); - document.addEventListener("pointerup", (e:PointerEvent) => { - document.removeEventListener("pointermove", this.onBarPointerMove); - }); - } - - @action - onBarPointerMove = (e:PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - if (this.kfd.position >= 0){ - let futureX = this.kfd.position + e.movementX; - if (futureX <= 0){ - this.kfd.position = 0; - } else{ - this.kfd.position += e.movementX; + e.preventDefault(); + e.stopPropagation(); + document.addEventListener("pointermove", this.onBarPointerMove); + document.addEventListener("pointerup", (e: PointerEvent) => { + document.removeEventListener("pointermove", this.onBarPointerMove); + }); + } + + @action + onBarPointerMove = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + let left = this.findAdjacentRegion(Direction.left); + let right = this.findAdjacentRegion(Direction.right); + let futureX = this.regiondata.position + e.movementX; + if (futureX <= 0) { + this.regiondata.position = 0; + } else if ((left && left.position + left.duration >= futureX)) { + this.regiondata.position = left.position + left.duration; + } else if ((right && right.position <= futureX + this.regiondata.duration)) { + this.regiondata.position = right.position - this.regiondata.duration; + } else { + this.regiondata.position = futureX; + } + + } + + @action + findAdjacentRegion = (dir: Direction): (RegionData | undefined) => { + let leftMost: (RegionData | undefined) = undefined; + let rightMost: (RegionData | undefined) = undefined; + this.regions.forEach(region => { + let neighbor = RegionData(region as Doc); + if (this.regiondata.position > neighbor.position) { + if (!leftMost || neighbor.position > leftMost.position) { + leftMost = neighbor; + } + } else if (this.regiondata.position < neighbor.position) { + if (!rightMost || neighbor.position < rightMost.position) { + rightMost = neighbor; + } } + }); + if (dir === Direction.left) { + return leftMost; + } else if (dir === Direction.right) { + return rightMost; } } - @action - onResizeLeft = (e:React.PointerEvent)=>{ - e.preventDefault(); - e.stopPropagation(); - document.addEventListener("pointermove", this.onDragResizeLeft); - document.addEventListener("pointerup", ()=>{ + @action + onResizeLeft = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.addEventListener("pointermove", this.onDragResizeLeft); + document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onDragResizeLeft); - }); + }); } - @action - onResizeRight = (e:React.PointerEvent)=> { - e.preventDefault(); - e.stopPropagation(); - document.addEventListener("pointermove", this.onDragResizeRight); - document.addEventListener("pointerup", ()=>{ + @action + onResizeRight = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.addEventListener("pointermove", this.onDragResizeRight); + document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onDragResizeRight); - }); - } - - @action - onDragResizeLeft = (e:PointerEvent)=>{ - e.preventDefault(); - e.stopPropagation(); - let bar = this._bar.current!; - let barX = bar.getBoundingClientRect().left; - let offset = e.clientX - barX; - this._duration -= offset; - this.kfd.position += offset; - this.kfd.kfs!.forEach(kf => { - kf = kf as Doc; - kf.time = NumCast(kf.time) + offset; - }); + }); + } + + @action + onDragResizeLeft = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + let bar = this._bar.current!; + let barX = bar.getBoundingClientRect().left; + 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; + }); this._keyframes.forEach(num => { - num += offset; - }); - } - - - @action - onDragResizeRight = (e:PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let bar = this._bar.current!; - let barX = bar.getBoundingClientRect().right; - let offset = e.clientX - barX; - console.log(offset); - this._duration += offset; - this.kfd.duration = this._duration; - } - - createDivider = (type?: string):JSX.Element => { - if (type === "left"){ - return <div className="divider" style={{right:"0px"}}></div>; - } else if (type === "right"){ - return <div className="divider" style={{left:"0px"}}> </div>; + num -= offset; + }); + } + + + @action + onDragResizeRight = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + 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 => { + if (type === "left") { + return <div className="divider" style={{ right: "0px" }}></div>; + } else if (type === "right") { + return <div className="divider" style={{ left: "0px" }}> </div>; } - return <div className="divider"></div>; + return <div className="divider"></div>; } @action createKeyframe = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); + e.preventDefault(); + e.stopPropagation(); let mouse = e.nativeEvent; - let position = NumCast(this.props.keyframedata.position); - this._keyframes.push(mouse.offsetX); - this.makeKeyData(position + mouse.offsetX); - + let position = NumCast(this.regiondata.position); + this._keyframes.push(mouse.offsetX); + this.makeKeyData(position + mouse.offsetX); } - @action + @action onMenuHover = (e: React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - if (this._display === "none"){ - this._display = "grid"; + e.preventDefault(); + e.stopPropagation(); + if (this._display === "none") { + this._display = "grid"; } else { - this._display = "none"; + this._display = "none"; } } @@ -236,18 +260,26 @@ export class Keyframe extends React.Component<IProp> { render() { return ( <div> - <div className="bar" ref={this._bar} style={{ transform: `translate(${this.props.keyframedata.position}px)`, width:`${this._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> + <FlyoutInfoContext.Provider value={{ + position: this.position, + fadeOut: this.fadeout, + fadeIn: this.fadein + }}> + <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> + </FlyoutInfoContext.Provider> </div> ); } diff --git a/src/client/views/nodes/Timeline.scss b/src/client/views/nodes/Timeline.scss index d89fce7fc..d59ed1323 100644 --- a/src/client/views/nodes/Timeline.scss +++ b/src/client/views/nodes/Timeline.scss @@ -1,5 +1,18 @@ @import "./../globalCssVariables.scss"; +.flyout{ + position: absolute; + z-index: 9999; + background-color: black; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + min-width: 150px; + color: black; + top: 100px; + + padding: 10px; + border-radius: 3px; +} + .timeline-container{ width:100%; height:300px; diff --git a/src/client/views/nodes/Timeline.tsx b/src/client/views/nodes/Timeline.tsx index e6b57ec75..02c2a530d 100644 --- a/src/client/views/nodes/Timeline.tsx +++ b/src/client/views/nodes/Timeline.tsx @@ -1,9 +1,9 @@ -import * as React from "react"; -import * as ReactDOM from "react-dom"; -import "./Timeline.scss"; +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 { 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"; @@ -14,35 +14,37 @@ 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"; +import { FlyoutInfoContext } from "./Keyframe"; @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; +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 _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 _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 _ticks: number[] = []; + @observable private _infoContainer = React.createRef<HTMLDivElement>(); + @observable private _ticks: number[] = []; + @action - componentDidMount(){ - let scrubber = this._scrubberbox.current!; - this._boxLength = scrubber.getBoundingClientRect().width; + 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; @@ -50,26 +52,26 @@ 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}`; - }); + 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; - } + for (let i = 0; i < this._time;) { + this._ticks.push(i); + i += 1000; + } } @action - componentDidUpdate(){ - this._time = 100001; + componentDidUpdate() { + this._time = 100001; } - componentWillUnmount(){ - + componentWillUnmount() { + } //for playing @@ -84,7 +86,7 @@ export class Timeline extends CollectionSubView(Document){ } @action - changeCurrentX = () => { + changeCurrentX = () => { if (this._currentBarX === this._boxLength && this._isPlaying) { this._currentBarX = 0; } @@ -109,141 +111,166 @@ export class Timeline extends CollectionSubView(Document){ } //for scrubber action - @action - onScrubberDown = (e:React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.addEventListener("pointermove", this.onScrubberMove); + @action + onScrubberDown = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.addEventListener("pointermove", this.onScrubberMove); document.addEventListener("pointerup", () => { - document.removeEventListener("pointermove", this.onScrubberMove); - }); - } + document.removeEventListener("pointermove", this.onScrubberMove); + }); + } - @action + @action onScrubberMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); + e.preventDefault(); + e.stopPropagation(); let scrubberbox = this._scrubberbox.current!; let left = scrubberbox.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left); - this._currentBarX = offsetX; + 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 + 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; + 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; + if (Math.floor(sec / 10) === 0) { + sec = "0" + sec; } - return `${min}:${sec}`; - + return `${min}:${sec}`; + } - @action - onPanDown = (e:React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.addEventListener("pointermove", this.onPanMove); + @action + onPanDown = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.addEventListener("pointermove", this.onPanMove); document.addEventListener("pointerup", () => { - document.removeEventListener("pointermove", this.onPanMove); + 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 + 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); + @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 ){ + @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 if (this._containerHeight + offset >= this.MAX_CONTAINER_HEIGHT) { + this._containerHeight = this.MAX_CONTAINER_HEIGHT; } else { - this._containerHeight += offset; + this._containerHeight += offset; } } - @action - minimize = (e:React.MouseEvent) => { - this._containerHeight = 0; + @action + minimize = (e: React.MouseEvent) => { + this._containerHeight = 0; + } + + renderFlyout = ():JSX.Element => { + // console.log("rendered"); + + // let data = React.useContext(FlyoutInfoContext); + return ( + <div style ={{height:"100px", width:"100px", backgroundColor:"yellow", position:"absolute"}}> + <p> + {/* {data.fadeIn} */} + </p> + </div> + ); } + - render(){ + + render() { return ( - <div className="timeline-container" style={{height:`${this._containerHeight}px`}}> + <div className="timeline-container" style={{ height: `${this._containerHeight}px` }}> <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 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}> + </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", pointerEvents:"none"}}> <p>{this.toTime(element)}</p></div>; + return <div className="tick" style={{ transform: `translate(${element / 20}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="scrubber" onPointerDown={this.onScrubberDown} style={{ transform: `translate(${this._currentBarX}px)` }}> <div className="scrubberhead"></div> </div> - <div className="trackbox" ref={this._trackbox} onPointerDown={this.onPanDown}> + <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}/>; + return <Track node={(doc as any).value() as Doc} currentBarX={this._currentBarX} />; })} - </div> - </div> + </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>;})} + {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 onPointerDown={this.onResizeDown}> + <FontAwesomeIcon className="resize" icon={faGripLines} /> + </div> + <FlyoutInfoContext.Consumer> + {value => ( + <div style ={{height:"100px", width:"100px", backgroundColor:"yellow", position:"absolute"}}> + <p> + {value.fadeIn} + </p> + </div>)} + </FlyoutInfoContext.Consumer> </div> - ); + ); } }
\ No newline at end of file diff --git a/src/client/views/nodes/Track.tsx b/src/client/views/nodes/Track.tsx index c7154fbb3..957128040 100644 --- a/src/client/views/nodes/Track.tsx +++ b/src/client/views/nodes/Track.tsx @@ -1,50 +1,17 @@ import * as React from "react"; -import * as ReactDOM from "react-dom"; import { observer } from "mobx-react"; import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS, IObservableObject } from "mobx"; import "./Track.scss"; -import { CollectionViewProps } from "../collections/CollectionBaseView"; -import { CollectionSubView, SubCollectionViewProps } from "../collections/CollectionSubView"; -import { DocumentViewProps, DocumentView } from "./DocumentView"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { Doc, DocListCastAsync, DocListCast } from "../../../new_fields/Doc"; import { Document, listSpec, createSchema, makeInterface, defaultSpec } from "../../../new_fields/Schema"; import { FieldValue, Cast, NumCast, BoolCast } from "../../../new_fields/Types"; -import { emptyStatement, thisExpression, react } from "babel-types"; -import { DocumentManager } from "../../util/DocumentManager"; -import { SelectionManager } from "../../util/SelectionManager"; import { List } from "../../../new_fields/List"; -import { Self } from "../../../new_fields/FieldSymbols"; -import { list } from "serializr"; -import { arrays, Dictionary } from "typescript-collections"; -import { forEach } from "typescript-collections/dist/lib/arrays"; -import { CompileScript } from "../../util/Scripting"; -import { FieldView } from "./FieldView"; -import { promises } from "fs"; -import { Tapable } from "tapable"; -import { Keyframe, KeyframeData } from "./Keyframe"; -import { timingSafeEqual } from "crypto"; -import { node } from "prop-types"; - -const PositionSchema = createSchema({ - x: defaultSpec("number", 0), - y: defaultSpec("number", 0) -}); - -type Position = makeInterface<[typeof PositionSchema]>; -const Position = makeInterface(PositionSchema); -const TimeAndPositionSchema = createSchema({ - time: defaultSpec("number", 0), - position: Doc -}); - -type TimeAndPosition = makeInterface<[typeof TimeAndPositionSchema]>; -const TimeAndPosition = makeInterface(TimeAndPositionSchema); - - -interface IProp{ - node: Doc; - currentBarX: number; +import { Keyframe, RegionData } from "./Keyframe"; + +interface IProp { + node: Doc; + currentBarX: number; + // setPosition: (position: number) => any; } @observer @@ -57,54 +24,88 @@ export class Track extends React.Component<IProp> { @observable private _keys = ["x", "y", "width", "height", "panX", "panY", "scale"]; - @computed - private get keyframes(){ - return Cast(this.props.node.keyframes, listSpec(Doc)) as List<Doc> ; + componentWillMount() { + this.props.node.regions = new List<Doc>(); + console.log((Cast(this.props.node.regions, listSpec(Doc)) as List<Doc>).length); } + @action + componentDidMount() { + this.props.node.hidden = true; + this.props.node.opacity = 0; + reaction(() => this.props.currentBarX, () => { + let region: (Doc | undefined) = this.findRegion(this.props.currentBarX); + if (region !== undefined) { + this.props.node.hidden = false; + this.timeChange(this.props.currentBarX); + } else { + this.props.node.hidden = true; + } + }); + + // reaction(() => { + // let keys = Doc.allKeys(this.props.node); + // let x = keys.indexOf("keyframes"); + // let afterX = keys.slice(x + 1); + // let beforeX = keys.slice(0, x); + // keys = beforeX.concat(afterX); + // return keys.map(key => FieldValue(this.props.node[key])); + // }, data => { + // if (this.keyframes.length !== 0){ + // let kf:(Doc | undefined) = this.findKeyframe(this.props.currentBarX); + // } + // }); + } + /** + * removes reaction when the component is removed from the timeline + */ + componentWillUnmount() { + this._reactionDisposers.forEach(disp => disp()); + this._reactionDisposers = []; + } @action timeChange = async (time: number) => { - let singlekfd = this.findKeyframe(time); - let leftkf: (Doc | undefined) = this.calcMinLeft(singlekfd!); - let rightkf: (Doc | undefined) = this.calcMinRight(singlekfd!); - - if (leftkf && rightkf){ - this.interpolate(leftkf, rightkf); - } else if(leftkf){ - console.log("left exists"); + let region = this.findRegion(time); + 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"); this._keys.forEach(k => { - let data = leftkf!.key as Doc; - this.props.node[k] = data[k]; - }); - } else if (rightkf){ - console.log("right exists"); + let data = leftkf!.key as Doc; + this.props.node[k] = data[k]; + }); + } else if (rightkf) { + console.log("right exists"); this._keys.forEach(k => { - let data = rightkf!.key as Doc; - this.props.node[k] = data[k]; - }); + let data = rightkf!.key as Doc; + this.props.node[k] = data[k]; + }); } } - + /** * calculates the closest left keyframe, if there is one * @param kfList: keyframe list * @param time */ @action - calcMinLeft = (singlekfd: Doc): (Doc|undefined) => { //returns the time of the closet keyframe to the left - let leftKf:Doc = new Doc(); - leftKf.time = Infinity; - (singlekfd.kfs! as List<Doc>).forEach((kf) => { + calcMinLeft = (region: Doc): (Doc | undefined) => { //returns the time of the closet keyframe to the left + let leftKf: Doc = new Doc(); + leftKf.time = Infinity; + (region.keyframes! as List<Doc>).forEach((kf) => { kf = kf as Doc; if (NumCast(kf.time) < this.props.currentBarX && NumCast(leftKf.time) > NumCast(kf.time)) { - leftKf = kf; + leftKf = kf; } }); - if (NumCast(leftKf.time) === Infinity){ - return undefined; + if (NumCast(leftKf.time) === Infinity) { + return undefined; } return leftKf; } @@ -115,36 +116,36 @@ export class Track extends React.Component<IProp> { * @param time: time */ @action - calcMinRight = (singlekfd: Doc): (Doc|undefined) => { //returns the time of the closest keyframe to the right - let rightKf:Doc = new Doc(); - rightKf.time = Infinity; - (singlekfd.kfs! as List<Doc>).forEach((kf) => { + calcMinRight = (region: Doc): (Doc | undefined) => { //returns the time of the closest keyframe to the right + let rightKf: Doc = new Doc(); + rightKf.time = 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)) { - rightKf = kf; + rightKf = kf; } }); - if (NumCast(rightKf.time) === Infinity){ - return undefined; + if (NumCast(rightKf.time) === Infinity) { + return undefined; } return rightKf; } - /** - * Linearly interpolates a document from time1 to time2 - * @param Doc that needs to be modified - * @param kf1 timeandposition of the first yellow bar - * @param kf2 timeandposition of the second yellow bar - * @param time time that you want to interpolate - */ + /** + * Linearly interpolates a document from time1 to time2 + * @param Doc that needs to be modified + * @param kf1 timeandposition of the first yellow bar + * @param kf2 timeandposition of the second yellow bar + * @param time time that you want to interpolate + */ @action interpolate = async (kf1: Doc, kf2: Doc) => { - let node1 = kf1.key as Doc; - let node2 = kf2.key as Doc; - console.log(toJS(node1)); - console.log(toJS(node2)); + let node1 = kf1.key as Doc; + let node2 = kf2.key as Doc; + console.log(toJS(node1)); + console.log(toJS(node2)); const dif_time = NumCast(kf2.time) - NumCast(kf1.time); const ratio = (this.props.currentBarX - NumCast(kf1.time)) / dif_time; //linear @@ -155,74 +156,38 @@ export class Track extends React.Component<IProp> { }); } - @action - componentDidMount() { - this.props.node.hidden = true; - this.props.node.opacity = 0; - this.props.node.keyframes = new List<Doc>(); - reaction (() => this.props.currentBarX, () => { - this.keyframes.forEach((kfd) => { - kfd = KeyframeData(kfd as Doc); - if (kfd.length !== 0){ - let singlekfd:(Doc | undefined) = this.findKeyframe(this.props.currentBarX); - if (singlekfd !== undefined){ - this.props.node.hidden = false; - this.timeChange(this.props.currentBarX); - } else { - this.props.node.hidden = true; - } - } - }); - }); - - // reaction(() => { - // let keys = Doc.allKeys(this.props.node); - // let x = keys.indexOf("keyframes"); - // let afterX = keys.slice(x + 1); - // let beforeX = keys.slice(0, x); - // keys = beforeX.concat(afterX); - // return keys.map(key => FieldValue(this.props.node[key])); - // }, data => { - // if (this.keyframes.length !== 0){ - // let kf:(Doc | undefined) = this.findKeyframe(this.props.currentBarX); - // } - // }); - } - @action - findKeyframe(time:number): (Doc | undefined){ - let foundKeyframe = undefined; - this.keyframes.map(kfd => { - kfd = KeyframeData(kfd as Doc); - if (time >= NumCast(kfd.position) && time <= (NumCast(kfd.position) + NumCast(kfd.duration))){ - foundKeyframe = kfd; - } - }); - return foundKeyframe; - } - /** - * removes reaction when the component is removed from the timeline - */ - componentWillUnmount() { - this._reactionDisposers.forEach(disp => disp()); - this._reactionDisposers = []; + @action + findRegion(time: number): (Doc | undefined) { + let foundRegion = undefined; + this.regions.map(region => { + region = RegionData(region as Doc); + if (time >= NumCast(region.position) && time <= (NumCast(region.position) + NumCast(region.duration))) { + foundRegion = region; + } + }); + return foundRegion; } - @observable private _keyframes: JSX.Element[] = []; - - @action + @action onInnerDoubleClick = (e: React.MouseEvent) => { - let inner = this._inner.current!; + let inner = this._inner.current!; let left = inner.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left); - let keyframedata:Doc = new Doc(); - keyframedata.duration = 200; - keyframedata.position = offsetX; - keyframedata.kfs = new List<Doc>(); - this.keyframes.push(keyframedata); - this._keyframes.push(<Keyframe node={this.props.node} keyframedata={keyframedata}/>); + let regiondata: Doc = new Doc(); //creating regiond ata + regiondata.duration = 200; + regiondata.position = offsetX; + regiondata.keyframes = new List<Doc>(); + this.regions.push(regiondata); + } + + + @computed + private get regions() { + console.log((Cast(this.props.node.regions, listSpec(Doc)) as List<Doc>).length + "from get"); + return Cast(this.props.node.regions, listSpec(Doc)) as List<Doc>; } render() { @@ -230,7 +195,9 @@ export class Track extends React.Component<IProp> { <div className="track-container"> <div className="track"> <div className="inner" ref={this._inner} onDoubleClick={this.onInnerDoubleClick}> - {this._keyframes.map((element)=> element)} + {this.regions.map((region) => { + return <Keyframe node={this.props.node} RegionData={region as Doc}/>; + })} </div> </div> </div> |