diff options
| author | andrewdkim <adkim414@gmail.com> | 2019-08-01 17:00:10 -0400 |
|---|---|---|
| committer | andrewdkim <adkim414@gmail.com> | 2019-08-01 17:00:10 -0400 |
| commit | bf3d45f8a16d23384a308f65adfa9a2baee495af (patch) | |
| tree | 15e8d74c57436a5987133bf5732764a8155bb282 /src/client/views/nodes | |
| parent | 59f3d2405870135314d00ea80acae0dc17b99955 (diff) | |
prosemirror
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/FormattedTextBox.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/Keyframe.scss | 94 | ||||
| -rw-r--r-- | src/client/views/nodes/Keyframe.tsx | 550 | ||||
| -rw-r--r-- | src/client/views/nodes/Timeline.scss | 170 | ||||
| -rw-r--r-- | src/client/views/nodes/Timeline.tsx | 530 | ||||
| -rw-r--r-- | src/client/views/nodes/Track.scss | 15 | ||||
| -rw-r--r-- | src/client/views/nodes/Track.tsx | 266 |
7 files changed, 7 insertions, 1628 deletions
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f019868aa..9b37a6491 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEdit, faSmile, faTextHeight } from '@fortawesome/free-solid-svg-icons'; -import { action, IReactionDisposer, observable, reaction, runInAction, computed, trace } from "mobx"; +import { action, IReactionDisposer, observable, reaction, runInAction, computed, trace, toJS } from "mobx"; import { observer } from "mobx-react"; import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; @@ -35,6 +35,7 @@ import "./FormattedTextBox.scss"; import React = require("react"); import { DateField } from '../../../new_fields/DateField'; import { Utils } from '../../../Utils'; +import { toSvgDataURL } from 'html-to-image'; library.add(faEdit); library.add(faSmile, faTextHeight); @@ -122,13 +123,14 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this.props.isOverlay) { DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); } + toJS(console.log(this.props.Document)); document.addEventListener("paste", this.paste); } @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "dummy"); } - @computed get dataDoc() { return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : Doc.GetProto(this.props.Document); } + @computed get dataDoc() {return BoolCast(this.props.Document.isTemplate) && this.props.DataDoc ? this.props.DataDoc : Doc.GetProto(this.props.Document); } paste = (e: ClipboardEvent) => { if (e.clipboardData && this._editorView) { @@ -163,7 +165,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this._applyingChange = true; if (this.extensionDoc) this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n"); if (this.extensionDoc) this.extensionDoc.lastModified = new DateField(new Date(Date.now())); - this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON())); + this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON())); + this.props.Document.stateData = JSON.stringify(state.toJSON()); + this.props.Document.dataDocTest = this.dataDoc[this.props.fieldKey]; this._applyingChange = false; let title = StrCast(this.dataDoc.title); if (title && title.startsWith("-") && this._editorView && !this.Document.customTitle) { diff --git a/src/client/views/nodes/Keyframe.scss b/src/client/views/nodes/Keyframe.scss deleted file mode 100644 index b1e8b0b65..000000000 --- a/src/client/views/nodes/Keyframe.scss +++ /dev/null @@ -1,94 +0,0 @@ -@import "./../globalCssVariables.scss"; - -.bar { - height: 100%; - width: 5px; - position: absolute; - - // pointer-events: none; - .menubox { - width: 200px; - height:200px; - top: 50%; - position: relative; - background-color: $light-color; - .menutable{ - tr:nth-child(odd){ - background-color:$light-color-secondary; - } - } - } - - .leftResize{ - left:-12.5px; - height:25px; - width:25px; - border-radius: 50%; - background-color: white; - border:3px solid black; - top: calc(50% - 12.5px); - z-index: 1000; - position:absolute; - } - .rightResize{ - right:-12.5px; - height:25px; - width:25px; - border-radius: 50%; - top:calc(50% - 12.5px); - background-color:white; - border:3px solid black; - z-index: 1000; - position:absolute; - } - .fadeLeft{ - left:0px; - height:100%; - position:absolute; - pointer-events: none; - background: linear-gradient(to left, #4d9900 10%, $light-color); - } - - .fadeRight{ - right:0px; - height:100%; - position:absolute; - pointer-events: none; - background: linear-gradient(to right, #4d9900 10%, $light-color); - } - .divider{ - height:100%; - width: 1px; - position: absolute; - background-color:black; - cursor: col-resize; - pointer-events:none; - } - .keyframe{ - height:100%; - position:absolute; - } - .keyframeCircle{ - left:-15px; - height:30px; - width:30px; - border-radius: 50%; - top:calc(50% - 15px); - background-color:white; - border:3px solid green; - z-index: 1000; - position:absolute; - } - - .fadeIn-container, .fadeOut-container, .body-container{ - position:absolute; - height:100%; - background-color: rgba(0, 0, 0, 0.5); - opacity: 0; - } - - -} - - - diff --git a/src/client/views/nodes/Keyframe.tsx b/src/client/views/nodes/Keyframe.tsx deleted file mode 100644 index 780928e77..000000000 --- a/src/client/views/nodes/Keyframe.tsx +++ /dev/null @@ -1,550 +0,0 @@ -import * as React from "react"; -import "./Keyframe.scss"; -import "./Timeline.scss"; -import "./../globalCssVariables.scss"; -import { observer, Observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS, isComputedProp, runInAction } from "mobx"; -import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; -import { Cast, FieldValue, StrCast, NumCast } from "../../../new_fields/Types"; -import { List } from "../../../new_fields/List"; -import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema"; -import { FlyoutProps } from "./Timeline"; -import { Transform } from "../../util/Transform"; -import { InkField, StrokeData } from "../../../new_fields/InkField"; -import { number } from "prop-types"; - -export namespace KeyframeFunc { - export enum KeyframeType { - fade = "fade", - default = "default", - } - export enum Direction { - left = "left", - right = "right" - } - export const findAdjacentRegion = (dir: KeyframeFunc.Direction, currentRegion: Doc, regions: List<Doc>): (RegionData | undefined) => { - let leftMost: (RegionData | undefined) = undefined; - let rightMost: (RegionData | undefined) = undefined; - DocListCast(regions).forEach(region => { - let neighbor = RegionData(region as Doc); - if (currentRegion.position! > neighbor.position) { - if (!leftMost || neighbor.position > leftMost.position) { - leftMost = neighbor; - } - } else if (currentRegion.position! < neighbor.position) { - if (!rightMost || neighbor.position < rightMost.position) { - rightMost = neighbor; - } - } - }); - if (dir === Direction.left) { - return leftMost; - } else if (dir === Direction.right) { - return rightMost; - } - }; - - export const calcMinLeft = async (region: Doc, currentBarX: number, ref?: Doc) => { //returns the time of the closet keyframe to the left - let leftKf: (Doc | undefined) = undefined; - let time: number = 0; - let keyframes = await DocListCastAsync(region.keyframes!); - keyframes!.forEach((kf) => { - let compTime = currentBarX; - if (ref) { - compTime = NumCast(ref.time); - } - if (NumCast(kf.time) < compTime && NumCast(kf.time) >= time) { - leftKf = kf; - time = NumCast(kf.time); - } - }); - return leftKf; - }; - - - export const calcMinRight = async (region: Doc, currentBarX: number, ref?: Doc) => { //returns the time of the closest keyframe to the right - let rightKf: (Doc | undefined) = undefined; - let time: number = Infinity; - let keyframes = await DocListCastAsync(region.keyframes!); - keyframes!.forEach((kf) => { - let compTime = currentBarX; - if (ref) { - compTime = NumCast(ref.time); - } - if (NumCast(kf.time) > compTime && NumCast(kf.time) <= NumCast(time)) { - rightKf = kf; - time = NumCast(kf.time); - } - }); - return rightKf; - }; - - export const defaultKeyframe = () => { - let regiondata = new Doc(); //creating regiondata - regiondata.duration = 200; - regiondata.position = 0; - regiondata.fadeIn = 20; - regiondata.fadeOut = 20; - regiondata.functions = new List<Doc>(); - return regiondata; - }; -} - -export const RegionDataSchema = createSchema({ - position: defaultSpec("number", 0), - duration: defaultSpec("number", 0), - keyframes: listSpec(Doc), - fadeIn: defaultSpec("number", 0), - fadeOut: defaultSpec("number", 0), - functions: listSpec(Doc) -}); -export type RegionData = makeInterface<[typeof RegionDataSchema]>; -export const RegionData = makeInterface(RegionDataSchema); - -interface IProps { - node: Doc; - RegionData: Doc; - collection: Doc; - changeCurrentBarX: (x: number) => void; - setFlyout: (props: FlyoutProps) => any; - transform: Transform; -} - -@observer -export class Keyframe extends React.Component<IProps> { - - @observable private _bar = React.createRef<HTMLDivElement>(); - @observable private _gain = 20; //default - - @computed - private get regiondata() { - let index = this.regions.indexOf(this.props.RegionData); - return RegionData(this.regions[index] as Doc); - } - - @computed - private get regions() { - return Cast(this.props.node.regions, listSpec(Doc)) as List<Doc>; - } - - @computed - private get firstKeyframe() { - let first: (Doc | undefined) = undefined; - DocListCast(this.regiondata.keyframes!).forEach(kf => { - if (kf.type !== KeyframeFunc.KeyframeType.fade) { - if (!first || first && NumCast(kf.time) < NumCast(first.time)) { - first = kf; - } - } - }); - return first; - } - - @computed - private get lastKeyframe() { - let last: (Doc | undefined) = undefined; - DocListCast(this.regiondata.keyframes!).forEach(kf => { - if (kf.type !== KeyframeFunc.KeyframeType.fade) { - if (!last || last && NumCast(kf.time) > NumCast(last.time)) { - last = kf; - } - } - }); - return last; - } - @computed - private get keyframes(){ - return DocListCast(this.regiondata.keyframes); - } - - @computed - private get inks() { - if (this.props.collection.data_ext) { - let data_ext = Cast(this.props.collection.data_ext, Doc) as Doc; - let ink = Cast(data_ext.ink, InkField) as InkField; - if (ink) { - return ink.inkData; - } - } - } - - async componentWillMount() { - if (!this.regiondata.keyframes) { - this.regiondata.keyframes = new List<Doc>(); - } - let fadeIn = await this.makeKeyData(this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade)!; - let fadeOut = await this.makeKeyData(this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade)!; - let start = await this.makeKeyData(this.regiondata.position, KeyframeFunc.KeyframeType.fade)!; - let finish = await this.makeKeyData(this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.fade)!; - (fadeIn.key! as Doc).opacity = 1; - (fadeOut.key! as Doc).opacity = 1; - (start.key! as Doc).opacity = 0.1; - (finish.key! as Doc).opacity = 0.1; - - observe(this.regiondata, change => { - if (change.type === "update") { - fadeIn.time = this.regiondata.position + this.regiondata.fadeIn; - fadeOut.time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; - start.time = this.regiondata.position; - finish.time = this.regiondata.position + this.regiondata.duration; - this.regiondata.keyframes![this.regiondata.keyframes!.indexOf(fadeIn)] = fadeIn; - this.regiondata.keyframes![this.regiondata.keyframes!.indexOf(fadeOut)] = fadeOut; - this.regiondata.keyframes![this.regiondata.keyframes!.indexOf(start)] = start; - this.regiondata.keyframes![this.regiondata.keyframes!.indexOf(finish)] = finish; - this.forceUpdate(); - } - }); - } - - @action - makeKeyData = async (kfpos: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - let doclist = (await DocListCastAsync(this.regiondata.keyframes))!; - let existingkf: (Doc | undefined) = undefined; - doclist.forEach(TK => { - TK = TK as Doc; - if (TK.time === kfpos) existingkf = TK; - }); - if (existingkf) return existingkf; - let TK: Doc = new Doc(); - TK.time = kfpos; - TK.key = Doc.MakeCopy(this.props.node, true); - TK.type = type; - this.regiondata.keyframes!.push(TK); - - let interpolationFunctions = new Doc(); - interpolationFunctions.interpolationX = new List<number>([0, 1]); - interpolationFunctions.interpolationY = new List<number>([0,100]); - interpolationFunctions.pathX = new List<number>(); - interpolationFunctions.pathY = new List<number>(); - - this.regiondata.functions!.push(interpolationFunctions); - let found:boolean = false; - this.regiondata.keyframes!.forEach(compkf => { - compkf = compkf as Doc; - if (kfpos < NumCast(compkf.time) && !found) { - runInAction(() => { - this.regiondata.keyframes!.splice(doclist.indexOf(compkf as Doc), 0, TK); - this.regiondata.keyframes!.pop(); - found = true; - }); - return; - } - }); - - let index = this.regiondata.keyframes!.indexOf(TK); - console.log(toJS(this.regiondata.keyframes!)); - - return TK; - } - - @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(); - let left = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions)!; - let right = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions!); - let prevX = this.regiondata.position; - 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; - } - for (let i = 0; i < this.regiondata.keyframes!.length; i++) { - if ((this.regiondata.keyframes![i] as Doc).type !== KeyframeFunc.KeyframeType.fade) { - let movement = this.regiondata.position - prevX; - (this.regiondata.keyframes![i] as Doc).time = NumCast((this.regiondata.keyframes![i] as Doc).time) + movement; - } - } - this.forceUpdate(); - } - - @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", () => { - document.removeEventListener("pointermove", this.onDragResizeRight); - }); - } - - @action - onDragResizeLeft = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let bar = this._bar.current!; - let offset = Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale); - let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions); - let firstkf: (Doc | undefined) = this.firstKeyframe; - if (firstkf && this.regiondata.position + this.regiondata.fadeIn + offset >= NumCast(firstkf!.time)) { - let dif = NumCast(firstkf!.time) - (this.regiondata.position + this.regiondata.fadeIn); - this.regiondata.position = NumCast(firstkf!.time) - this.regiondata.fadeIn; - this.regiondata.duration -= dif; - } else if (this.regiondata.duration - offset < this.regiondata.fadeIn + this.regiondata.fadeOut) { // no keyframes, just fades - this.regiondata.position -= (this.regiondata.fadeIn + this.regiondata.fadeOut - this.regiondata.duration); - this.regiondata.duration = this.regiondata.fadeIn + this.regiondata.fadeOut; - } else if (leftRegion && this.regiondata.position + offset <= leftRegion.position + leftRegion.duration) { - let dif = this.regiondata.position - (leftRegion.position + leftRegion.duration); - this.regiondata.position = leftRegion.position + leftRegion.duration; - this.regiondata.duration += dif; - - } else { - this.regiondata.duration -= offset; - this.regiondata.position += offset; - } - } - - - @action - onDragResizeRight = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let bar = this._bar.current!; - let offset = Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale); - let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions); - if (this.lastKeyframe! && this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= NumCast((this.lastKeyframe! as Doc).time)) { - let dif = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut - NumCast((this.lastKeyframe! as Doc).time); - this.regiondata.duration -= dif; - } else if (this.regiondata.duration + offset < this.regiondata.fadeIn + this.regiondata.fadeOut) { // nokeyframes, just fades - this.regiondata.duration = this.regiondata.fadeIn + this.regiondata.fadeOut; - } else if (rightRegion && this.regiondata.position + this.regiondata.duration + offset >= rightRegion.position) { - let dif = rightRegion.position - (this.regiondata.position + this.regiondata.duration); - this.regiondata.duration += dif; - } else { - this.regiondata.duration += offset; - } - } - - createDivider = (type?: KeyframeFunc.Direction): 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>; - } - - @action - createKeyframe = async (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - let bar = this._bar.current!; - let offset = Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale); - if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends - let position = NumCast(this.regiondata.position); - await this.makeKeyData(Math.round(position + offset)); - console.log(this.regiondata.keyframes!.length); - this.props.changeCurrentBarX(NumCast(Math.round(position + offset))); //first move the keyframe to the correct location and make a copy so the correct file gets coppied - } - } - - - @action - moveKeyframe = async (e: React.MouseEvent, kf: Doc) => { - e.preventDefault(); - e.stopPropagation(); - this.props.changeCurrentBarX(NumCast(kf.time!)); - } - - - @action - onKeyframeOver = (e: React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - this.props.node.backgroundColor = "#000000"; - - } - @action - private createKeyframeJSX = (kf: Doc, type = KeyframeFunc.KeyframeType.default) => { - if (type === KeyframeFunc.KeyframeType.default) { - return ( - <div className="keyframe" style={{ left: `${NumCast(kf.time) - this.regiondata.position}px` }}> - {this.createDivider()} - <div className="keyframeCircle" onPointerDown={(e) => { this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - }}></div> - </div>); - } - return ( - <div className="keyframe" style={{ left: `${NumCast(kf.time) - this.regiondata.position}px` }}> - {this.createDivider()} - </div> - ); - } - - onContainerOver = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => { - e.preventDefault(); - e.stopPropagation(); - let div = ref.current!; - div.style.opacity = "1"; - } - - onContainerOut = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => { - e.preventDefault(); - e.stopPropagation(); - let div = ref.current!; - div.style.opacity = "0"; - } - - - private _reac: (undefined | IReactionDisposer) = undefined; - private _plotList: ([string, StrokeData] | undefined) = undefined; - private _interpolationKeyframe: (Doc | undefined) = undefined; - private _type: string = ""; - - @action - onContainerDown = (e: React.MouseEvent, kf: Doc) => { - e.preventDefault(); - e.stopPropagation(); - let listenerCreated = false; - let type = prompt("Type? (interpolate or path)"); - if (type) { - if (type !== "interpolate" && type !=="path") { - alert("Wrong type. Try again."); - return; - } - this._type = type; - this.props.collection.backgroundColor = "rgb(0,0,0)"; - this._reac = reaction(() => { - return this.inks; - }, data => { - if (!listenerCreated) { - this._plotList = Array.from(data!)[data!.size - 1]!; - this._interpolationKeyframe = kf; - document.addEventListener("pointerup", this.onReactionListen); - listenerCreated = true; - } - }); - } - - } - - - - - @action - onReactionListen = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let message = prompt("GRAPHING MODE: Enter gain"); - if (message) { - let messageContent = parseInt(message, 10); - if (messageContent === NaN) { - this._gain = Infinity; - } else { - this._gain = messageContent; - } - - } - if (this._reac && this._plotList && this._interpolationKeyframe) { - this.props.collection.backgroundColor = "#FFF"; - this._reac(); - let xPlots = new List<number>(); - let yPlots = new List<number>(); - let maxY = 0; - let minY = Infinity; - let pathData = this._plotList![1].pathData; - for (let i = 0; i < pathData.length - 1;) { - let val = pathData[i]; - if (val.y > maxY) { - maxY = val.y; - } - if (val.y < minY) { - minY = val.y; - } - xPlots.push(val.x); - yPlots.push(val.y); - let increment = Math.floor(pathData.length / this._gain); - if (pathData.length > this._gain) { - if (i + increment < pathData.length) { - i = i + increment; - } else { - i = pathData.length - 1; - } - } else { - i++; - } - } - let index = this.keyframes.indexOf(this._interpolationKeyframe!); - if (this._type === "interpolate"){ - (Cast(this.regiondata.functions![index], Doc) as Doc).interpolationX = xPlots; - (Cast(this.regiondata.functions![index], Doc) as Doc).interpolationY = yPlots; - } else if (this._type === "path") { - (Cast(this.regiondata.functions![index], Doc) as Doc).pathX = xPlots; - (Cast(this.regiondata.functions![index], Doc) as Doc).pathY = yPlots; - } - - this._reac = undefined; - this._interpolationKeyframe = undefined; - this._plotList = undefined; - this._type = ""; - document.removeEventListener("pointerup", this.onReactionListen); - } - } - - - render() { - return ( - <div> - <div className="bar" ref={this._bar} style={{ transform: `translate(${this.regiondata.position}px)`, width: `${this.regiondata.duration}px`, background: `linear-gradient(90deg, rgba(77, 153, 0, 0) 0%, rgba(77, 153, 0, 1) ${this.regiondata.fadeIn / this.regiondata.duration * 100}%, rgba(77, 153, 0, 1) ${(this.regiondata.duration - this.regiondata.fadeOut) / this.regiondata.duration * 100}%, rgba(77, 153, 0, 0) 100% )` }} - onPointerDown={this.onBarPointerDown} - onDoubleClick={this.createKeyframe} - onContextMenu={action((e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - let offsetLeft = this._bar.current!.getBoundingClientRect().left - this._bar.current!.parentElement!.getBoundingClientRect().left; - let offsetTop = this._bar.current!.getBoundingClientRect().top; //+ this._bar.current!.parentElement!.getBoundingClientRect().top; - this.props.setFlyout({ x: offsetLeft * this.props.transform.Scale, y: offsetTop * this.props.transform.Scale, display: "block", regiondata: this.regiondata, regions: this.regions }); - })}> - <div className="leftResize" onPointerDown={this.onResizeLeft} ></div> - <div className="rightResize" onPointerDown={this.onResizeRight}></div> - {this.regiondata.keyframes!.map(kf => { - return this.createKeyframeJSX(kf as Doc, (kf! as Doc).type as KeyframeFunc.KeyframeType); - })} - {this.keyframes.map( kf => { - if(this.keyframes.indexOf(kf) !== this.keyframes.length - 1) { - - let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; - let bodyRef = React.createRef<HTMLDivElement>(); - return ( - <div ref={bodyRef}className="body-container" style={{left: `${NumCast(kf.time) - this.regiondata.position}px`, width:`${NumCast(left!.time) - NumCast(kf.time)}px`}} - onPointerOver={(e) => { this.onContainerOver(e, bodyRef); }} - onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} - onContextMenu={(e) => { this.onContainerDown(e, kf); }}> - </div> - ); - } - })} - - </div> - </div> - ); - } -}
\ No newline at end of file diff --git a/src/client/views/nodes/Timeline.scss b/src/client/views/nodes/Timeline.scss deleted file mode 100644 index 47f448adb..000000000 --- a/src/client/views/nodes/Timeline.scss +++ /dev/null @@ -1,170 +0,0 @@ -@import "./../globalCssVariables.scss"; - -.minimize{ - position:relative; - z-index: 1000; - height: 30px; - width: 100px; -} -.flyout-container{ - background-color: transparent; - position:absolute; - - z-index:9999; - height: 150px; - width: 150px; - - .flyout{ - background-color: transparent; - transform: rotate(180deg); - left:0px; - top:0px; - width: 100%; - height: 100%; - } - .input-container{ - position: absolute; - right:0px; - top: 30px; - width: 70px; - input{ - width: 100%; - } - } - .text-container{ - position:absolute; - top:30px; - left:0px; - color:white - } -} - -.placement-highlight{ - background-color:blue; - transform: translate(0px, 0px); - transition: width 1000ms ease-in-out; - transition: height 1000ms ease-in-out; - position: absolute; -} - -.timeline-container{ - width:100%; - height:300px; - position:absolute; - background-color: $light-color-secondary; - box-shadow: 0px 10px 20px; - //transition: transform 1000ms ease-in-out; - - .toolbox{ - position:absolute; - width: 100%; - top: 10px; - left: 20px; - div{ - float:left; - margin-left: 10px; - position:relative; - .overview{ - width: 200px; - height: 100%; - background-color: black; - position:absolute; - } - } - } - .info-container{ - margin-top: 50px; - right:20px; - position:absolute; - height: calc(100% - 100px); - width: calc(100% - 140px); - overflow: hidden; - - .scrubberbox{ - position:absolute; - background-color: transparent; - height: 30px; - width:100%; - - .tick{ - height:100%; - width: 1px; - background-color:black; - - } - } - .scrubber{ - top:30px; - height: 100%; - width: 2px; - position:absolute; - z-index: 1001; - background-color:black; - .scrubberhead{ - top: -30px; - height: 30px; - width: 30px; - background-color:transparent; - border-radius: 50%; - border: 5px solid black; - left: -15px; - position:absolute; - } - } - - .trackbox{ - top: 30px; - height:calc(100% - 30px); - width:100%; - border:1px; - overflow:hidden; - background-color:white; - position:absolute; - box-shadow: -10px 0px 10px 10px grey; - } - - } - .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); - } -} - - - -.overview{ - position: absolute; - height: 50px; - width: 200px; - background-color: black; - .container{ - position: absolute; - float: left 0px; - top: 25%; - height: 75%; - width: 100%; - background-color: grey; - } -}
\ No newline at end of file diff --git a/src/client/views/nodes/Timeline.tsx b/src/client/views/nodes/Timeline.tsx deleted file mode 100644 index 923e99e63..000000000 --- a/src/client/views/nodes/Timeline.tsx +++ /dev/null @@ -1,530 +0,0 @@ -import * as React from "react"; -import "./Timeline.scss"; -import { CollectionSubView } from "../collections/CollectionSubView"; -import { Document, listSpec } from "../../../new_fields/Schema"; -import { observer } from "mobx-react"; -import { Track } from "./Track"; -import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS, Reaction, IObservableObject, trace, autorun, runInAction } from "mobx"; -import { Cast, NumCast, FieldValue, StrCast } from "../../../new_fields/Types"; -import { List } from "../../../new_fields/List"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDown, faClock, faPauseCircle } from "@fortawesome/free-solid-svg-icons"; -import { ContextMenuProps } from "../ContextMenuItem"; -import { ContextMenu } from "../ContextMenu"; -import { DocumentManager } from "../../util/DocumentManager"; -import { VideoBox } from "./VideoBox"; -import { VideoField } from "../../../new_fields/URLField"; -import { CollectionVideoView } from "../collections/CollectionVideoView"; -import { Transform } from "../../util/Transform"; -import { faGrinTongueSquint } from "@fortawesome/free-regular-svg-icons"; -import { InkField } from "../../../new_fields/InkField"; -import { AddComparisonParameters } from "../../northstar/model/idea/idea"; -import { keepAlive } from "mobx-utils"; - - -export interface FlyoutProps { - x?: number; - y?: number; - display?: string; - regiondata?: Doc; - regions?: List<Doc>; -} - - -@observer -export class Timeline extends CollectionSubView(Document) { - private readonly DEFAULT_CONTAINER_HEIGHT: number = 300; - private readonly DEFAULT_TICK_SPACING: number = 50; - private readonly MIN_CONTAINER_HEIGHT: number = 205; - private readonly MAX_CONTAINER_HEIGHT: number = 800; - private readonly DEFAULT_TICK_INCREMENT: number = 1000; - - @observable private _isMinimized = false; - @observable private _tickSpacing = this.DEFAULT_TICK_SPACING; - @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; - - @observable private _scrubberbox = React.createRef<HTMLDivElement>(); - @observable private _scrubber = React.createRef<HTMLDivElement>(); - @observable private _trackbox = React.createRef<HTMLDivElement>(); - @observable private _titleContainer = React.createRef<HTMLDivElement>(); - @observable private _timelineContainer = React.createRef<HTMLDivElement>(); - - @observable private _timelineWrapper = React.createRef<HTMLDivElement>(); - @observable private _infoContainer = React.createRef<HTMLDivElement>(); - - - @observable private _currentBarX: number = 0; - @observable private _windSpeed: number = 1; - @observable private _isPlaying: boolean = false; //scrubber playing - @observable private _isFrozen: boolean = false; //timeline freeze - @observable private _boxLength: number = 0; - @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; - @observable private _time = 100000; //DEFAULT - @observable private _ticks: number[] = []; - @observable private _playButton = faPlayCircle; - @observable private flyoutInfo: FlyoutProps = { x: 0, y: 0, display: "none", regiondata: new Doc(), regions: new List<Doc>() }; - - @computed - private get children(): List<Doc> { - let extendedDocument = ["image", "video", "pdf"].includes(StrCast(this.props.Document.type)); - - if (extendedDocument) { - if (this.props.Document.data_ext) { - return Cast((Cast(this.props.Document.data_ext, Doc) as Doc).annotations, listSpec(Doc)) as List<Doc>; - } else { - return new List<Doc>(); - } - } - return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List<Doc>; - } - - @computed - private get inks(){ - if (this.props.Document.data_ext){ - let data_ext = Cast(this.props.Document.data_ext, Doc) as Doc; - let ink = Cast(data_ext.ink, InkField) as InkField; - if (ink){ - return ink.inkData; - } - } - } - - - componentDidMount() { - if (StrCast(this.props.Document.type) === "video") { - console.log("ran"); - console.log(this.props.Document.duration); - if (this.props.Document.duration) { - this._time = Math.round(NumCast(this.props.Document.duration)) * 1000; - - reaction(() => { - return NumCast(this.props.Document.curPage); - }, curPage => { - this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); - }); - - } - - } - runInAction(() => { - - reaction(() => { - return this._time; - }, () => { - this._ticks = []; - for (let i = 0; i < this._time;) { - this._ticks.push(i); - i += this._tickIncrement; - } - let trackbox = this._trackbox.current!; - this._boxLength = this._tickIncrement / 1000 * this._tickSpacing * this._ticks.length; - trackbox.style.width = `${this._boxLength}`; - this._scrubberbox.current!.style.width = `${this._boxLength}`; - }, { fireImmediately: true }); - }); - } - - @action - changeCurrentBarX = (x: number) => { - this._currentBarX = x; - } - - //for playing - @action - onPlay = async (e: React.MouseEvent) => { - if (this._isPlaying) { - this._isPlaying = false; - this._playButton = faPlayCircle; - } else { - this._isPlaying = true; - this._playButton = faPauseCircle; - this.changeCurrentX(); - } - } - - @action - changeCurrentX = () => { - if (this._currentBarX === this._boxLength && this._isPlaying) { - this._currentBarX = 0; - } - if (this._currentBarX <= this._boxLength && this._isPlaying) { - this._currentBarX = this._currentBarX + this._windSpeed; - setTimeout(this.changeCurrentX, 15); - } - } - - @action - windForward = (e: React.MouseEvent) => { - if (this._windSpeed < 64) { //max speed is 32 - this._windSpeed = this._windSpeed * 2; - } - } - - @action - windBackward = (e: React.MouseEvent) => { - if (this._windSpeed > 1 / 16) { // min speed is 1/8 - this._windSpeed = this._windSpeed / 2; - } - } - - //for scrubber action - @action - onScrubberDown = (e: React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.addEventListener("pointermove", this.onScrubberMove); - document.addEventListener("pointerup", () => { - document.removeEventListener("pointermove", this.onScrubberMove); - }); - } - - @action - onScrubberMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let scrubberbox = this._scrubberbox.current!; - let left = scrubberbox.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; - this._currentBarX = offsetX; - } - - @action - onScrubberClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - let scrubberbox = this._scrubberbox.current!; - let offset = (e.clientX - scrubberbox.getBoundingClientRect().left) * this.props.ScreenToLocalTransform().Scale; - this._currentBarX = offset; - } - - - - @action - onPanDown = (e: React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.addEventListener("pointermove", this.onPanMove); - document.addEventListener("pointerup", () => { - 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 - 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._timelineContainer.current!.getBoundingClientRect().bottom; - 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 - onTimelineDown = (e: React.PointerEvent) => { - e.preventDefault(); - if (e.nativeEvent.which === 1 && !this._isFrozen) { - document.addEventListener("pointermove", this.onTimelineMove); - document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onTimelineMove); }); - } - } - - @action - onTimelineMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let timelineContainer = this._timelineWrapper.current!; - let left = parseFloat(timelineContainer.style.left!); - let top = parseFloat(timelineContainer.style.top!); - timelineContainer.style.left = `${left + e.movementX}px`; - timelineContainer.style.top = `${top + e.movementY}px`; - } - - @action - minimize = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - let timelineContainer = this._timelineContainer.current!; - if (this._isMinimized) { - this._isMinimized = false; - timelineContainer.style.visibility = "visible"; - } else { - this._isMinimized = true; - timelineContainer.style.visibility = "hidden"; - } - } - - @action - 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; - } - return `${min}:${sec}`; - } - - - private _freezeText = "Freeze Timeline"; - - timelineContextMenu = (e: React.MouseEvent): void => { - let subitems: ContextMenuProps[] = []; - let timelineContainer = this._timelineWrapper.current!; - subitems.push({ - description: "Pin to Top", event: action(() => { - if (!this._isFrozen) { - timelineContainer.style.transition = "top 1000ms ease-in, left 1000ms ease-in"; //????? - timelineContainer.style.left = "0px"; - timelineContainer.style.top = "0px"; - timelineContainer.style.transition = "none"; - } - }), icon: faArrowUp - }); - subitems.push({ - description: "Pin to Bottom", event: action(() => { - console.log(this.props.Document.y); - - if (!this._isFrozen) { - timelineContainer.style.transform = `translate(0px, ${e.pageY - this._containerHeight}px)`; - } - }), icon: faArrowDown - }); - subitems.push({ - description: this._freezeText, event: action(() => { - if (this._isFrozen) { - this._isFrozen = false; - this._freezeText = "Freeze Timeline"; - } else { - this._isFrozen = true; - this._freezeText = "Unfreeze Timeline"; - } - }), icon: "thumbtack" - }); - ContextMenu.Instance.addItem({ description: "Timeline Funcs...", subitems: subitems, icon: faClock }); - } - - - - @action - getFlyout = (props: FlyoutProps) => { - for (const [k, v] of Object.entries(props)) { - (this.flyoutInfo as any)[k] = v; - } - console.log(this.flyoutInfo); - } - - render() { - return ( - <div style={{ left: "0px", top: "0px", position: "absolute", width: "100%", transform: "translate(0px, 0px)" }} ref={this._timelineWrapper}> - <button className="minimize" onClick={this.minimize}>Minimize</button> - <div className="timeline-container" style={{ height: `${this._containerHeight}px`, left: "0px", top: "30px" }} ref={this._timelineContainer} onPointerDown={this.onTimelineDown} onContextMenu={this.timelineContextMenu}> - <TimelineFlyout flyoutInfo={this.flyoutInfo} tickSpacing={this._tickSpacing}/> - <div className="toolbox"> - <div onClick={this.windBackward}> <FontAwesomeIcon icon={faBackward} size="2x" /> </div> - <div onClick={this.onPlay}> <FontAwesomeIcon icon={this._playButton} size="2x" /> </div> - <div onClick={this.windForward}> <FontAwesomeIcon icon={faForward} size="2x" /> </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> - <div className="scrubber" ref={this._scrubber} onPointerDown={this.onScrubberDown} style={{ transform: `translate(${this._currentBarX}px)` }}> - <div className="scrubberhead"></div> - </div> - <div className="trackbox" ref={this._trackbox} onPointerDown={this.onPanDown}> - {DocListCast(this.children).map(doc => <Track node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} setFlyout={this.getFlyout} transform={this.props.ScreenToLocalTransform()} collection = {this.props.Document}/>)} - </div> - </div> - <div className="title-container" ref={this._titleContainer}> - {DocListCast(this.children).map(doc => <div className="datapane"><p>{doc.title}</p></div>)} - </div> - <div onPointerDown={this.onResizeDown}> - <FontAwesomeIcon className="resize" icon={faGripLines} /> - </div> - </div> - </div> - ); - } - -} - - -interface TimelineFlyoutProps { - flyoutInfo: FlyoutProps; - tickSpacing: number; - -} - -interface TimelineOverviewProps { - currentBarX: number; -} - -class TimelineOverview extends React.Component<TimelineOverviewProps>{ - - componentWillMount() { - - } - - render() { - return ( - <div className="overview"> - <div className="container"> - <div className="scrubber"> - <div className="scrubberhead"></div> - </div> - </div> - </div> - ); - } -} - -class TimelineFlyout extends React.Component<TimelineFlyoutProps>{ - - @observable private _timeInput = React.createRef<HTMLInputElement>(); - @observable private _durationInput = React.createRef<HTMLInputElement>(); - @observable private _fadeInInput = React.createRef<HTMLInputElement>(); - @observable private _fadeOutInput = React.createRef<HTMLInputElement>(); - @observable private _data: FlyoutProps = { x: 0, y: 0, display: "none", regiondata: new Doc(), regions: new List<Doc>() }; - - private block = false; - - componentDidMount() { - - document.addEventListener("pointerdown", this.closeFlyout); - } - - - componentWillUnmount() { - document.removeEventListener("pointerdown", this.closeFlyout); - } - - - @action - changeTime = (e: React.KeyboardEvent) => { - let time = this._timeInput.current!; - if (e.keyCode === 13) { - if (!Number.isNaN(Number(time.value))) { - this.props.flyoutInfo.regiondata!.position = Number(time.value) / 1000 * this.props.tickSpacing; - time.placeholder = time.value + "ms"; - time.value = ""; - } - } - } - @action - onFlyoutDown = (e: React.PointerEvent) => { - this._data.display = "block"; - this.block = true; - } - - @action - closeFlyout = (e: PointerEvent) => { - if (this.block) { - this.block = false; - return; - } - this._data.display = "none"; - } - - @action - changeDuration = (e: React.KeyboardEvent) => { - let duration = this._durationInput.current!; - if (e.keyCode === 13) { - if (!Number.isNaN(Number(duration.value))) { - this.props.flyoutInfo.regiondata!.duration = Number(duration.value) / 1000 * this.props.tickSpacing; - duration.placeholder = duration.value + "ms"; - duration.value = ""; - } - } - } - - @action - changeFadeIn = (e: React.KeyboardEvent) => { - let fadeIn = this._fadeInInput.current!; - if (e.keyCode === 13) { - if (!Number.isNaN(Number(fadeIn.value))) { - this.props.flyoutInfo.regiondata!.fadeIn = Number(fadeIn.value); - fadeIn.placeholder = fadeIn.value + "ms"; - fadeIn.value = ""; - } - } - } - - @action - changeFadeOut = (e: React.KeyboardEvent) => { - let fadeOut = this._fadeOutInput.current!; - if (e.keyCode === 13) { - if (!Number.isNaN(Number(fadeOut.value))) { - this.props.flyoutInfo.regiondata!.fadeOut = Number(fadeOut.value); - fadeOut.placeholder = fadeOut.value + "ms"; - fadeOut.value = ""; - } - } - } - - render() { - return ( - <div> - <div className="flyout-container" style={{ left: `${this._data.x}px`, top: `${this._data.y}px`, display: `${this._data.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 ref={this._timeInput} type="text" placeholder={`${Math.round(NumCast(this.props.flyoutInfo.regiondata!.position) / this.props.tickSpacing * 1000)}ms`} onKeyDown={this.changeTime} /> - <input ref={this._durationInput} type="text" placeholder={`${Math.round(NumCast(this.props.flyoutInfo.regiondata!.duration) / this.props.tickSpacing * 1000)}ms`} onKeyDown={this.changeDuration} /> - <input ref={this._fadeInInput} type="text" placeholder={`${Math.round(NumCast(this.props.flyoutInfo.regiondata!.fadeIn))}ms`} onKeyDown={this.changeFadeIn} /> - <input ref={this._fadeOutInput} type="text" placeholder={`${Math.round(NumCast(this.props.flyoutInfo.regiondata!.fadeOut))}ms`} onKeyDown={this.changeFadeOut} /> - </div> - <button onClick={action((e: React.MouseEvent) => { this.props.flyoutInfo.regions!.splice(this.props.flyoutInfo.regions!.indexOf(this.props.flyoutInfo.regiondata!), 1); this.props.flyoutInfo.display = "none"; })}>delete</button> - </div> - </div> - ); - } -} - -class TimelineZoom extends React.Component { - componentDidMount() { - - } - render() { - return ( - <div> - - </div> - ); - } -}
\ No newline at end of file diff --git a/src/client/views/nodes/Track.scss b/src/client/views/nodes/Track.scss deleted file mode 100644 index c8d56edf6..000000000 --- a/src/client/views/nodes/Track.scss +++ /dev/null @@ -1,15 +0,0 @@ -@import "./../globalCssVariables.scss"; - -.track-container{ - - .track { - .inner { - top:0px; - height: 75px; - width: calc(100%); - background-color: $light-color; - border: 1px solid $dark-color; - position:relative; - } - } -}
\ No newline at end of file diff --git a/src/client/views/nodes/Track.tsx b/src/client/views/nodes/Track.tsx deleted file mode 100644 index b12dabadf..000000000 --- a/src/client/views/nodes/Track.tsx +++ /dev/null @@ -1,266 +0,0 @@ -import * as React from "react"; -import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, observe, IObservableArray, computed, toJS, IObservableObject, runInAction, autorun } from "mobx"; -import "./Track.scss"; -import { Doc, DocListCastAsync, DocListCast, Field } from "../../../new_fields/Doc"; -import { listSpec } from "../../../new_fields/Schema"; -import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; -import { List } from "../../../new_fields/List"; -import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; -import { FlyoutProps } from "./Timeline"; -import { Transform } from "../../util/Transform"; -import { AddComparisonParameters } from "../../northstar/model/idea/idea"; -import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells"; - -interface IProps { - node: Doc; - currentBarX: number; - transform: Transform; - collection: Doc; - changeCurrentBarX: (x: number) => void; - setFlyout: (props: FlyoutProps) => any; -} - -@observer -export class Track extends React.Component<IProps> { - @observable private _inner = React.createRef<HTMLDivElement>(); - @observable private _reactionDisposers: IReactionDisposer[] = []; - @observable private _keyReaction: any; //reaction that is used to dispose when necessary - @observable private _currentBarXReaction: any; - - @computed - private get regions() { - return Cast(this.props.node.regions, listSpec(Doc)) as List<Doc>; - } - - componentWillMount() { - if (!this.props.node.regions) { - this.props.node.regions = new List<Doc>(); - } - this.props.node.opacity = 1; - } - - componentDidMount() { - runInAction(() => { - this._currentBarXReaction = this.currentBarXReaction(); - if (this.regions.length === 0) this.createRegion(this.props.currentBarX); - this.props.node.hidden = false; - }); - } - - componentWillUnmount() { - runInAction(() => { - if (this._keyReaction) this._keyReaction(); - if (this._currentBarXReaction) this._currentBarXReaction(); - }); - } - - @action - keyReaction = () => { - return reaction( () => { - return Doc.allKeys(this.props.node).map(key => FieldValue(this.props.node[key])); - }, async () => { - let regiondata: (Doc | undefined) = await this.findRegion(this.props.currentBarX) ; - if (regiondata) { - let keyframes = await DocListCastAsync((regiondata as Doc).keyframes!); - keyframes!.forEach( async (kf) => { - if (kf.type === KeyframeFunc.KeyframeType.default && kf.time === this.props.currentBarX) { - console.log("full keychange triggered"); - //for this specific keyframe - kf.key = Doc.MakeCopy(this.props.node, true); - - //for fades - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, this.props.currentBarX, kf); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, this.props.currentBarX, kf); //right keyframe, if it exists - if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades - let edge:(Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, this.props.currentBarX, leftkf!); - edge!.key = Doc.MakeCopy(kf.key as Doc, true); - leftkf!.key = Doc.MakeCopy(kf.key as Doc, true); - (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; - (Cast(leftkf!.key, Doc)! as Doc).opacity = 1; - } - if (rightkf!.type === KeyframeFunc.KeyframeType.fade) { - let edge:(Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!,this.props.currentBarX, rightkf!); - edge!.key = Doc.MakeCopy(kf.key as Doc, true); - rightkf!.key = Doc.MakeCopy(kf.key as Doc, true); - (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; - (Cast(rightkf!.key, Doc)! as Doc).opacity = 1; - } - } - }); - } - }, {fireImmediately: true}); - } - - @action - currentBarXReaction = () => { - return reaction(() => this.props.currentBarX, async () => { - if (this._keyReaction) this._keyReaction(); //dispose previous reaction first - let regiondata: (Doc | undefined) = await this.findRegion(this.props.currentBarX); - if (regiondata) { - this.props.node.hidden = false; - await this.timeChange(this.props.currentBarX); - } else { - this.props.node.hidden = true; - } - }, { fireImmediately: true }); - } - - - @action - timeChange = async (time: number) => { - let regiondata = await this.findRegion(Math.round(time)); //finds a region that the scrubber is on - if (regiondata) { - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.props.currentBarX); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.props.currentBarX); //right keyframe, if it exists - let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe - if (currentkf) { - await this.applyKeys(currentkf); - this._keyReaction = this.keyReaction(); //reactivates reaction. - } else if (leftkf && rightkf) { - await this.interpolate(leftkf, rightkf, regiondata); - } - } - } - - @action - private applyKeys = async (kf: Doc) => { - let kfNode = await Cast(kf.key, Doc) as Doc; - let docFromApply = kfNode; - if (this.filterKeys(Doc.allKeys(this.props.node)).length > this.filterKeys(Doc.allKeys(kfNode)).length) docFromApply = this.props.node; - this.filterKeys(Doc.allKeys(docFromApply)).forEach(key => { - if (!kfNode[key]) { - this.props.node[key] = undefined; - } else { - this.props.node[key] = kfNode[key]; - } - }); - } - - - @action - private filterKeys = (keys: string[]): string[] => { - return keys.reduce((acc: string[], key: string) => { - if (key !== "regions" && key !== "data" && key !== "creationDate" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns") acc.push(key); - return acc; - }, []) as string[]; - } - - @action - calcCurrent = async (region: Doc) => { - let currentkf: (Doc | undefined) = undefined; - let keyframes = await DocListCastAsync(region.keyframes!); - keyframes!.forEach((kf) => { - if (NumCast(kf.time) === Math.round(this.props.currentBarX)) currentkf = kf; - }); - return currentkf; - } - - @action - interpolate = async (left: Doc, right: Doc, regiondata:Doc) => { - console.log("interpolating"); - let leftNode = left.key as Doc; - let rightNode = right.key as Doc; - const dif_time = NumCast(right.time) - NumCast(left.time); - const timeratio = (this.props.currentBarX - NumCast(left.time)) / dif_time; //linear - let keyframes = (await DocListCastAsync(regiondata.keyframes!))!; - let indexLeft = keyframes.indexOf(left); - let interY:List<number> = await ((regiondata.functions as List<Doc>)[indexLeft] as Doc).interpolationY as List<number>; - let realIndex = (interY.length - 1) * timeratio; - let xIndex = Math.floor(realIndex); - let yValue = interY[xIndex]; - let secondYOffset:number = yValue; - let minY = interY[0]; // for now - let maxY = interY[interY.length - 1]; //for now - if (interY.length !== 1) { - secondYOffset = interY[xIndex] + ((realIndex - xIndex) / 1) * (interY[xIndex + 1] - interY[xIndex]) - minY; - } - let finalRatio = secondYOffset / (maxY - minY); - let pathX:List<number> = await ((regiondata.functions as List<Doc>)[indexLeft] as Doc).pathX as List<number>; - let pathY:List<number> = await ((regiondata.functions as List<Doc>)[indexLeft] as Doc).pathY as List<number>; - let proposedX = 0; - let proposedY = 0; - if (pathX.length !== 0) { - let realPathCorrespondingIndex = finalRatio * (pathX.length - 1); - let pathCorrespondingIndex = Math.floor(realPathCorrespondingIndex); - if (pathCorrespondingIndex >= pathX.length - 1) { - proposedX = pathX[pathX.length - 1]; - proposedY = pathY[pathY.length - 1]; - } else if (pathCorrespondingIndex < 0){ - proposedX = pathX[0]; - proposedY = pathY[0]; - } else { - proposedX = pathX[pathCorrespondingIndex] + ((realPathCorrespondingIndex - pathCorrespondingIndex) / 1) * (pathX[pathCorrespondingIndex + 1] - pathX[pathCorrespondingIndex]); - proposedY = pathY[pathCorrespondingIndex] + ((realPathCorrespondingIndex - pathCorrespondingIndex) / 1) * (pathY[pathCorrespondingIndex + 1] - pathY[pathCorrespondingIndex]); - } - - } - - - this.filterKeys(Doc.allKeys(leftNode)).forEach(key => { - if (leftNode[key] && rightNode[key] && typeof (leftNode[key]) === "number" && typeof (rightNode[key]) === "number") { //if it is number, interpolate - if ((key === "x" || key === "y") && pathX.length !== 0){ - if (key === "x") this.props.node[key] = proposedX; - if (key === "y") this.props.node[key] = proposedY; - console.log(pathX.length); - - } else { - const diff = NumCast(rightNode[key]) - NumCast(leftNode[key]); - const adjusted = diff * finalRatio; - this.props.node[key] = NumCast(leftNode[key]) + adjusted; - } - } else { - this.props.node[key] = leftNode[key]; - } - }); - } - - @action - findRegion = async (time: number) => { - let foundRegion:(Doc | undefined) = undefined; - let regions = await DocListCastAsync(this.regions); - regions!.forEach(region => { - region = region as RegionData; - if (time >= NumCast(region.position) && time <= (NumCast(region.position) + NumCast(region.duration))) { - foundRegion = region; - } - }); - return foundRegion; - } - - @action - onInnerDoubleClick = (e: React.MouseEvent) => { - let inner = this._inner.current!; - let offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); - this.createRegion(offsetX); - } - - createRegion = (position: number) => { - let regiondata = KeyframeFunc.defaultKeyframe(); - regiondata.position = position; - let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, regiondata, this.regions); - let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); - if ((rightRegion && leftRegion && rightRegion.position - (leftRegion.position + leftRegion.duration) < NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut)) || (rightRegion && rightRegion.position - regiondata.position < NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { - return; - } else if (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut)) { - regiondata.duration = rightRegion.position - regiondata.position; - } - this.regions.push(regiondata); - return regiondata; - } - - - render() { - return ( - <div className="track-container"> - <div className="track"> - <div className="inner" ref={this._inner} onDoubleClick={this.onInnerDoubleClick}> - {DocListCast(this.regions).map((region) => { - return <Keyframe node={this.props.node} RegionData={region} changeCurrentBarX={this.props.changeCurrentBarX} setFlyout={this.props.setFlyout} transform={this.props.transform} collection={this.props.collection}/>; - })} - </div> - </div> - </div> - ); - } -}
\ No newline at end of file |
