diff options
Diffstat (limited to 'src/client/views/nodes/Keyframe.tsx')
-rw-r--r-- | src/client/views/nodes/Keyframe.tsx | 364 |
1 files changed, 198 insertions, 166 deletions
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> ); } |