From bf3d45f8a16d23384a308f65adfa9a2baee495af Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Thu, 1 Aug 2019 17:00:10 -0400 Subject: prosemirror --- src/client/views/animationtimeline/Keyframe.scss | 94 ++++ src/client/views/animationtimeline/Keyframe.tsx | 551 +++++++++++++++++++++ src/client/views/animationtimeline/Timeline.scss | 170 +++++++ src/client/views/animationtimeline/Timeline.tsx | 530 ++++++++++++++++++++ .../views/animationtimeline/TimelineMenu.scss | 0 .../views/animationtimeline/TimelineMenu.tsx | 47 ++ src/client/views/animationtimeline/Track.scss | 15 + src/client/views/animationtimeline/Track.tsx | 276 +++++++++++ 8 files changed, 1683 insertions(+) create mode 100644 src/client/views/animationtimeline/Keyframe.scss create mode 100644 src/client/views/animationtimeline/Keyframe.tsx create mode 100644 src/client/views/animationtimeline/Timeline.scss create mode 100644 src/client/views/animationtimeline/Timeline.tsx create mode 100644 src/client/views/animationtimeline/TimelineMenu.scss create mode 100644 src/client/views/animationtimeline/TimelineMenu.tsx create mode 100644 src/client/views/animationtimeline/Track.scss create mode 100644 src/client/views/animationtimeline/Track.tsx (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.scss b/src/client/views/animationtimeline/Keyframe.scss new file mode 100644 index 000000000..b1e8b0b65 --- /dev/null +++ b/src/client/views/animationtimeline/Keyframe.scss @@ -0,0 +1,94 @@ +@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/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx new file mode 100644 index 000000000..995a5b402 --- /dev/null +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -0,0 +1,551 @@ +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): (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(); + 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 { + + @observable private _bar = React.createRef(); + @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; + } + + @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(); + } + 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([0, 1]); + interpolationFunctions.interpolationY = new List([0,100]); + interpolationFunctions.pathX = new List(); + interpolationFunctions.pathY = new List(); + + 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
; + } else if (type === "right") { + return
; + } + return
; + } + + @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 ( +
+ {this.createDivider()} +
{ this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + }}>
+
); + } + return ( +
+ {this.createDivider()} +
+ ); + } + + onContainerOver = (e: React.PointerEvent, ref: React.RefObject) => { + e.preventDefault(); + e.stopPropagation(); + let div = ref.current!; + div.style.opacity = "1"; + } + + onContainerOut = (e: React.PointerEvent, ref: React.RefObject) => { + 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(); + let yPlots = new List(); + 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 ( +
+
{ + e.preventDefault(); + e.stopPropagation(); + console.log("has been clicked!"); + 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 }); + })}> +
+
+ {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(); + return ( +
{ this.onContainerOver(e, bodyRef); }} + onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} + onContextMenu={(e) => { this.onContainerDown(e, kf); }}> +
+ ); + } + })} + +
+
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss new file mode 100644 index 000000000..47f448adb --- /dev/null +++ b/src/client/views/animationtimeline/Timeline.scss @@ -0,0 +1,170 @@ +@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/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx new file mode 100644 index 000000000..d2714592e --- /dev/null +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -0,0 +1,530 @@ +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 "../nodes/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; +} + + +@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(); + @observable private _scrubber = React.createRef(); + @observable private _trackbox = React.createRef(); + @observable private _titleContainer = React.createRef(); + @observable private _timelineContainer = React.createRef(); + + @observable private _timelineWrapper = React.createRef(); + @observable private _infoContainer = React.createRef(); + + + @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() }; + + @computed + private get children(): List { + 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; + } else { + return new List(); + } + } + return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; + } + + @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 ( +
+ +
+ +
+
+
+
+
+
+
+ {this._ticks.map(element => { + return

{this.toTime(element)}

; + })} +
+
+
+
+
+ {DocListCast(this.children).map(doc => )} +
+
+
+ {DocListCast(this.children).map(doc =>

{doc.title}

)} +
+
+ +
+
+
+ ); + } + +} + + +interface TimelineFlyoutProps { + flyoutInfo: FlyoutProps; + tickSpacing: number; + +} + +interface TimelineOverviewProps { + currentBarX: number; +} + +class TimelineOverview extends React.Component{ + + componentWillMount() { + + } + + render() { + return ( +
+
+
+
+
+
+
+ ); + } +} + +class TimelineFlyout extends React.Component{ + + @observable private _timeInput = React.createRef(); + @observable private _durationInput = React.createRef(); + @observable private _fadeInInput = React.createRef(); + @observable private _fadeOutInput = React.createRef(); + @observable private _data: FlyoutProps = { x: 0, y: 0, display: "none", regiondata: new Doc(), regions: new List() }; + + 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 ( +
+
+ +
+

Time:

+

Duration:

+

Fade-in

+

Fade-out

+
+
+ + + + +
+ +
+
+ ); + } +} + +class TimelineZoom extends React.Component { + componentDidMount() { + + } + render() { + return ( +
+ +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/animationtimeline/TimelineMenu.scss b/src/client/views/animationtimeline/TimelineMenu.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx new file mode 100644 index 000000000..e82075f6a --- /dev/null +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -0,0 +1,47 @@ + +import * as React from "react"; + + +/** + * TimelineMenu: + * + * + * Timeline: + * - + * + * + * Keyframe: + * - Delete keyframe + * - Move keyframe + * - Edit keyframe (shows schema) + * + * + * Region: + * - Add Keyframe + * - Copy Interpolation + * - Copy path + * - Add Interpolation + * - Add Path + * - Change fades + * - position region + * - duration region + * - + */ +export class TimelineMenu extends React.Component { + public static Instance:TimelineMenu; + + constructor (props:Readonly<{}>){ + super(props); + TimelineMenu.Instance = this; + } + + + + + render() { + return ( +
+ ); + } + +} \ No newline at end of file diff --git a/src/client/views/animationtimeline/Track.scss b/src/client/views/animationtimeline/Track.scss new file mode 100644 index 000000000..c8d56edf6 --- /dev/null +++ b/src/client/views/animationtimeline/Track.scss @@ -0,0 +1,15 @@ +@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/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx new file mode 100644 index 000000000..4f78f86b8 --- /dev/null +++ b/src/client/views/animationtimeline/Track.tsx @@ -0,0 +1,276 @@ +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"; +import { DocumentManager } from "../../util/DocumentManager"; +import { DocumentView } from "../nodes/DocumentView"; +import { RichTextField } from "../../../new_fields/RichTextField"; + +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 { + @observable private _inner = React.createRef(); + @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; + } + + componentWillMount() { + if (!this.props.node.regions) { + this.props.node.regions = new List(); + } + 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 () => { + console.log("rAN"); + 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 (key === "type") { + if (this.props.node[key] === "text") { + this.props.node.dataDocTest = new RichTextField(StrCast(kfNode.stateData)); + console.log("updated"); + } + } + 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 = await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List; + 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 = await ((regiondata.functions as List)[indexLeft] as Doc).pathX as List; + let pathY:List = await ((regiondata.functions as List)[indexLeft] as Doc).pathY as List; + 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 ( +
+
+
+ {DocListCast(this.regions).map((region) => { + return ; + })} +
+
+
+ ); + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 62a34043949f051533ca549be48b774696331d43 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Mon, 5 Aug 2019 17:37:43 -0400 Subject: richtext + buttons --- src/client/views/MainView.tsx | 1 + src/client/views/animationtimeline/Timeline.tsx | 3 + .../views/animationtimeline/TimelineMenu.tsx | 12 +- src/client/views/animationtimeline/Track.tsx | 124 +++++++++++---------- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/FormattedTextBox.tsx | 2 - src/new_fields/RichTextField.ts | 1 + 7 files changed, 85 insertions(+), 62 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f5a6715e5..669b8f018 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -400,6 +400,7 @@ export class MainView extends React.Component {
  • +
  • {btns.map(btn => diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index d2714592e..7a6d9fa52 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -34,6 +34,9 @@ export interface FlyoutProps { @observer export class Timeline extends CollectionSubView(Document) { + static Instance:Timeline; + + private readonly DEFAULT_CONTAINER_HEIGHT: number = 300; private readonly DEFAULT_TICK_SPACING: number = 50; private readonly MIN_CONTAINER_HEIGHT: number = 205; diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index e82075f6a..7768f51df 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -1,6 +1,6 @@ - import * as React from "react"; - +import {observable, action, runInAction} from "mobx"; +import {observer} from "mobx-react"; /** * TimelineMenu: @@ -27,9 +27,17 @@ import * as React from "react"; * - duration region * - */ + +@observer export class TimelineMenu extends React.Component { public static Instance:TimelineMenu; + @observable private _opacity = 1; + @observable private _x = 0; + @observable private _y = 0; + @observable private _type: "timeline" | "keyframe" | "region" | "" = ""; + + constructor (props:Readonly<{}>){ super(props); TimelineMenu.Instance = this; diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 4f78f86b8..fc2cacba8 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -9,10 +9,6 @@ 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"; -import { DocumentManager } from "../../util/DocumentManager"; -import { DocumentView } from "../nodes/DocumentView"; import { RichTextField } from "../../../new_fields/RichTextField"; interface IProps { @@ -28,8 +24,11 @@ interface IProps { export class Track extends React.Component { @observable private _inner = React.createRef(); @observable private _reactionDisposers: IReactionDisposer[] = []; - @observable private _keyReaction: any; //reaction that is used to dispose when necessary - @observable private _currentBarXReaction: any; + @observable private _currentBarXReaction: any; + @observable private _isOnKeyframe: boolean = false; + @observable private _onKeyframe: (Doc | undefined) = undefined; + @observable private _onRegionData : ( Doc | undefined) = undefined; + @observable private _leftCurrKeyframe: (Doc | undefined) = undefined; @computed private get regions() { @@ -53,53 +52,44 @@ export class Track extends React.Component { 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 () => { - console.log("rAN"); - 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; - } - } - }); + saveKeyframe = async (ref:Doc, regiondata:Doc) => { + let keyframes:List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); + let kfIndex:number = keyframes.indexOf(ref); + let kf = keyframes[kfIndex] as Doc; + if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for fades + console.log("full keychange triggered"); + kf.key = Doc.MakeCopy(this.props.node, true); + 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; } - }, {fireImmediately: true}); + 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; + } + } + keyframes[kfIndex] = kf; + this._onKeyframe = undefined; + this._onRegionData = undefined; + this._isOnKeyframe = false; } - + @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; @@ -113,6 +103,10 @@ export class Track extends React.Component { @action timeChange = async (time: number) => { + if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { + console.log("saving"); + await this.saveKeyframe(this._onKeyframe, this._onRegionData); + } 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 @@ -120,7 +114,10 @@ export class Track extends React.Component { 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. + this._leftCurrKeyframe = currentkf; + this._isOnKeyframe = true; + this._onKeyframe = currentkf; + this._onRegionData = regiondata; } else if (leftkf && rightkf) { await this.interpolate(leftkf, rightkf, regiondata); } @@ -133,17 +130,21 @@ export class Track extends React.Component { 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 (key === "type") { - if (this.props.node[key] === "text") { - this.props.node.dataDocTest = new RichTextField(StrCast(kfNode.stateData)); - console.log("updated"); - } - } if (!kfNode[key]) { this.props.node[key] = undefined; } else { - this.props.node[key] = kfNode[key]; + if (key === "data") { + if (this.props.node.type === "text"){ + let nodeData = (kfNode[key] as RichTextField).Data; + this.props.node[key] = new RichTextField(nodeData); + } + } else if (key === "creationDate") { + } else { + this.props.node[key] = kfNode[key]; + } + } + }); } @@ -151,7 +152,7 @@ export class Track extends React.Component { @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); + if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns") acc.push(key); return acc; }, []) as string[]; } @@ -205,22 +206,31 @@ export class Track extends React.Component { } } - - 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]; + if (key === "data") { + if (this.props.node.type === "text"){ + let nodeData = StrCast((leftNode[key] as RichTextField).Data); + let currentNodeData = StrCast((this.props.node[key] as RichTextField).Data); + if (nodeData !== currentNodeData) { + this.props.node[key] = new RichTextField(nodeData); + } + } + + } else if (key === "creationDate") { + + } else { + this.props.node[key] = leftNode[key]; + } } }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 676a49288..71329f166 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -573,7 +573,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300, title: "Layout Script" }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); }; } - + private _timeline = ; + se = () => { + } render() { const easing = () => this.props.Document.panTransformType === "Ease"; Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 9b37a6491..03fa7f0cd 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -166,8 +166,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe 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.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/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts index 78a3a4067..0095eb31f 100644 --- a/src/new_fields/RichTextField.ts +++ b/src/new_fields/RichTextField.ts @@ -15,6 +15,7 @@ export class RichTextField extends ObjectField { this.Data = data; } + [Copy]() { return new RichTextField(this.Data); } -- cgit v1.2.3-70-g09d2 From 2c86a6958186c020ce7fbe99555f07ffe9f9f821 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 6 Aug 2019 12:25:09 -0400 Subject: timeline contextmenu --- src/client/views/MainView.tsx | 5 ++ src/client/views/animationtimeline/Keyframe.tsx | 4 +- .../views/animationtimeline/TimelineMenu.scss | 7 ++ .../views/animationtimeline/TimelineMenu.tsx | 79 +++++++++++++++++++++- 4 files changed, 92 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 669b8f018..589fcc409 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,6 +39,7 @@ import { FilterBox } from './search/FilterBox'; import { CollectionTreeView } from './collections/CollectionTreeView'; import { ClientUtils } from '../util/ClientUtils'; import { SchemaHeaderField, RandomPastel } from '../../new_fields/SchemaHeaderField'; +import { TimelineMenu } from './animationtimeline/TimelineMenu'; @observer export class MainView extends React.Component { @@ -148,6 +149,9 @@ export class MainView extends React.Component { const targets = document.elementsFromPoint(e.x, e.y); if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { ContextMenu.Instance.closeMenu(); + } + if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-container") === -1){ + TimelineMenu.Instance.closeMenu(); } }), true); } @@ -461,6 +465,7 @@ export class MainView extends React.Component { {this.nodesMenu()} {this.miscButtons} + diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 995a5b402..9dae3896f 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -12,6 +12,7 @@ import { FlyoutProps } from "./Timeline"; import { Transform } from "../../util/Transform"; import { InkField, StrokeData } from "../../../new_fields/InkField"; import { number } from "prop-types"; +import { TimelineMenu } from "./TimelineMenu"; export namespace KeyframeFunc { export enum KeyframeType { @@ -389,6 +390,7 @@ export class Keyframe extends React.Component {
    { this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); + TimelineMenu.Instance.openMenu("keyframe", e.clientX, e.clientY); }}>
    ); } @@ -538,7 +540,7 @@ export class Keyframe extends React.Component {
    { this.onContainerOver(e, bodyRef); }} onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} - onContextMenu={(e) => { this.onContainerDown(e, kf); }}> + onContextMenu={(e) => {TimelineMenu.Instance.openMenu("region", e.clientX, e.clientY);}}>
    ); } diff --git a/src/client/views/animationtimeline/TimelineMenu.scss b/src/client/views/animationtimeline/TimelineMenu.scss index e69de29bb..458c1eda1 100644 --- a/src/client/views/animationtimeline/TimelineMenu.scss +++ b/src/client/views/animationtimeline/TimelineMenu.scss @@ -0,0 +1,7 @@ +.timeline-menu-container{ + position: absolute; + z-index: 10000; + width: 500px; + height: 500px; + background-color: black; +} \ No newline at end of file diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 7768f51df..adbd21e62 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -1,6 +1,8 @@ import * as React from "react"; import {observable, action, runInAction} from "mobx"; import {observer} from "mobx-react"; +import "./TimelineMenu.scss"; +import { jSXAttribute } from "babel-types"; /** * TimelineMenu: @@ -32,7 +34,7 @@ import {observer} from "mobx-react"; export class TimelineMenu extends React.Component { public static Instance:TimelineMenu; - @observable private _opacity = 1; + @observable private _opacity = 0; @observable private _x = 0; @observable private _y = 0; @observable private _type: "timeline" | "keyframe" | "region" | "" = ""; @@ -42,13 +44,86 @@ export class TimelineMenu extends React.Component { super(props); TimelineMenu.Instance = this; } + @action + pointerDown = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.removeEventListener("pointerup", this.pointerUp); + document.addEventListener("pointerup", this.pointerUp); + document.removeEventListener("pointermove", this.pointerMove); + document.addEventListener("pointermove", this.pointerMove); + + + } + @action + pointerMove = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + } + @action + pointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.pointerMove); + document.removeEventListener("pointerup", this.pointerUp); + } + + @action + openMenu = (type: "timeline" | "keyframe" | "region", x?:number, y?:number) => { + this._type = type; + this._opacity = 1; + x ? this._x = x : this._x = 0; + y ? this._y = y : this._y = 0; + } + + @action + closeMenu = () => { + this._opacity = 0; + } + @action + addEase = (e: React.MouseEvent) => { + + } + @action + addPath = (e:React.MouseEvent) => { + + } render() { + let menu: (JSX.Element[] | undefined); + switch(this._type){ + case "keyframe": + menu = [ + , + , + + + ]; + break; + case "region" : + menu = [ + , + , + , + , + , + , + ]; + break; + case "timeline": + menu = [ + + ]; + break; + default: + break; + + } return ( -
    +
    + {menu} +
    ); } -- cgit v1.2.3-70-g09d2 From a4b5fa0273bb7bc872699f0f0ce047ec0fbc7d43 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 6 Aug 2019 16:02:35 -0400 Subject: debug and context menu --- src/client/views/MainView.tsx | 3 +++ src/client/views/animationtimeline/Keyframe.tsx | 4 ---- src/client/views/animationtimeline/TimelineMenu.scss | 13 +++++++++++-- src/client/views/animationtimeline/TimelineMenu.tsx | 18 +++++++++--------- src/client/views/animationtimeline/Track.tsx | 2 ++ src/new_fields/Doc.ts | 3 ++- 6 files changed, 27 insertions(+), 16 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index f38367d98..270e9b183 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -149,6 +149,9 @@ export class MainView extends React.Component { if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { ContextMenu.Instance.closeMenu(); } + if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-container") === -1) { + TimelineMenu.Instance.closeMenu(); + } }); globalPointerUp = () => this.isPointerDown = false; diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 9dae3896f..b27cb6d98 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -231,10 +231,6 @@ export class Keyframe extends React.Component { return; } }); - - let index = this.regiondata.keyframes!.indexOf(TK); - console.log(toJS(this.regiondata.keyframes!)); - return TK; } diff --git a/src/client/views/animationtimeline/TimelineMenu.scss b/src/client/views/animationtimeline/TimelineMenu.scss index 458c1eda1..ed047e52d 100644 --- a/src/client/views/animationtimeline/TimelineMenu.scss +++ b/src/client/views/animationtimeline/TimelineMenu.scss @@ -1,7 +1,16 @@ .timeline-menu-container{ position: absolute; z-index: 10000; - width: 500px; - height: 500px; + width: 150px; + height: auto; background-color: black; + .timeline-menu-button{ + width:100%; + height: 30px; + + } + .timeline-menu-input{ + width:100%; + height: 30px; + } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index adbd21e62..52f6f6535 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -95,20 +95,20 @@ export class TimelineMenu extends React.Component { switch(this._type){ case "keyframe": menu = [ - , - , - + , + , + ]; break; case "region" : menu = [ - , - , - , - , - , - , + , + , + , + , + , + , ]; break; case "timeline": diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index fc2cacba8..363ab4074 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -128,8 +128,10 @@ export class Track extends React.Component { private applyKeys = async (kf: Doc) => { let kfNode = await Cast(kf.key, Doc) as Doc; let docFromApply = kfNode; + console.log(Doc.allKeys(docFromApply)); 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 => { + console.log(key); if (!kfNode[key]) { this.props.node[key] = undefined; } else { diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index c01f4e8cf..b272bc1b9 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -425,12 +425,13 @@ export namespace Doc { export function MakeCopy(doc: Doc, copyProto: boolean = false): Doc { const copy = new Doc; Object.keys(doc).forEach(key => { - const field = ProxyField.WithoutProxy(() => doc[key]); if (key === "proto" && copyProto) { + const field = doc[key]; if (field instanceof Doc) { copy[key] = Doc.MakeCopy(field); } } else { + const field = ProxyField.WithoutProxy(() => doc[key]); if (field instanceof RefField) { copy[key] = field; } else if (field instanceof ObjectField) { -- cgit v1.2.3-70-g09d2 From 300aff743835dfd1c021609fd005819c8f2fd584 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Wed, 7 Aug 2019 14:56:48 -0400 Subject: working timelinemenu --- src/client/views/ContextMenu.scss | 1 + src/client/views/MainView.tsx | 6 +- src/client/views/animationtimeline/Keyframe.tsx | 73 +++++++++-------- .../views/animationtimeline/TimelineMenu.scss | 94 ++++++++++++++++++++-- .../views/animationtimeline/TimelineMenu.tsx | 85 ++++++++++++------- 5 files changed, 185 insertions(+), 74 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index e2c0de8af..6c619fbe1 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -7,6 +7,7 @@ box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw; flex-direction: column; background: whitesmoke; + padding-top: 10px; padding-bottom: 10px; border-radius: 15px; border: solid #BBBBBBBB 1px; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 270e9b183..3941c9c20 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -149,9 +149,9 @@ export class MainView extends React.Component { if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { ContextMenu.Instance.closeMenu(); } - if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-container") === -1) { - TimelineMenu.Instance.closeMenu(); - } + // if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-container") === -1) { + // TimelineMenu.Instance.closeMenu(); + // } }); globalPointerUp = () => this.isPointerDown = false; diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index b27cb6d98..d1975d847 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -386,7 +386,7 @@ export class Keyframe extends React.Component {
    { this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); - TimelineMenu.Instance.openMenu("keyframe", e.clientX, e.clientY); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); }}>
    ); } @@ -418,29 +418,21 @@ export class Keyframe extends React.Component { private _type: string = ""; @action - onContainerDown = (e: React.MouseEvent, kf: Doc) => { - e.preventDefault(); - e.stopPropagation(); + onContainerDown = (kf: Doc, type: string) => { 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; } - 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; - } - }); - } + }); + } @@ -508,20 +500,26 @@ export class Keyframe extends React.Component { } + + /** + * + * + * TEMPORARY + * let items = [ + TimelineMenu.Instance.addItem("button", "Show Data", () => {console.log(toJS(this.props.node));}), + TimelineMenu.Instance.addItem("button", "Delete", () => {}), + TimelineMenu.Instance.addItem("input", "Move", (val) => {console.log(val);}) + ]; + TimelineMenu.Instance.addMenu("Keyframe", items); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + * + */ render() { return (
    { - e.preventDefault(); - e.stopPropagation(); - console.log("has been clicked!"); - 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 }); - })}> + onDoubleClick={this.createKeyframe}>
    {this.regiondata.keyframes!.map(kf => { @@ -536,7 +534,18 @@ export class Keyframe extends React.Component {
    { this.onContainerOver(e, bodyRef); }} onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} - onContextMenu={(e) => {TimelineMenu.Instance.openMenu("region", e.clientX, e.clientY);}}> + onContextMenu={(e) => { + let items = [ + TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), + TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), + TimelineMenu.Instance.addItem("input", "fadeIn", (val) => {this.regiondata.fadeIn = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "fadeOut", (val) => {this.regiondata.fadeOut = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "position", (val) => {this.regiondata.position = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "duration", (val) => {this.regiondata.duration = parseInt(val, 10);}), + ]; + TimelineMenu.Instance.addMenu("Region", items); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + }}>
    ); } diff --git a/src/client/views/animationtimeline/TimelineMenu.scss b/src/client/views/animationtimeline/TimelineMenu.scss index ed047e52d..90cc53b4c 100644 --- a/src/client/views/animationtimeline/TimelineMenu.scss +++ b/src/client/views/animationtimeline/TimelineMenu.scss @@ -1,16 +1,94 @@ +@import "./../globalCssVariables.scss"; + + .timeline-menu-container{ position: absolute; + display: flex; + box-shadow: $intermediate-color 0.2vw 0.2vw 0.4vw; + flex-direction: column; + background: whitesmoke; z-index: 10000; width: 150px; - height: auto; - background-color: black; - .timeline-menu-button{ - width:100%; - height: 30px; + padding-bottom: 10px; + border-radius: 15px; + + border: solid #BBBBBBBB 1px; + + - } .timeline-menu-input{ - width:100%; - height: 30px; + font: $sans-serif; + font-size: 13px; + width:100%; + text-transform: uppercase; + letter-spacing: 2px; + margin-left: 10px; + background-color: transparent; + border-width: 0px; + transition: border-width 500ms; + } + + .timeline-menu-input:hover{ + border-width: 2px; + } + + + + + .timeline-menu-header{ + border-top-left-radius: 15px; + border-top-right-radius: 15px; + text-transform: uppercase; + background: $dark-color; + letter-spacing: 2px; + + .timeline-menu-header-desc{ + font:$sans-serif; + font-size: 13px; + text-align: center; + color: whitesmoke; + } + } + + + .timeline-menu-item { + // width: 11vw; //10vw + height: 30px; //2vh + background: whitesmoke; + display: flex; //comment out to allow search icon to be inline with search text + justify-content: left; + align-items: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; + border-style: none; + // padding: 10px 0px 10px 0px; + white-space: nowrap; + font-size: 13px; + color: grey; + letter-spacing: 2px; + text-transform: uppercase; + padding-right: 20px; + padding-left: 10px; + } + + .timeline-menu-item:hover { + border-width: .11px; + border-style: none; + border-color: $intermediate-color; + border-bottom-style: solid; + border-top-style: solid; + background: $darker-alt-accent; + } + + .timeline-menu-desc { + padding-left: 10px; + font:$sans-serif; + font-size: 13px; } + } \ No newline at end of file diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 52f6f6535..8b47448f2 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -3,6 +3,10 @@ import {observable, action, runInAction} from "mobx"; import {observer} from "mobx-react"; import "./TimelineMenu.scss"; import { jSXAttribute } from "babel-types"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons"; +import { AddComparisonResult } from "../../northstar/model/idea/idea"; + /** * TimelineMenu: @@ -38,8 +42,7 @@ export class TimelineMenu extends React.Component { @observable private _x = 0; @observable private _y = 0; @observable private _type: "timeline" | "keyframe" | "region" | "" = ""; - - + @observable private _currentMenu:JSX.Element[] = []; constructor (props:Readonly<{}>){ super(props); TimelineMenu.Instance = this; @@ -67,8 +70,7 @@ export class TimelineMenu extends React.Component { } @action - openMenu = (type: "timeline" | "keyframe" | "region", x?:number, y?:number) => { - this._type = type; + openMenu = (x?:number, y?:number) => { this._opacity = 1; x ? this._x = x : this._x = 0; y ? this._y = y : this._y = 0; @@ -88,41 +90,62 @@ export class TimelineMenu extends React.Component { } + addItem = (type: "input" | "button", title: string, event: (e:any) => void) => { + if (type === "input"){ + let ref = React.createRef(); + let text = ""; + return
    {text = e.target.value;}} onKeyDown={(e:React.KeyboardEvent) => { + if(e.keyCode === 13){ + event(text); + }}}/>
    ; + } else if (type === "button") { + let ref = React.createRef(); + return

    {title}

    ; + } + return
    ; + } + @action + addMenu = (title:string, items: JSX.Element[]) => { + items.unshift(

    {title}

    ); + this._currentMenu = items; + } render() { - let menu: (JSX.Element[] | undefined); - switch(this._type){ - case "keyframe": - menu = [ - , - , - + // let menu: (JSX.Element[] | undefined); + // switch(this._type){ + // case "keyframe": + // menu = [ + //

    Keyframe

    , + //

    Show Data

    , + //

    Delete

    , + //
    - ]; - break; - case "region" : - menu = [ - , - , - , - , - , - , - ]; - break; - case "timeline": - menu = [ + // ]; + // break; + // case "region" : + // menu = [ + //

    Region

    , + //

    Add Ease

    , + //

    Add Path

    , + //
    , + //
    , + //
    , + //
    , + // ]; + // break; + // case "timeline": + // menu = [ - ]; - break; - default: - break; + // ]; + // break; + // default: + // break; - } + // } return (
    - {menu} + {this._currentMenu}
    ); } -- cgit v1.2.3-70-g09d2 From 2ec9f8160204aaaa94239910731facce260c34a5 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Thu, 8 Aug 2019 17:01:05 -0400 Subject: added functionalities --- src/client/views/animationtimeline/Keyframe.tsx | 14 +++- .../views/animationtimeline/TimelineMenu.tsx | 77 ++-------------------- src/client/views/graph/Graph.tsx | 20 ++++-- 3 files changed, 33 insertions(+), 78 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index d1975d847..3c0d17796 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -13,6 +13,9 @@ import { Transform } from "../../util/Transform"; import { InkField, StrokeData } from "../../../new_fields/InkField"; import { number } from "prop-types"; import { TimelineMenu } from "./TimelineMenu"; +import { Docs } from "../../documents/Documents"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; export namespace KeyframeFunc { export enum KeyframeType { @@ -310,7 +313,6 @@ export class Keyframe extends React.Component { 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; @@ -386,6 +388,15 @@ export class Keyframe extends React.Component {
    { this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); + let items = [ + TimelineMenu.Instance.addItem("button", "Show Data", () => { + runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); + CollectionDockingView.Instance.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); + }), + TimelineMenu.Instance.addItem("button", "Delete", () => {}), + TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}) + ]; + TimelineMenu.Instance.addMenu("Keyframe", items); TimelineMenu.Instance.openMenu(e.clientX, e.clientY); }}>
    ); @@ -527,7 +538,6 @@ export class Keyframe extends React.Component { })} {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(); return ( diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 8b47448f2..4223ee099 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -2,38 +2,10 @@ import * as React from "react"; import {observable, action, runInAction} from "mobx"; import {observer} from "mobx-react"; import "./TimelineMenu.scss"; -import { jSXAttribute } from "babel-types"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons"; -import { AddComparisonResult } from "../../northstar/model/idea/idea"; -/** - * TimelineMenu: - * - * - * Timeline: - * - - * - * - * Keyframe: - * - Delete keyframe - * - Move keyframe - * - Edit keyframe (shows schema) - * - * - * Region: - * - Add Keyframe - * - Copy Interpolation - * - Copy path - * - Add Interpolation - * - Add Path - * - Change fades - * - position region - * - duration region - * - - */ - @observer export class TimelineMenu extends React.Component { public static Instance:TimelineMenu; @@ -41,12 +13,13 @@ export class TimelineMenu extends React.Component { @observable private _opacity = 0; @observable private _x = 0; @observable private _y = 0; - @observable private _type: "timeline" | "keyframe" | "region" | "" = ""; @observable private _currentMenu:JSX.Element[] = []; + constructor (props:Readonly<{}>){ super(props); TimelineMenu.Instance = this; } + @action pointerDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -54,15 +27,15 @@ export class TimelineMenu extends React.Component { document.removeEventListener("pointerup", this.pointerUp); document.addEventListener("pointerup", this.pointerUp); document.removeEventListener("pointermove", this.pointerMove); - document.addEventListener("pointermove", this.pointerMove); - - + document.addEventListener("pointermove", this.pointerMove); } + @action pointerMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); } + @action pointerUp = (e: PointerEvent) => { document.removeEventListener("pointermove", this.pointerMove); @@ -81,15 +54,6 @@ export class TimelineMenu extends React.Component { this._opacity = 0; } - @action - addEase = (e: React.MouseEvent) => { - - } - @action - addPath = (e:React.MouseEvent) => { - - } - addItem = (type: "input" | "button", title: string, event: (e:any) => void) => { if (type === "input"){ let ref = React.createRef(); @@ -112,37 +76,6 @@ export class TimelineMenu extends React.Component { } render() { - // let menu: (JSX.Element[] | undefined); - // switch(this._type){ - // case "keyframe": - // menu = [ - //

    Keyframe

    , - //

    Show Data

    , - //

    Delete

    , - //
    - - // ]; - // break; - // case "region" : - // menu = [ - //

    Region

    , - //

    Add Ease

    , - //

    Add Path

    , - //
    , - //
    , - //
    , - //
    , - // ]; - // break; - // case "timeline": - // menu = [ - - // ]; - // break; - // default: - // break; - - // } return (
    {this._currentMenu} diff --git a/src/client/views/graph/Graph.tsx b/src/client/views/graph/Graph.tsx index 864bb8f46..326f33358 100644 --- a/src/client/views/graph/Graph.tsx +++ b/src/client/views/graph/Graph.tsx @@ -1,17 +1,29 @@ import * as React from "react"; +import {observable} from "mobx"; +import { observer } from "mobx-react"; +import { Document, listSpec } from "../../../new_fields/Schema"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { CollectionSubView, CollectionViewProps, SubCollectionViewProps } from "../collections/CollectionSubView"; -export class Graph extends React.Component { - - +export class Graph extends CollectionSubView(Document) { + static Instance:Graph; + + private constructor(props:SubCollectionViewProps) { + super(props); + Graph.Instance = this; + } + + render() { return ( - ) + + + ); } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From c8cb602f06f1b6c325ce467eea8dfa405b673810 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Fri, 9 Aug 2019 17:10:49 -0400 Subject: some changes --- src/client/views/animationtimeline/Keyframe.tsx | 20 +---- src/client/views/animationtimeline/Timeline.tsx | 114 ------------------------ src/client/views/animationtimeline/Track.tsx | 9 +- src/client/views/graph/Graph.tsx | 5 +- 4 files changed, 9 insertions(+), 139 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 3c0d17796..dbc26e3d4 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -11,7 +11,6 @@ import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new import { FlyoutProps } from "./Timeline"; import { Transform } from "../../util/Transform"; import { InkField, StrokeData } from "../../../new_fields/InkField"; -import { number } from "prop-types"; import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; @@ -509,22 +508,6 @@ export class Keyframe extends React.Component { document.removeEventListener("pointerup", this.onReactionListen); } } - - - - /** - * - * - * TEMPORARY - * let items = [ - TimelineMenu.Instance.addItem("button", "Show Data", () => {console.log(toJS(this.props.node));}), - TimelineMenu.Instance.addItem("button", "Delete", () => {}), - TimelineMenu.Instance.addItem("input", "Move", (val) => {console.log(val);}) - ]; - TimelineMenu.Instance.addMenu("Keyframe", items); - TimelineMenu.Instance.openMenu(e.clientX, e.clientY); - * - */ render() { return (
    @@ -537,13 +520,14 @@ export class Keyframe extends React.Component { 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) { + if(this.keyframes.indexOf(kf ) !== this.keyframes.length - 1) { let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; let bodyRef = React.createRef(); return (
    { this.onContainerOver(e, bodyRef); }} onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} + onPointerDown={(e) => { this.props.changeCurrentBarX(NumCast(kf.time) + (e.clientX - bodyRef.current!.getBoundingClientRect().left) * this.props.transform.Scale);}} onContextMenu={(e) => { let items = [ TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 7a6d9fa52..052e6e925 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -350,7 +350,6 @@ export class Timeline extends CollectionSubView(Document) {
    -
    @@ -383,12 +382,6 @@ export class Timeline extends CollectionSubView(Document) { } -interface TimelineFlyoutProps { - flyoutInfo: FlyoutProps; - tickSpacing: number; - -} - interface TimelineOverviewProps { currentBarX: number; } @@ -412,116 +405,9 @@ class TimelineOverview extends React.Component{ } } -class TimelineFlyout extends React.Component{ - - @observable private _timeInput = React.createRef(); - @observable private _durationInput = React.createRef(); - @observable private _fadeInInput = React.createRef(); - @observable private _fadeOutInput = React.createRef(); - @observable private _data: FlyoutProps = { x: 0, y: 0, display: "none", regiondata: new Doc(), regions: new List() }; - - 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 ( -
    -
    - -
    -

    Time:

    -

    Duration:

    -

    Fade-in

    -

    Fade-out

    -
    -
    - - - - -
    - -
    -
    - ); - } -} class TimelineZoom extends React.Component { componentDidMount() { - } render() { return ( diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 363ab4074..64e94f0f1 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -61,8 +61,7 @@ export class Track extends React.Component { let keyframes:List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); let kfIndex:number = keyframes.indexOf(ref); let kf = keyframes[kfIndex] as Doc; - if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for fades - console.log("full keychange triggered"); + if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); 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 @@ -104,7 +103,6 @@ export class Track extends React.Component { @action timeChange = async (time: number) => { if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { - console.log("saving"); await this.saveKeyframe(this._onKeyframe, this._onRegionData); } let regiondata = await this.findRegion(Math.round(time)); //finds a region that the scrubber is on @@ -169,9 +167,9 @@ export class Track extends React.Component { 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); @@ -226,8 +224,7 @@ export class Track extends React.Component { if (nodeData !== currentNodeData) { this.props.node[key] = new RichTextField(nodeData); } - } - + } } else if (key === "creationDate") { } else { diff --git a/src/client/views/graph/Graph.tsx b/src/client/views/graph/Graph.tsx index 326f33358..d925cc32c 100644 --- a/src/client/views/graph/Graph.tsx +++ b/src/client/views/graph/Graph.tsx @@ -20,8 +20,11 @@ export class Graph extends CollectionSubView(Document) { render() { + let collection = ; + return ( - +
    +
    ); } -- cgit v1.2.3-70-g09d2 From c7e258d9d56990daec490b239e4103f9ca9d521a Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Mon, 12 Aug 2019 15:17:20 -0400 Subject: playmode / authoring mode --- src/client/views/animationtimeline/Timeline.scss | 66 ++----------- src/client/views/animationtimeline/Timeline.tsx | 121 +++++++++-------------- src/client/views/nodes/DocumentView.tsx | 3 +- 3 files changed, 57 insertions(+), 133 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 47f448adb..e5d898502 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -6,72 +6,24 @@ 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%; - } +.timeline-toolbox{ + position:absolute; + display:flex; + align-items: flex-start; + flex-direction: row; + top: 10px; + div{ + margin-left:10px; } - .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; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 052e6e925..c8f11db5b 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -5,7 +5,7 @@ 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 { Cast, NumCast, FieldValue, StrCast, BoolCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Doc, DocListCast } from "../../../new_fields/Doc"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -82,51 +82,48 @@ export class Timeline extends CollectionSubView(Document) { return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; } - @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; - } - } + componentWillMount() { + this.props.Document.isAnimating ? this.props.Document.isAnimating = true : this.props.Document.isAnimating = false; } - 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 }); + return this.props.Document.isAnimating; + }, async isAnimating => { + if (isAnimating){ + this._ticks = []; + for (let i = 0; i < this._time;) { + this._ticks.push(i); + i += this._tickIncrement; + } + observe(this._trackbox, change => {if (change.type === "update"){ + if (this.props.Document.isAnimating){ + 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}`; + } + }}); + } + + }); }); } + @action changeCurrentBarX = (x: number) => { @@ -346,73 +343,47 @@ export class Timeline extends CollectionSubView(Document) { } render() { - return ( -
    - -
    -
    -
    -
    -
    + let timeline:JSX.Element[] = []; + BoolCast(this.props.Document.isAnimating) ? timeline = [ +
    + +
    +
    +
    +
    +
    -
    -
    +
    +
    {this._ticks.map(element => { return

    {this.toTime(element)}

    ; })}
    -
    -
    +
    +
    -
    +
    {DocListCast(this.children).map(doc => )}
    -
    +
    {DocListCast(this.children).map(doc =>

    {doc.title}

    )}
    -
    +
    - ); - } - -} - - -interface TimelineOverviewProps { - currentBarX: number; -} - -class TimelineOverview extends React.Component{ - - componentWillMount() { - - } - - render() { - return ( -
    -
    -
    -
    -
    -
    -
    - ); - } -} - + ] : timeline = [ +
    +
    +
    +
    +
    ]; -class TimelineZoom extends React.Component { - componentDidMount() { - } - render() { return (
    - + {timeline}
    ); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a7b4f33db..95970cb81 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -107,7 +107,7 @@ const schema = createSchema({ nativeHeight: "number", backgroundColor: "string", opacity: "number", - hidden: "boolean" + hidden: "boolean", }); export const positionSchema = createSchema({ @@ -565,6 +565,7 @@ export class DocumentView extends DocComponent(Docu cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); cm.addItem({ description: "Transcribe Speech", event: this.listen, icon: "microphone" }); + cm.addItem({ description: BoolCast(this.props.Document.isAnimating) ? "Enter Play Mode" : "Enter Authoring Mode", event: () => {BoolCast(this.props.Document.isAnimating) ? this.props.Document.isAnimating = false : this.props.Document.isAnimating = true;}, icon:BoolCast(this.props.Document.isAnimating) ? "play" : "edit"}); let makes: ContextMenuProps[] = []; makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); -- cgit v1.2.3-70-g09d2 From eab360d4c415163e4bfe14f167c84b58902971b5 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Mon, 12 Aug 2019 17:04:36 -0400 Subject: authoring/play mode and timeline overview --- src/client/views/animationtimeline/Timeline.tsx | 100 ++++++++++----------- .../views/animationtimeline/TimelineOverview.scss | 13 +++ .../views/animationtimeline/TimelineOverview.tsx | 33 +++++++ 3 files changed, 92 insertions(+), 54 deletions(-) create mode 100644 src/client/views/animationtimeline/TimelineOverview.scss create mode 100644 src/client/views/animationtimeline/TimelineOverview.tsx (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index c8f11db5b..97b9ad4db 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -21,6 +21,7 @@ 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"; +import { TimelineOverview } from "./TimelineOverview"; export interface FlyoutProps { @@ -102,24 +103,17 @@ export class Timeline extends CollectionSubView(Document) { } runInAction(() => { reaction(() => { - return this.props.Document.isAnimating; - }, async isAnimating => { - if (isAnimating){ - this._ticks = []; - for (let i = 0; i < this._time;) { - this._ticks.push(i); - i += this._tickIncrement; - } - observe(this._trackbox, change => {if (change.type === "update"){ - if (this.props.Document.isAnimating){ - 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}`; - } - }}); - } - + 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}`; }); }); } @@ -343,47 +337,45 @@ export class Timeline extends CollectionSubView(Document) { } render() { - let timeline:JSX.Element[] = []; - BoolCast(this.props.Document.isAnimating) ? timeline = [ -
    - -
    -
    -
    -
    -
    -
    -
    -
    - {this._ticks.map(element => { - return

    {this.toTime(element)}

    ; - })} + + + return ( +
    +
    + +
    +
    +
    +
    +
    -
    -
    +
    +
    + {this._ticks.map(element => { + return

    {this.toTime(element)}

    ; + })} +
    +
    +
    +
    +
    + {DocListCast(this.children).map(doc => )} +
    -
    - {DocListCast(this.children).map(doc => )} +
    + {DocListCast(this.children).map(doc =>

    {doc.title}

    )} +
    +
    +
    -
    -
    - {DocListCast(this.children).map(doc =>

    {doc.title}

    )} -
    -
    -
    -
    - ] : timeline = [ -
    -
    -
    -
    -
    ]; - - return ( -
    - {timeline} +
    +
    +
    +
    + +
    ); } diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss new file mode 100644 index 000000000..a71abf348 --- /dev/null +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -0,0 +1,13 @@ +.timeline-overview-container{ + width: 300px; + height: 40px; + margin-top: 10px; + margin-left: 20px; + background: white; + border: 1px solid black; + .timeline-overview-visible{ + height: 100%; + background: green; + border: 1px solid black; + } +} \ No newline at end of file diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx new file mode 100644 index 000000000..0e10e655d --- /dev/null +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -0,0 +1,33 @@ +import * as React from "react"; +import {observable} from "mobx"; +import {observer} from "mobx-react"; +import "./TimelineOverview.scss"; + + + +interface TimelineOverviewProps{ + totalLength: number; + visibleLength:number; + visibleStart:number; + changeCurrentBarX: (x:number) => any; +} + + +export class TimelineOverview extends React.Component{ + + + render(){ + return( +
    +
    +
    +
    +
    +
    +
    + ); + } + +} + + -- cgit v1.2.3-70-g09d2 From 91335b57dafdeda7a1feb703b0839337fcbbfecd Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 13 Aug 2019 17:26:32 -0400 Subject: timeline overview completed! --- src/client/views/animationtimeline/Keyframe.tsx | 2 + src/client/views/animationtimeline/Timeline.tsx | 77 ++++++++++++++-------- .../views/animationtimeline/TimelineOverview.scss | 24 +++++++ .../views/animationtimeline/TimelineOverview.tsx | 61 +++++++++++++++-- src/client/views/animationtimeline/Track.tsx | 1 + 5 files changed, 134 insertions(+), 31 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index dbc26e3d4..f6c04d43e 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -91,6 +91,8 @@ export namespace KeyframeFunc { regiondata.functions = new List(); return regiondata; }; + + } export const RegionDataSchema = createSchema({ diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 97b9ad4db..a29758694 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import "./Timeline.scss"; -import { CollectionSubView } from "../collections/CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "../collections/CollectionSubView"; import { Document, listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; @@ -35,8 +35,6 @@ export interface FlyoutProps { @observer export class Timeline extends CollectionSubView(Document) { - static Instance:Timeline; - private readonly DEFAULT_CONTAINER_HEIGHT: number = 300; private readonly DEFAULT_TICK_SPACING: number = 50; @@ -53,16 +51,16 @@ export class Timeline extends CollectionSubView(Document) { @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); - @observable private _timelineWrapper = React.createRef(); @observable private _infoContainer = React.createRef(); - @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 _totalLength: number = 0; + @observable private _visibleLength: number = 0; + @observable private _visibleStart: number = 0; @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; @observable private _time = 100000; //DEFAULT @observable private _ticks: number[] = []; @@ -83,8 +81,10 @@ export class Timeline extends CollectionSubView(Document) { return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; } + componentWillMount() { this.props.Document.isAnimating ? this.props.Document.isAnimating = true : this.props.Document.isAnimating = false; + console.log(this._currentBarX); } componentDidMount() { @@ -99,11 +99,10 @@ export class Timeline extends CollectionSubView(Document) { this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); }); } - } runInAction(() => { reaction(() => { - return this._time; + return this._time; }, () => { this._ticks = []; for (let i = 0; i < this._time;) { @@ -111,17 +110,22 @@ export class Timeline extends CollectionSubView(Document) { 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}`; - }); + this._totalLength = this._tickSpacing * this._ticks.length; + trackbox.style.width = `${this._totalLength}`; + this._scrubberbox.current!.style.width = `${this._totalLength}`; + }, {fireImmediately:true}); + this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; + this._visibleStart = this._infoContainer.current!.scrollLeft; }); + } + + @action - changeCurrentBarX = (x: number) => { - this._currentBarX = x; + changeCurrentBarX = (pixel: number) => { + this._currentBarX = pixel; } //for playing @@ -139,10 +143,10 @@ export class Timeline extends CollectionSubView(Document) { @action changeCurrentX = () => { - if (this._currentBarX === this._boxLength && this._isPlaying) { + if (this._currentBarX === this._totalLength && this._isPlaying) { this._currentBarX = 0; } - if (this._currentBarX <= this._boxLength && this._isPlaying) { + if (this._currentBarX <= this._totalLength && this._isPlaying) { this._currentBarX = this._currentBarX + this._windSpeed; setTimeout(this.changeCurrentX, 15); } @@ -208,13 +212,18 @@ export class Timeline extends CollectionSubView(Document) { 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; + this.movePanX(this._visibleStart - e.movementX); trackbox.scrollTop = trackbox.scrollTop - e.movementY; titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; } + @action + movePanX = (pixel:number) => { + let infoContainer = this._infoContainer.current!; + infoContainer.scrollLeft = pixel; + this._visibleStart = infoContainer.scrollLeft; + } @action @@ -276,7 +285,7 @@ export class Timeline extends CollectionSubView(Document) { } @action - toTime = (time: number): string => { + toReadTime = (time: number): string => { const inSeconds = time / 1000; let min: (string | number) = Math.floor(inSeconds / 60); let sec: (string | number) = inSeconds % 60; @@ -288,6 +297,23 @@ export class Timeline extends CollectionSubView(Document) { } + convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time") => { + let time = dir === "pixel" ? pos / this._tickSpacing * this._tickIncrement : pos * this._tickSpacing / this._tickIncrement; + switch (unit) { + case "mili": + return time; + case "sec": + return dir === "pixel" ? time / 1000 : time * 1000; + case "min": + return dir === "pixel" ? time / 60000 : time * 60000; + case "hr": + return dir === "pixel" ? time / 3600000 : time * 3600000; + default: + return time; + } + } + + private _freezeText = "Freeze Timeline"; timelineContextMenu = (e: React.MouseEvent): void => { @@ -337,22 +363,21 @@ export class Timeline extends CollectionSubView(Document) { } render() { - - return (
    -
    -
    -
    +
    +
    +
    +
    {this._ticks.map(element => { - return

    {this.toTime(element)}

    ; + return

    {this.toReadTime(element)}

    ; })}
    @@ -374,7 +399,7 @@ export class Timeline extends CollectionSubView(Document) {
    - +
    ); diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index a71abf348..21988927d 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -5,9 +5,33 @@ margin-left: 20px; background: white; border: 1px solid black; + padding: 0px; + display:inline-block; .timeline-overview-visible{ height: 100%; background: green; border: 1px solid black; + margin: 0px; + } + .timeline-overview-scrubber-container{ + height: 100%; + margin-top: -40px; + margin-left: 0px; + width: 2px; + z-index: 1001; + background-color:black; + display: inline-block; + .timeline-overview-scrubber-head{ + position:absolute; + height: 30px; + width: 30px; + background-color:transparent; + border-radius: 50%; + border: 5px solid black; + margin-left: -15px; + top: -15px; + + } + } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 0e10e655d..1ad7d20e5 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import {observable} from "mobx"; +import {observable, action} from "mobx"; import {observer} from "mobx-react"; import "./TimelineOverview.scss"; @@ -9,20 +9,71 @@ interface TimelineOverviewProps{ totalLength: number; visibleLength:number; visibleStart:number; - changeCurrentBarX: (x:number) => any; + currentBarX:number; + changeCurrentBarX: (pixel:number) => void; + movePanX: (pixel:number) => any; } +@observer export class TimelineOverview extends React.Component{ + @observable private _visibleRef = React.createRef(); + @observable private _scrubberRef = React.createRef(); + + @action + onPointerDown = (e:React.PointerEvent) => { + document.removeEventListener("pointermove", this.onPanX); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPanX); + document.addEventListener("pointerup", this.onPointerUp); + } + + @action + onPanX = (e: PointerEvent) => { + let movX = (this.props.visibleStart / this.props.totalLength)* 300 + e.movementX; + this.props.movePanX((movX / 300) * this.props.totalLength); + } + + @action + onPointerUp = (e: PointerEvent) => { + document.removeEventListener("pointermove", this.onPanX); + document.removeEventListener("pointerup", this.onPointerUp); + } + @action + onScrubberDown = ( e:React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.removeEventListener("pointermove", this.onScrubberMove); + document.removeEventListener("pointerup", this.onScrubberUp); + document.addEventListener("pointermove", this.onScrubberMove); + document.addEventListener("pointerup", this.onScrubberUp); + } + + @action + onScrubberMove = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + let scrubberRef = this._scrubberRef.current!; + let left = scrubberRef.getBoundingClientRect().left; + let offsetX = Math.round(e.clientX - left); + this.props.changeCurrentBarX(((offsetX / 300) * this.props.totalLength) + this.props.currentBarX); + } + + @action + onScrubberUp = (e:PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.removeEventListener("pointermove", this.onScrubberMove); + document.removeEventListener("pointerup", this.onScrubberUp); + } render(){ return(
    -
    -
    +
    +
    -
    ); diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 64e94f0f1..6bdabeb93 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -94,6 +94,7 @@ export class Track extends React.Component { this.props.node.hidden = false; await this.timeChange(this.props.currentBarX); } else { + console.log("heuulloo"); this.props.node.hidden = true; } }, { fireImmediately: true }); -- cgit v1.2.3-70-g09d2 From f43509c132b5302b9983667aaf99251051b38b59 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Wed, 14 Aug 2019 11:52:33 -0400 Subject: bug fix with currentBarX --- src/client/views/animationtimeline/Keyframe.tsx | 4 ++-- src/client/views/animationtimeline/Timeline.tsx | 8 ++++---- src/client/views/animationtimeline/Track.tsx | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index f6c04d43e..6a0525eb8 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -183,8 +183,8 @@ export class Keyframe extends React.Component { 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; + (start.key! as Doc).opacity = 0; + (finish.key! as Doc).opacity = 0; observe(this.regiondata, change => { if (change.type === "update") { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index a29758694..29787fa03 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -125,7 +125,7 @@ export class Timeline extends CollectionSubView(Document) { @action changeCurrentBarX = (pixel: number) => { - this._currentBarX = pixel; + pixel <= 0 ? this._currentBarX = 0 : pixel >= this._totalLength ? this._currentBarX = this._totalLength : this._currentBarX = pixel; } //for playing @@ -184,7 +184,7 @@ export class Timeline extends CollectionSubView(Document) { let scrubberbox = this._scrubberbox.current!; let left = scrubberbox.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; - this._currentBarX = offsetX; + this.changeCurrentBarX(offsetX); } @action @@ -192,8 +192,8 @@ export class Timeline extends CollectionSubView(Document) { e.preventDefault(); e.stopPropagation(); let scrubberbox = this._scrubberbox.current!; - let offset = (e.clientX - scrubberbox.getBoundingClientRect().left) * this.props.ScreenToLocalTransform().Scale; - this._currentBarX = offset; + let offsetX = (e.clientX - scrubberbox.getBoundingClientRect().left) * this.props.ScreenToLocalTransform().Scale; + this.changeCurrentBarX(offsetX); } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 6bdabeb93..288a1d2ad 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -94,8 +94,8 @@ export class Track extends React.Component { this.props.node.hidden = false; await this.timeChange(this.props.currentBarX); } else { - console.log("heuulloo"); - this.props.node.hidden = true; + this.props.node.hidden = true; + this.props.node.opacity = 0; } }, { fireImmediately: true }); } -- cgit v1.2.3-70-g09d2 From e04277caf25be1c657a2f779a64eb5fcb4e19461 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Wed, 14 Aug 2019 16:08:01 -0400 Subject: UI improvements, pointerevents, and improved overview --- src/client/views/animationtimeline/Keyframe.tsx | 51 +++++++---- src/client/views/animationtimeline/Timeline.scss | 2 +- src/client/views/animationtimeline/Timeline.tsx | 100 ++++++++------------- .../views/animationtimeline/TimelineOverview.scss | 5 +- .../views/animationtimeline/TimelineOverview.tsx | 19 ++-- src/client/views/animationtimeline/Track.tsx | 5 +- src/client/views/graph/GraphManager.ts | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 8 files changed, 90 insertions(+), 96 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 6a0525eb8..784765318 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -2,10 +2,10 @@ 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 { observer} from "mobx-react"; +import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction } from "mobx"; import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; -import { Cast, FieldValue, StrCast, NumCast } from "../../../new_fields/Types"; +import { Cast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema"; import { FlyoutProps } from "./Timeline"; @@ -13,7 +13,6 @@ import { Transform } from "../../util/Transform"; import { InkField, StrokeData } from "../../../new_fields/InkField"; import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionDockingView } from "../collections/CollectionDockingView"; export namespace KeyframeFunc { @@ -111,7 +110,6 @@ interface IProps { RegionData: Doc; collection: Doc; changeCurrentBarX: (x: number) => void; - setFlyout: (props: FlyoutProps) => any; transform: Transform; } @@ -183,8 +181,8 @@ export class Keyframe extends React.Component { 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; - (finish.key! as Doc).opacity = 0; + (start.key! as Doc).opacity = 0.1; + (finish.key! as Doc).opacity = 0.1; observe(this.regiondata, change => { if (change.type === "update") { @@ -238,14 +236,32 @@ export class Keyframe extends React.Component { return TK; } + @observable private _mouseToggled = false; + @observable private _doubleClickEnabled = false; @action onBarPointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - document.addEventListener("pointermove", this.onBarPointerMove); - document.addEventListener("pointerup", (e: PointerEvent) => { + + let clientX = e.clientX; + if (this._doubleClickEnabled){ + this.createKeyframe(clientX); + this._doubleClickEnabled = false; + } else { + setTimeout(() => {if(!this._mouseToggled && this._doubleClickEnabled)this.props.changeCurrentBarX(this.regiondata.position + (clientX - this._bar.current!.getBoundingClientRect().left) * this.props.transform.Scale); + this._mouseToggled = false; + this._doubleClickEnabled = false; }, 200); + this._doubleClickEnabled = true; + document.addEventListener("pointermove", this.onBarPointerMove); + document.addEventListener("pointerup", (e: PointerEvent) => { document.removeEventListener("pointermove", this.onBarPointerMove); }); + + } + + + + } @@ -253,6 +269,9 @@ export class Keyframe extends React.Component { onBarPointerMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); + if (e.movementX !== 0) { + this._mouseToggled = true; + } 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; @@ -351,11 +370,10 @@ export class Keyframe extends React.Component { } @action - createKeyframe = async (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); + createKeyframe = async (clientX:number) => { + this._mouseToggled = true; let bar = this._bar.current!; - let offset = Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale); + let offset = Math.round((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)); @@ -514,8 +532,7 @@ export class Keyframe extends React.Component { return (
    + onPointerDown={this.onBarPointerDown}>
    {this.regiondata.keyframes!.map(kf => { @@ -529,8 +546,10 @@ export class Keyframe extends React.Component {
    { this.onContainerOver(e, bodyRef); }} onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} - onPointerDown={(e) => { this.props.changeCurrentBarX(NumCast(kf.time) + (e.clientX - bodyRef.current!.getBoundingClientRect().left) * this.props.transform.Scale);}} onContextMenu={(e) => { + e.preventDefault(); + e.stopPropagation(); + this._mouseToggled = true; let items = [ TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index e5d898502..1457d5a84 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -8,7 +8,7 @@ } .timeline-toolbox{ - position:absolute; + position:absolute; display:flex; align-items: flex-start; flex-direction: row; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 29787fa03..3d878660d 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -1,26 +1,17 @@ import * as React from "react"; import "./Timeline.scss"; -import { CollectionSubView, SubCollectionViewProps } from "../collections/CollectionSubView"; +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, BoolCast } from "../../../new_fields/Types"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction } from "mobx"; +import { Cast, NumCast, StrCast, BoolCast } 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 "../nodes/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"; import { TimelineOverview } from "./TimelineOverview"; @@ -57,7 +48,7 @@ export class Timeline extends CollectionSubView(Document) { @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 _isFrozen: boolean = true; //timeline freeze @observable private _totalLength: number = 0; @observable private _visibleLength: number = 0; @observable private _visibleStart: number = 0; @@ -65,7 +56,6 @@ export class Timeline extends CollectionSubView(Document) { @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() }; @computed private get children(): List { @@ -130,30 +120,33 @@ export class Timeline extends CollectionSubView(Document) { //for playing @action - onPlay = async (e: React.MouseEvent) => { + onPlay = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); if (this._isPlaying) { this._isPlaying = false; this._playButton = faPlayCircle; } else { this._isPlaying = true; this._playButton = faPauseCircle; - this.changeCurrentX(); - } - } - - @action - changeCurrentX = () => { - if (this._currentBarX === this._totalLength && this._isPlaying) { - this._currentBarX = 0; - } - if (this._currentBarX <= this._totalLength && this._isPlaying) { - this._currentBarX = this._currentBarX + this._windSpeed; - setTimeout(this.changeCurrentX, 15); + const playTimeline = () => { + if (this._isPlaying){ + if (this._currentBarX >= this._totalLength) { + this.changeCurrentBarX(0); + } else { + this.changeCurrentBarX(this._currentBarX + this._windSpeed); + setTimeout(playTimeline, 15); + } + } + }; + playTimeline(); } } @action windForward = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); if (this._windSpeed < 64) { //max speed is 32 this._windSpeed = this._windSpeed * 2; } @@ -161,6 +154,8 @@ export class Timeline extends CollectionSubView(Document) { @action windBackward = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); if (this._windSpeed > 1 / 16) { // min speed is 1/8 this._windSpeed = this._windSpeed / 2; } @@ -313,16 +308,12 @@ export class Timeline extends CollectionSubView(Document) { } } - - 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"; @@ -330,22 +321,11 @@ export class Timeline extends CollectionSubView(Document) { }), 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(() => { + description: this._isFrozen ? "Unfreeze Timeline" : "Freeze Timeline", event: action(() => { if (this._isFrozen) { this._isFrozen = false; - this._freezeText = "Freeze Timeline"; } else { this._isFrozen = true; - this._freezeText = "Unfreeze Timeline"; } }), icon: "thumbtack" }); @@ -353,27 +333,24 @@ export class Timeline extends CollectionSubView(Document) { } - - @action - getFlyout = (props: FlyoutProps) => { - for (const [k, v] of Object.entries(props)) { - (this.flyoutInfo as any)[k] = v; - } - console.log(this.flyoutInfo); + private timelineToolBox = (scale:number) => { + let size = 50 * scale; //50 is default + return ( +
    +
    +
    +
    + +
    + ); } - render() { return (
    -
    -
    -
    -
    - -
    + {this.timelineToolBox(0.5)}
    {this._ticks.map(element => { @@ -384,7 +361,7 @@ export class Timeline extends CollectionSubView(Document) {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => )}
    @@ -395,12 +372,7 @@ export class Timeline extends CollectionSubView(Document) {
    -
    -
    -
    -
    - -
    + {BoolCast(this.props.Document.isAnimating) ?
    : this.timelineToolBox(1) }
    ); } diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index 21988927d..9e69c2adf 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -1,16 +1,17 @@ +@import "./../globalCssVariables.scss"; + .timeline-overview-container{ width: 300px; height: 40px; margin-top: 10px; margin-left: 20px; background: white; - border: 1px solid black; + border: 2px solid black; padding: 0px; display:inline-block; .timeline-overview-visible{ height: 100%; background: green; - border: 1px solid black; margin: 0px; } .timeline-overview-scrubber-container{ diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 1ad7d20e5..4fdf1381e 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -6,6 +6,7 @@ import "./TimelineOverview.scss"; interface TimelineOverviewProps{ + scale: number; totalLength: number; visibleLength:number; visibleStart:number; @@ -19,7 +20,9 @@ interface TimelineOverviewProps{ export class TimelineOverview extends React.Component{ @observable private _visibleRef = React.createRef(); @observable private _scrubberRef = React.createRef(); - + private readonly DEFAULT_HEIGHT = 50; + private readonly DEFAULT_WIDTH = 300; + @action onPointerDown = (e:React.PointerEvent) => { document.removeEventListener("pointermove", this.onPanX); @@ -30,8 +33,8 @@ export class TimelineOverview extends React.Component{ @action onPanX = (e: PointerEvent) => { - let movX = (this.props.visibleStart / this.props.totalLength)* 300 + e.movementX; - this.props.movePanX((movX / 300) * this.props.totalLength); + let movX = (this.props.visibleStart / this.props.totalLength)* (this.DEFAULT_WIDTH * this.props.scale) + e.movementX; + this.props.movePanX((movX / (this.DEFAULT_WIDTH * this.props.scale)) * this.props.totalLength); } @action @@ -57,7 +60,7 @@ export class TimelineOverview extends React.Component{ let scrubberRef = this._scrubberRef.current!; let left = scrubberRef.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left); - this.props.changeCurrentBarX(((offsetX / 300) * this.props.totalLength) + this.props.currentBarX); + this.props.changeCurrentBarX(((offsetX / (this.DEFAULT_WIDTH * this.props.scale)) * this.props.totalLength) + this.props.currentBarX); } @action @@ -70,10 +73,10 @@ export class TimelineOverview extends React.Component{ render(){ return( -
    -
    -
    -
    +
    +
    +
    +
    ); diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 288a1d2ad..d91954022 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -17,7 +17,6 @@ interface IProps { transform: Transform; collection: Doc; changeCurrentBarX: (x: number) => void; - setFlyout: (props: FlyoutProps) => any; } @observer @@ -177,7 +176,7 @@ export class Track extends React.Component { 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 = await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List; + let interY:List = (await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List)!; let realIndex = (interY.length - 1) * timeratio; let xIndex = Math.floor(realIndex); let yValue = interY[xIndex]; @@ -276,7 +275,7 @@ export class Track extends React.Component {
    {DocListCast(this.regions).map((region) => { - return ; + return ; })}
    diff --git a/src/client/views/graph/GraphManager.ts b/src/client/views/graph/GraphManager.ts index 9d62b1ef8..b62f2337b 100644 --- a/src/client/views/graph/GraphManager.ts +++ b/src/client/views/graph/GraphManager.ts @@ -32,7 +32,7 @@ export class GraphManager { defaultGraphs = () => { - this.GraphData.linear = ; + } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f2be7097a..28af39fb3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -729,7 +729,7 @@ export class DocumentView extends DocComponent(Docu render() { - trace(); + // trace(); let backgroundColor = this.layoutDoc.isBackground || (this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document.clusterOverridesDefaultBackground && this.layoutDoc.backgroundColor === this.layoutDoc.defaultBackgroundColor) ? this.props.backgroundColor(this.layoutDoc) || StrCast(this.layoutDoc.backgroundColor) : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.layoutDoc); -- cgit v1.2.3-70-g09d2 From e507271488fe4c8dcb548dc766ab131fa84f4437 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Wed, 14 Aug 2019 16:52:19 -0400 Subject: ink fix and overview fix --- src/client/views/InkingCanvas.scss | 2 +- src/client/views/InkingCanvas.tsx | 4 ++-- src/client/views/animationtimeline/TimelineOverview.tsx | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index d95398f17..2052021d1 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -21,7 +21,7 @@ width: 8192px; height: 8192px; cursor: "crosshair"; - pointer-events: auto; + pointer-events: all; } diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index b08133d80..1cfa8d644 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -170,12 +170,12 @@ export class InkingCanvas extends React.Component { return [!penPaths.length ? (null) : - {} + {penPaths} , !markerPaths.length ? (null) : - {} + {markerPaths} ]; } diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 4fdf1381e..38b823cbc 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -25,6 +25,8 @@ export class TimelineOverview extends React.Component{ @action onPointerDown = (e:React.PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); document.removeEventListener("pointermove", this.onPanX); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPanX); @@ -33,12 +35,16 @@ export class TimelineOverview extends React.Component{ @action onPanX = (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); let movX = (this.props.visibleStart / this.props.totalLength)* (this.DEFAULT_WIDTH * this.props.scale) + e.movementX; this.props.movePanX((movX / (this.DEFAULT_WIDTH * this.props.scale)) * this.props.totalLength); } @action onPointerUp = (e: PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); document.removeEventListener("pointermove", this.onPanX); document.removeEventListener("pointerup", this.onPointerUp); } -- cgit v1.2.3-70-g09d2 From 3d99b990b95edd6481a6c12bc994cdac87e6c8dc Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Thu, 15 Aug 2019 12:48:06 -0400 Subject: uiux --- src/client/views/animationtimeline/Keyframe.tsx | 8 +---- src/client/views/animationtimeline/Timeline.tsx | 30 +++++++++++++---- .../views/animationtimeline/TimelineMenu.tsx | 39 ++++++---------------- 3 files changed, 36 insertions(+), 41 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 784765318..4e58b07d8 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -242,7 +242,6 @@ export class Keyframe extends React.Component { onBarPointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let clientX = e.clientX; if (this._doubleClickEnabled){ this.createKeyframe(clientX); @@ -254,14 +253,9 @@ export class Keyframe extends React.Component { this._doubleClickEnabled = true; document.addEventListener("pointermove", this.onBarPointerMove); document.addEventListener("pointerup", (e: PointerEvent) => { - document.removeEventListener("pointermove", this.onBarPointerMove); + document.removeEventListener("pointermove", this.onBarPointerMove); }); - } - - - - } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 3d878660d..10034263c 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -135,8 +135,8 @@ export class Timeline extends CollectionSubView(Document) { this.changeCurrentBarX(0); } else { this.changeCurrentBarX(this._currentBarX + this._windSpeed); - setTimeout(playTimeline, 15); - } + } + setTimeout(playTimeline, 15); } }; playTimeline(); @@ -193,20 +193,38 @@ export class Timeline extends CollectionSubView(Document) { + @observable private _mouseToggled = false; + @observable private _doubleClickEnabled = false; @action onPanDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - document.addEventListener("pointermove", this.onPanMove); - document.addEventListener("pointerup", () => { - document.removeEventListener("pointermove", this.onPanMove); - }); + let clientX = e.clientX; + if (this._doubleClickEnabled){ + this._doubleClickEnabled = false; + } else { + setTimeout(() => {if(!this._mouseToggled && this._doubleClickEnabled) this.changeCurrentBarX(this._trackbox.current!.scrollLeft + clientX - this._trackbox.current!.getBoundingClientRect().left); + this._mouseToggled = false; + this._doubleClickEnabled = false;}, 200); + this._doubleClickEnabled = true; + document.addEventListener("pointermove", this.onPanMove); + document.addEventListener("pointerup", () => { + document.removeEventListener("pointermove", this.onPanMove); + if (!this._doubleClickEnabled) { + this._mouseToggled = false; + } + }); + + } } @action onPanMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); + if (e.movementX !== 0 || e.movementY !== 0) { + this._mouseToggled = true; + } let trackbox = this._trackbox.current!; let titleContainer = this._titleContainer.current!; this.movePanX(this._visibleStart - e.movementX); diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 4223ee099..3e63eec61 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -19,28 +19,6 @@ export class TimelineMenu extends React.Component { super(props); TimelineMenu.Instance = this; } - - @action - pointerDown = (e: React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.removeEventListener("pointerup", this.pointerUp); - document.addEventListener("pointerup", this.pointerUp); - document.removeEventListener("pointermove", this.pointerMove); - document.addEventListener("pointermove", this.pointerMove); - } - - @action - pointerMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - } - - @action - pointerUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.pointerMove); - document.removeEventListener("pointerup", this.pointerUp); - } @action openMenu = (x?:number, y?:number) => { @@ -52,16 +30,20 @@ export class TimelineMenu extends React.Component { @action closeMenu = () => { this._opacity = 0; + this._currentMenu = []; } addItem = (type: "input" | "button", title: string, event: (e:any) => void) => { if (type === "input"){ let ref = React.createRef(); - let text = ""; - return
    {text = e.target.value;}} onKeyDown={(e:React.KeyboardEvent) => { - if(e.keyCode === 13){ - event(text); - }}}/>
    ; + return
    { + let text = e.target.value; + document.addEventListener("keypress", (e:KeyboardEvent) => { + if (e.keyCode === 13) { + event(text); + } + }); + }}/>
    ; } else if (type === "button") { let ref = React.createRef(); return

    {title}

    ; @@ -72,7 +54,8 @@ export class TimelineMenu extends React.Component { @action addMenu = (title:string, items: JSX.Element[]) => { items.unshift(

    {title}

    ); - this._currentMenu = items; + this._currentMenu = items; + } render() { -- cgit v1.2.3-70-g09d2 From 02f5eed127280a6827ed57e86663291ce184495b Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Thu, 15 Aug 2019 17:05:53 -0400 Subject: Timeline visibility --- src/client/views/animationtimeline/Timeline.tsx | 46 ++++++++++++++++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +-- src/client/views/nodes/DocumentView.tsx | 1 - 3 files changed, 36 insertions(+), 17 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 10034263c..be1ae1773 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -4,15 +4,18 @@ 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, computed, runInAction } from "mobx"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction, observe } from "mobx"; import { Cast, NumCast, StrCast, BoolCast } 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 { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDown, faClock, faPauseCircle, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; +import { playcustomapp } from "googleapis/build/src/apis/playcustomapp"; +import { FieldView, FieldViewProps } from "../nodes/FieldView"; + export interface FlyoutProps { @@ -25,7 +28,7 @@ export interface FlyoutProps { @observer -export class Timeline extends CollectionSubView(Document) { +export class Timeline extends React.Component { private readonly DEFAULT_CONTAINER_HEIGHT: number = 300; private readonly DEFAULT_TICK_SPACING: number = 50; @@ -56,6 +59,7 @@ export class Timeline extends CollectionSubView(Document) { @observable private _time = 100000; //DEFAULT @observable private _ticks: number[] = []; @observable private _playButton = faPlayCircle; + @observable private _timelineVisible = false; @computed private get children(): List { @@ -74,6 +78,7 @@ export class Timeline extends CollectionSubView(Document) { componentWillMount() { this.props.Document.isAnimating ? this.props.Document.isAnimating = true : this.props.Document.isAnimating = false; + document.addEventListener("contextmenu", (e) => {this.timelineContextMenu(e);}); console.log(this._currentBarX); } @@ -86,7 +91,12 @@ export class Timeline extends CollectionSubView(Document) { reaction(() => { return NumCast(this.props.Document.curPage); }, curPage => { - this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); + if (!this._isPlaying) { + this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); + this.props.Document.curPage = this._currentBarX; + this.play(); + } + }); } } @@ -123,6 +133,10 @@ export class Timeline extends CollectionSubView(Document) { onPlay = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); + this.play(); + } + + play = () => { if (this._isPlaying) { this._isPlaying = false; this._playButton = faPlayCircle; @@ -143,6 +157,8 @@ export class Timeline extends CollectionSubView(Document) { } } + + @action windForward = (e: React.MouseEvent) => { e.preventDefault(); @@ -326,7 +342,7 @@ export class Timeline extends CollectionSubView(Document) { } } - timelineContextMenu = (e: React.MouseEvent): void => { + timelineContextMenu = (e:MouseEvent): void => { let subitems: ContextMenuProps[] = []; let timelineContainer = this._timelineWrapper.current!; subitems.push({ @@ -340,13 +356,17 @@ export class Timeline extends CollectionSubView(Document) { }); subitems.push({ description: this._isFrozen ? "Unfreeze Timeline" : "Freeze Timeline", event: action(() => { - if (this._isFrozen) { - this._isFrozen = false; - } else { - this._isFrozen = true; - } + this._isFrozen = !this._isFrozen; }), icon: "thumbtack" }); + subitems.push({ + description: this._timelineVisible ? "Hide Timeline" : "Show Timeline", event: action(() => { + this._timelineVisible = !this._timelineVisible; + }), icon: this._timelineVisible ? faEyeSlash : "eye" + }); + subitems.push({ description: BoolCast(this.props.Document.isAnimating) ? "Enter Play Mode" : "Enter Authoring Mode", event: () => { + BoolCast(this.props.Document.isAnimating) ? this.props.Document.isAnimating = false : this.props.Document.isAnimating = true;} + , icon:BoolCast(this.props.Document.isAnimating) ? "play" : "edit"}); ContextMenu.Instance.addItem({ description: "Timeline Funcs...", subitems: subitems, icon: faClock }); } @@ -364,10 +384,10 @@ export class Timeline extends CollectionSubView(Document) { } render() { return ( -
    -
    +
    +
    -
    +
    {this.timelineToolBox(0.5)}
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d01e5cadc..d10150f30 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -742,7 +742,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } let docviews = docs.filter(doc => doc instanceof Doc).reduce((prev, doc) => { var page = NumCast(doc.page, -1); - if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) { + // if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) { let minim = BoolCast(doc.isMinimized); if (minim === undefined || !minim) { const pos = script ? this.getCalculatedPositions(script, { doc, index: prev.length, collection: this.Document, docs, state }) : @@ -755,7 +755,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { bounds: (pos.x !== undefined && pos.y !== undefined) ? { x: pos.x, y: pos.y, z: pos.z, width: NumCast(pos.width), height: NumCast(pos.height) } : undefined }); } - } + // } return prev; }, elements); @@ -902,7 +902,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { - + {this.overlayChildViews()}
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 28af39fb3..8d33c4570 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -570,7 +570,6 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), this.dataDoc, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); - cm.addItem({ description: BoolCast(this.props.Document.isAnimating) ? "Enter Play Mode" : "Enter Authoring Mode", event: () => {BoolCast(this.props.Document.isAnimating) ? this.props.Document.isAnimating = false : this.props.Document.isAnimating = true;}, icon:BoolCast(this.props.Document.isAnimating) ? "play" : "edit"}); let existingMake = ContextMenu.Instance.findByDescription("Make..."); let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : []; makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); -- cgit v1.2.3-70-g09d2 From 078abf5548a18945527f12dde1a79095bd30e50e Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Fri, 16 Aug 2019 00:08:45 -0400 Subject: partially working zoom --- src/client/views/animationtimeline/Timeline.tsx | 61 +++++++++++++++------- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 ++-- src/client/views/nodes/VideoBox.tsx | 2 + 3 files changed, 49 insertions(+), 23 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index be1ae1773..d0f83676b 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -60,6 +60,9 @@ export class Timeline extends React.Component { @observable private _ticks: number[] = []; @observable private _playButton = faPlayCircle; @observable private _timelineVisible = false; + @observable private _mouseToggled = false; + @observable private _doubleClickEnabled = false; + @computed private get children(): List { @@ -78,8 +81,6 @@ export class Timeline extends React.Component { componentWillMount() { this.props.Document.isAnimating ? this.props.Document.isAnimating = true : this.props.Document.isAnimating = false; - document.addEventListener("contextmenu", (e) => {this.timelineContextMenu(e);}); - console.log(this._currentBarX); } componentDidMount() { @@ -92,7 +93,7 @@ export class Timeline extends React.Component { return NumCast(this.props.Document.curPage); }, curPage => { if (!this._isPlaying) { - this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); + this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); this.props.Document.curPage = this._currentBarX; this.play(); } @@ -107,21 +108,17 @@ export class Timeline extends React.Component { this._ticks = []; for (let i = 0; i < this._time;) { this._ticks.push(i); - i += this._tickIncrement; + i += 1000; } - let trackbox = this._trackbox.current!; - this._totalLength = this._tickSpacing * this._ticks.length; - trackbox.style.width = `${this._totalLength}`; - this._scrubberbox.current!.style.width = `${this._totalLength}`; + this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); }, {fireImmediately:true}); + this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; - this._visibleStart = this._infoContainer.current!.scrollLeft; + this._visibleStart = this._infoContainer.current!.scrollLeft; }); - } - - + } @action changeCurrentBarX = (pixel: number) => { @@ -136,6 +133,7 @@ export class Timeline extends React.Component { this.play(); } + @action play = () => { if (this._isPlaying) { this._isPlaying = false; @@ -209,8 +207,6 @@ export class Timeline extends React.Component { - @observable private _mouseToggled = false; - @observable private _doubleClickEnabled = false; @action onPanDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -370,6 +366,35 @@ export class Timeline extends React.Component { ContextMenu.Instance.addItem({ description: "Timeline Funcs...", subitems: subitems, icon: faClock }); } + @action + onWheelZoom = (e: React.WheelEvent) => { + e.preventDefault(); + e.stopPropagation(); + e.deltaY < 0 ? this.zoom(true) : this.zoom(false); + } + + @action + zoom = (dir: boolean) => { + if (dir){ + if (!(this._tickSpacing === 100 && this._tickIncrement === 1000)){ + if (this._tickSpacing >= 100) { + this._tickIncrement /= 2; + this._tickSpacing = 50; + this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); //CONSIDER THIS MUST CHANGE + } else { + this._tickSpacing += 10; + } + } + } else { + if (this._tickSpacing <= 50) { + this._tickSpacing = 100; + this._tickIncrement *= 2; + this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); //CONSIDER THIS MUST CHANGE + } else { + this._tickSpacing -= 10; + } + } + } private timelineToolBox = (scale:number) => { let size = 50 * scale; //50 is default @@ -389,16 +414,16 @@ export class Timeline extends React.Component {
    {this.timelineToolBox(0.5)} -
    -
    +
    +
    {this._ticks.map(element => { - return

    {this.toReadTime(element)}

    ; + if(element % this._tickIncrement === 0) return

    {this.toReadTime(element)}

    ; })}
    -
    +
    {DocListCast(this.children).map(doc => )}
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d10150f30..b932db424 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -194,6 +194,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _lastY: number = 0; private get _pwidth() { return this.props.PanelWidth(); } private get _pheight() { return this.props.PanelHeight(); } + private _timelineRef = React.createRef(); private inkKey = "ink"; constructor(props: any) { @@ -842,6 +843,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; analyzers.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" }); !existingAnalyze && ContextMenu.Instance.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" }); + this._timelineRef.current!.timelineContextMenu(e.nativeEvent); } @@ -879,10 +881,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { addOverlay("arrangeInit", { x: 400, y: 100, width: 400, height: 300, title: "Layout Initialization" }, { collection: "Doc", docs: "Doc[]" }, undefined); addOverlay("arrangeScript", { x: 400, y: 500, width: 400, height: 300, title: "Layout Script" }, { doc: "Doc", index: "number", collection: "Doc", state: "any", docs: "Doc[]" }, "{x: number, y: number, width?: number, height?: number}"); }; - } - private _timeline = ; - se = () => { - } + } render() { const easing = () => this.props.Document.panTransformType === "Ease"; Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); @@ -902,7 +901,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { - + {this.overlayChildViews()}
    diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 704030d85..a33155fff 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -166,6 +166,8 @@ export class VideoBox extends DocComponent(VideoD this.Document.height = FieldValue(this.Document.width, 0) / youtubeaspect; } } + + this.player && (this.player.style.transform = ""); } componentWillUnmount() { -- cgit v1.2.3-70-g09d2 From 8ab017ac1928db4a9a9f8eb6b579245814cde725 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Fri, 16 Aug 2019 17:26:43 -0400 Subject: zooming fixes --- src/client/views/animationtimeline/Keyframe.tsx | 169 +++++++++++++----------- src/client/views/animationtimeline/Timeline.tsx | 35 ++--- src/client/views/animationtimeline/Track.tsx | 49 +++++-- 3 files changed, 139 insertions(+), 114 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 4e58b07d8..b02d89bf0 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -82,16 +82,30 @@ export namespace KeyframeFunc { }; export const defaultKeyframe = () => { - let regiondata = new Doc(); //creating regiondata - regiondata.duration = 200; + let regiondata = new Doc(); //creating regiondata in MILI + regiondata.duration = 4000; regiondata.position = 0; - regiondata.fadeIn = 20; - regiondata.fadeOut = 20; + regiondata.fadeIn = 1000; + regiondata.fadeOut = 1000; regiondata.functions = new List(); return regiondata; }; - + export const convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time", tickSpacing:number, tickIncrement:number) => { + let time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement; + switch (unit) { + case "mili": + return time; + case "sec": + return dir === "pixel" ? time / 1000 : time * 1000; + case "min": + return dir === "pixel" ? time / 60000 : time * 60000; + case "hr": + return dir === "pixel" ? time / 3600000 : time * 3600000; + default: + return time; + } + }; } export const RegionDataSchema = createSchema({ @@ -109,6 +123,9 @@ interface IProps { node: Doc; RegionData: Doc; collection: Doc; + tickSpacing: number; + tickIncrement: number; + time: number; changeCurrentBarX: (x: number) => void; transform: Transform; } @@ -118,6 +135,8 @@ export class Keyframe extends React.Component { @observable private _bar = React.createRef(); @observable private _gain = 20; //default + @observable private _mouseToggled = false; + @observable private _doubleClickEnabled = false; @computed private get regiondata() { @@ -155,6 +174,7 @@ export class Keyframe extends React.Component { }); return last; } + @computed private get keyframes(){ return DocListCast(this.regiondata.keyframes); @@ -171,6 +191,26 @@ export class Keyframe extends React.Component { } } + @computed + private get pixelPosition(){ + return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + } + + @computed + private get pixelDuration(){ + return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + } + + @computed + private get pixelFadeIn() { + return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + } + + @computed + private get pixelFadeOut(){ + return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + } + async componentWillMount() { if (!this.regiondata.keyframes) { this.regiondata.keyframes = new List(); @@ -183,20 +223,6 @@ export class Keyframe extends React.Component { (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 @@ -236,8 +262,7 @@ export class Keyframe extends React.Component { return TK; } - @observable private _mouseToggled = false; - @observable private _doubleClickEnabled = false; + @action onBarPointerDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -247,14 +272,14 @@ export class Keyframe extends React.Component { this.createKeyframe(clientX); this._doubleClickEnabled = false; } else { - setTimeout(() => {if(!this._mouseToggled && this._doubleClickEnabled)this.props.changeCurrentBarX(this.regiondata.position + (clientX - this._bar.current!.getBoundingClientRect().left) * this.props.transform.Scale); + setTimeout(() => {if(!this._mouseToggled && this._doubleClickEnabled)this.props.changeCurrentBarX(this.pixelPosition + (clientX - this._bar.current!.getBoundingClientRect().left) * this.props.transform.Scale); this._mouseToggled = false; this._doubleClickEnabled = false; }, 200); this._doubleClickEnabled = true; document.addEventListener("pointermove", this.onBarPointerMove); document.addEventListener("pointerup", (e: PointerEvent) => { document.removeEventListener("pointermove", this.onBarPointerMove); - }); + }); } } @@ -269,23 +294,20 @@ export class Keyframe extends React.Component { 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; + let futureX = this.regiondata.position + KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); 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 if ((right && right.position <= futureX + this.pixelDuration)) { + this.regiondata.position = right.position - this.pixelDuration; } 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(); + } + let movement = this.regiondata.position - prevX; + this.keyframes.forEach(kf => { + kf.time = NumCast(kf.time) + movement; + }); } @action @@ -313,11 +335,11 @@ export class Keyframe extends React.Component { e.preventDefault(); e.stopPropagation(); let bar = this._bar.current!; - let offset = Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale); + let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); 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); + let dif = NumCast(firstkf!.time) - (this.pixelPosition + this.pixelFadeIn); 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 @@ -331,6 +353,7 @@ export class Keyframe extends React.Component { this.regiondata.duration -= offset; this.regiondata.position += offset; } + } @@ -339,7 +362,7 @@ export class Keyframe extends React.Component { e.preventDefault(); e.stopPropagation(); let bar = this._bar.current!; - let offset = Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale); + let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); 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); @@ -352,27 +375,18 @@ export class Keyframe extends React.Component { } else { this.regiondata.duration += offset; } - } - - createDivider = (type?: KeyframeFunc.Direction): JSX.Element => { - if (type === "left") { - return
    ; - } else if (type === "right") { - return
    ; - } - return
    ; + } @action createKeyframe = async (clientX:number) => { this._mouseToggled = true; let bar = this._bar.current!; - let offset = Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale); + let offset = KeyframeFunc.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); 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); + let position = 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 + this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied } } @@ -381,7 +395,7 @@ export class Keyframe extends React.Component { moveKeyframe = async (e: React.MouseEvent, kf: Doc) => { e.preventDefault(); e.stopPropagation(); - this.props.changeCurrentBarX(NumCast(kf.time!)); + this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(NumCast(kf.time!), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); } @@ -390,33 +404,38 @@ export class Keyframe extends React.Component { e.preventDefault(); e.stopPropagation(); this.props.node.backgroundColor = "#000000"; - } + + @action + makeKeyframeMenu = (kf :Doc, e:MouseEvent) => { + let items = [ + TimelineMenu.Instance.addItem("button", "Show Data", () => { + runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); + CollectionDockingView.Instance.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); + }), + TimelineMenu.Instance.addItem("button", "Delete", () => {}), + TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}) + ]; + TimelineMenu.Instance.addMenu("Keyframe", items); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); +} @action private createKeyframeJSX = (kf: Doc, type = KeyframeFunc.KeyframeType.default) => { if (type === KeyframeFunc.KeyframeType.default) { return ( -
    - {this.createDivider()} +
    +
    { this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); - let items = [ - TimelineMenu.Instance.addItem("button", "Show Data", () => { - runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); - CollectionDockingView.Instance.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); - }), - TimelineMenu.Instance.addItem("button", "Delete", () => {}), - TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}) - ]; - TimelineMenu.Instance.addMenu("Keyframe", items); - TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + this.makeKeyframeMenu(kf, e.nativeEvent); }}>
    -
    ); +
    + ); } return ( -
    - {this.createDivider()} +
    +
    ); } @@ -460,9 +479,6 @@ export class Keyframe extends React.Component { } - - - @action onReactionListen = (e: PointerEvent) => { e.preventDefault(); @@ -514,7 +530,6 @@ export class Keyframe extends React.Component { (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; @@ -525,19 +540,21 @@ export class Keyframe extends React.Component { render() { return (
    -
    - {this.regiondata.keyframes!.map(kf => { - return this.createKeyframeJSX(kf as Doc, (kf! as Doc).type as KeyframeFunc.KeyframeType); - })} + {this.keyframes.map(kf => { this.createKeyframeJSX(kf, kf.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(); + let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + let leftPos = KeyframeFunc.convertPixelTime(NumCast(left!.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); return ( -
    { this.onContainerOver(e, bodyRef); }} onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} onContextMenu={(e) => { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index d0f83676b..036c3ad9b 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -110,7 +110,7 @@ export class Timeline extends React.Component { this._ticks.push(i); i += 1000; } - this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); + this._totalLength = this._tickSpacing * (this._time/ this._tickIncrement); }, {fireImmediately:true}); this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; @@ -322,22 +322,6 @@ export class Timeline extends React.Component { } - convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time") => { - let time = dir === "pixel" ? pos / this._tickSpacing * this._tickIncrement : pos * this._tickSpacing / this._tickIncrement; - switch (unit) { - case "mili": - return time; - case "sec": - return dir === "pixel" ? time / 1000 : time * 1000; - case "min": - return dir === "pixel" ? time / 60000 : time * 60000; - case "hr": - return dir === "pixel" ? time / 3600000 : time * 3600000; - default: - return time; - } - } - timelineContextMenu = (e:MouseEvent): void => { let subitems: ContextMenuProps[] = []; let timelineContainer = this._timelineWrapper.current!; @@ -380,20 +364,21 @@ export class Timeline extends React.Component { if (this._tickSpacing >= 100) { this._tickIncrement /= 2; this._tickSpacing = 50; - this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); //CONSIDER THIS MUST CHANGE } else { this._tickSpacing += 10; } } } else { - if (this._tickSpacing <= 50) { - this._tickSpacing = 100; - this._tickIncrement *= 2; - this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); //CONSIDER THIS MUST CHANGE - } else { - this._tickSpacing -= 10; + if (this._totalLength >= this._infoContainer.current!.getBoundingClientRect().width){ + if (this._tickSpacing <= 50) { + this._tickSpacing = 100; + this._tickIncrement *= 2; + } else { + this._tickSpacing -= 10; + } } } + this._totalLength = this._tickSpacing * (this._time/ this._tickIncrement); } private timelineToolBox = (scale:number) => { @@ -424,7 +409,7 @@ export class Timeline extends React.Component {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => )}
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index d91954022..5a43f5b2a 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -10,12 +10,17 @@ import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; import { FlyoutProps } from "./Timeline"; import { Transform } from "../../util/Transform"; import { RichTextField } from "../../../new_fields/RichTextField"; +import { createObjectBindingPattern } from "typescript"; +import { DateField } from "../../../new_fields/DateField"; interface IProps { node: Doc; currentBarX: number; transform: Transform; collection: Doc; + time: number; + tickIncrement: number; + tickSpacing: number; changeCurrentBarX: (x: number) => void; } @@ -44,7 +49,7 @@ export class Track extends React.Component { componentDidMount() { runInAction(() => { this._currentBarXReaction = this.currentBarXReaction(); - if (this.regions.length === 0) this.createRegion(this.props.currentBarX); + if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); this.props.node.hidden = false; }); } @@ -62,17 +67,33 @@ export class Track extends React.Component { let kf = keyframes[kfIndex] as Doc; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); - 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 + let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); // lef keyframe, if it exists + let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); //right keyframe, if it exists + // while (leftkf !== undefined) { + // if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { + // let edge:(Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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; + // } else if (leftkf!.key ) { + // leftkf!.key = Doc.MakeCopy(kf.key as Doc, true); + // } + + // } + + + + if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades - let edge:(Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, this.props.currentBarX, leftkf!); + let edge:(Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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!); + let edge:(Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!,KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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; @@ -88,10 +109,10 @@ export class Track extends React.Component { @action currentBarXReaction = () => { return reaction(() => this.props.currentBarX, async () => { - let regiondata: (Doc | undefined) = await this.findRegion(this.props.currentBarX); + let regiondata: (Doc | undefined) = await this.findRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); if (regiondata) { this.props.node.hidden = false; - await this.timeChange(this.props.currentBarX); + await this.timeChange(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } else { this.props.node.hidden = true; this.props.node.opacity = 0; @@ -107,8 +128,8 @@ export class Track extends React.Component { } 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 leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); // lef keyframe, if it exists + let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); //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); @@ -139,6 +160,8 @@ export class Track extends React.Component { this.props.node[key] = new RichTextField(nodeData); } } else if (key === "creationDate") { + + this.props.node[key] = new DateField(); } else { this.props.node[key] = kfNode[key]; } @@ -162,7 +185,7 @@ export class Track extends React.Component { 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; + if (NumCast(kf.time) === Math.round(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement))) currentkf = kf; }); return currentkf; } @@ -173,7 +196,7 @@ export class Track extends React.Component { 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 + const timeratio = (KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement) - NumCast(left.time)) / dif_time; //linear let keyframes = (await DocListCastAsync(regiondata.keyframes!))!; let indexLeft = keyframes.indexOf(left); let interY:List = (await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List)!; @@ -251,7 +274,7 @@ export class Track extends React.Component { 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); + this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } createRegion = (position: number) => { @@ -275,7 +298,7 @@ export class Track extends React.Component {
    {DocListCast(this.regions).map((region) => { - return ; + return ; })}
    -- cgit v1.2.3-70-g09d2 From 1b7e8873ead3cc15349bd4c9e669f3b1edcbbc2b Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Mon, 19 Aug 2019 00:25:14 -0400 Subject: updates --- src/client/views/MainView.tsx | 6 +-- src/client/views/animationtimeline/Keyframe.tsx | 59 ++++++++++++---------- src/client/views/animationtimeline/Timeline.tsx | 51 +++++++++++-------- .../views/animationtimeline/TimelineMenu.tsx | 20 ++++---- src/client/views/animationtimeline/Track.tsx | 40 ++++----------- 5 files changed, 87 insertions(+), 89 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d5482bc12..126760234 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -206,9 +206,9 @@ export class MainView extends React.Component { if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { ContextMenu.Instance.closeMenu(); } - // if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-container") === -1) { - // TimelineMenu.Instance.closeMenu(); - // } + if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-desc") === -1 || targets[0].className.toString().indexOf("timeline-menu-item") === -1 || targets[0].className.toString().indexOf("timeline-menu-item") === -1 || targets[0].className.toString().indexOf("timeline-menu-input") === -1){ + TimelineMenu.Instance.closeMenu(); + } }); globalPointerUp = () => this.isPointerDown = false; diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index b02d89bf0..2f0a968b9 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -28,7 +28,7 @@ export namespace KeyframeFunc { let leftMost: (RegionData | undefined) = undefined; let rightMost: (RegionData | undefined) = undefined; DocListCast(regions).forEach(region => { - let neighbor = RegionData(region as Doc); + let neighbor = RegionData(region); if (currentRegion.position! > neighbor.position) { if (!leftMost || neighbor.position > leftMost.position) { leftMost = neighbor; @@ -230,7 +230,6 @@ export class Keyframe extends React.Component { 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; @@ -299,8 +298,8 @@ export class Keyframe extends React.Component { 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.pixelDuration)) { - this.regiondata.position = right.position - this.pixelDuration; + } else if ((right && right.position <= futureX + this.regiondata.duration)) { + this.regiondata.position = right.position - this.regiondata.duration; } else { this.regiondata.position = futureX; } @@ -353,7 +352,8 @@ export class Keyframe extends React.Component { this.regiondata.duration -= offset; this.regiondata.position += offset; } - + this.keyframes[0].time = this.regiondata.position; + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; } @@ -375,6 +375,8 @@ export class Keyframe extends React.Component { } else { this.regiondata.duration += offset; } + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; + this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; } @@ -408,24 +410,36 @@ export class Keyframe extends React.Component { @action makeKeyframeMenu = (kf :Doc, e:MouseEvent) => { - let items = [ - TimelineMenu.Instance.addItem("button", "Show Data", () => { - runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); + + TimelineMenu.Instance.addItem("button", "Show Data", () => { + runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); CollectionDockingView.Instance.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); - }), - TimelineMenu.Instance.addItem("button", "Delete", () => {}), - TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}) - ]; - TimelineMenu.Instance.addMenu("Keyframe", items); + }), + TimelineMenu.Instance.addItem("button", "Delete", () => {}), + TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}); + + TimelineMenu.Instance.addMenu("Keyframe"); TimelineMenu.Instance.openMenu(e.clientX, e.clientY); -} + } + + @action + makeRegionMenu = (kf: Doc, e: MouseEvent) => { + TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), + TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), + TimelineMenu.Instance.addItem("input", "fadeIn", (val) => {this.regiondata.fadeIn = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "fadeOut", (val) => {this.regiondata.fadeOut = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "position", (val) => {this.regiondata.position = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", "duration", (val) => {this.regiondata.duration = parseInt(val, 10);}), + TimelineMenu.Instance.addMenu("Region"); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + } @action private createKeyframeJSX = (kf: Doc, type = KeyframeFunc.KeyframeType.default) => { if (type === KeyframeFunc.KeyframeType.default) { return (
    -
    { this.moveKeyframe(e, kf as Doc); }} onContextMenu={(e: React.MouseEvent) => { +
    { this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); this.makeKeyframeMenu(kf, e.nativeEvent); @@ -546,7 +560,9 @@ export class Keyframe extends React.Component { onPointerDown={this.onBarPointerDown}>
    - {this.keyframes.map(kf => { this.createKeyframeJSX(kf, kf.type as KeyframeFunc.KeyframeType); })} + {this.keyframes.map(kf => { + return this.createKeyframeJSX(kf, kf.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]; @@ -561,16 +577,7 @@ export class Keyframe extends React.Component { e.preventDefault(); e.stopPropagation(); this._mouseToggled = true; - let items = [ - TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), - TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), - TimelineMenu.Instance.addItem("input", "fadeIn", (val) => {this.regiondata.fadeIn = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "fadeOut", (val) => {this.regiondata.fadeOut = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "position", (val) => {this.regiondata.position = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "duration", (val) => {this.regiondata.duration = parseInt(val, 10);}), - ]; - TimelineMenu.Instance.addMenu("Region", items); - TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + this.makeRegionMenu(kf, e.nativeEvent); }}>
    ); diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 036c3ad9b..4eb5958b5 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -1,7 +1,6 @@ import * as React from "react"; import "./Timeline.scss"; -import { CollectionSubView } from "../collections/CollectionSubView"; -import { Document, listSpec } from "../../../new_fields/Schema"; +import { listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; import { observable, reaction, action, IReactionDisposer, computed, runInAction, observe } from "mobx"; @@ -13,8 +12,8 @@ import { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDow import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; -import { playcustomapp } from "googleapis/build/src/apis/playcustomapp"; -import { FieldView, FieldViewProps } from "../nodes/FieldView"; +import { FieldViewProps } from "../nodes/FieldView"; +import { KeyframeFunc } from "./Keyframe"; @@ -62,6 +61,8 @@ export class Timeline extends React.Component { @observable private _timelineVisible = false; @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; + @observable private _mutationDisposer:MutationObserver[] = []; + @observable private _reactionDisposer:IReactionDisposer[] = []; @computed @@ -89,7 +90,7 @@ export class Timeline extends React.Component { console.log(this.props.Document.duration); if (this.props.Document.duration) { this._time = Math.round(NumCast(this.props.Document.duration)) * 1000; - reaction(() => { + this._reactionDisposer.push(reaction(() => { return NumCast(this.props.Document.curPage); }, curPage => { if (!this._isPlaying) { @@ -97,12 +98,11 @@ export class Timeline extends React.Component { this.props.Document.curPage = this._currentBarX; this.play(); } - - }); + })); } } runInAction(() => { - reaction(() => { + this._reactionDisposer.push(reaction(() => { return this._time; }, () => { this._ticks = []; @@ -111,7 +111,7 @@ export class Timeline extends React.Component { i += 1000; } this._totalLength = this._tickSpacing * (this._time/ this._tickIncrement); - }, {fireImmediately:true}); + }, {fireImmediately:true})); this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; this._visibleStart = this._infoContainer.current!.scrollLeft; @@ -321,7 +321,6 @@ export class Timeline extends React.Component { return `${min}:${sec}`; } - timelineContextMenu = (e:MouseEvent): void => { let subitems: ContextMenuProps[] = []; let timelineContainer = this._timelineWrapper.current!; @@ -354,31 +353,41 @@ export class Timeline extends React.Component { onWheelZoom = (e: React.WheelEvent) => { e.preventDefault(); e.stopPropagation(); + let offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left; + let prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement); e.deltaY < 0 ? this.zoom(true) : this.zoom(false); + let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); + this._infoContainer.current!.scrollLeft = currPixel - offset; + this._visibleStart = currPixel - offset; } @action zoom = (dir: boolean) => { + let spacingChange = this._tickSpacing; + let incrementChange = this._tickIncrement; if (dir){ if (!(this._tickSpacing === 100 && this._tickIncrement === 1000)){ if (this._tickSpacing >= 100) { - this._tickIncrement /= 2; - this._tickSpacing = 50; + incrementChange /= 2; + spacingChange = 50; } else { - this._tickSpacing += 10; + spacingChange += 5; } } } else { - if (this._totalLength >= this._infoContainer.current!.getBoundingClientRect().width){ - if (this._tickSpacing <= 50) { - this._tickSpacing = 100; - this._tickIncrement *= 2; - } else { - this._tickSpacing -= 10; - } + if (this._tickSpacing <= 50) { + spacingChange = 100; + incrementChange *= 2; + } else { + spacingChange -= 5; } } - this._totalLength = this._tickSpacing * (this._time/ this._tickIncrement); + let finalLength = spacingChange * (this._time / incrementChange); + if (finalLength >= this._infoContainer.current!.getBoundingClientRect().width){ + this._totalLength = finalLength; + this._tickSpacing = spacingChange; + this._tickIncrement = incrementChange; + } } private timelineToolBox = (scale:number) => { diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 3e63eec61..572b35b90 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -31,31 +31,31 @@ export class TimelineMenu extends React.Component { closeMenu = () => { this._opacity = 0; this._currentMenu = []; + this._x = -1000000; + this._y = -1000000; } + @action addItem = (type: "input" | "button", title: string, event: (e:any) => void) => { if (type === "input"){ - let ref = React.createRef(); - return
    { + let inputRef = React.createRef(); + this._currentMenu.push(
    { let text = e.target.value; document.addEventListener("keypress", (e:KeyboardEvent) => { if (e.keyCode === 13) { event(text); } }); - }}/>
    ; + }}/>
    ); } else if (type === "button") { - let ref = React.createRef(); - return

    {title}

    ; + let buttonRef = React.createRef(); + this._currentMenu.push(

    {title}

    ); } - return
    ; } @action - addMenu = (title:string, items: JSX.Element[]) => { - items.unshift(

    {title}

    ); - this._currentMenu = items; - + addMenu = (title:string) => { + this._currentMenu.unshift(

    {title}

    ); } render() { diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 5a43f5b2a..13fe55e80 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,16 +1,14 @@ 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 { observable, reaction, action, IReactionDisposer, computed, 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 { RichTextField } from "../../../new_fields/RichTextField"; -import { createObjectBindingPattern } from "typescript"; import { DateField } from "../../../new_fields/DateField"; interface IProps { @@ -68,23 +66,7 @@ export class Track extends React.Component { if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); //right keyframe, if it exists - // while (leftkf !== undefined) { - // if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { - // let edge:(Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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; - // } else if (leftkf!.key ) { - // leftkf!.key = Doc.MakeCopy(kf.key as Doc, true); - // } - - // } - - - - + let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), leftkf!); edge!.key = Doc.MakeCopy(kf.key as Doc, true); @@ -160,7 +142,6 @@ export class Track extends React.Component { this.props.node[key] = new RichTextField(nodeData); } } else if (key === "creationDate") { - this.props.node[key] = new DateField(); } else { this.props.node[key] = kfNode[key]; @@ -175,9 +156,9 @@ export class Track extends React.Component { @action private filterKeys = (keys: string[]): string[] => { return keys.reduce((acc: string[], key: string) => { - if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns") acc.push(key); + if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns" && key !== "creationDate") acc.push(key); return acc; - }, []) as string[]; + }, []); } @action @@ -280,15 +261,16 @@ export class Track extends React.Component { 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)) { + + if (rightRegion && rightRegion.position - regiondata.position <= 4000) { regiondata.duration = rightRegion.position - regiondata.position; + } + if(this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))){ + this.regions.push(regiondata); + return regiondata; } - this.regions.push(regiondata); - return regiondata; + } -- cgit v1.2.3-70-g09d2 From c7678db105f952e7562f1b573266fb295e13cf7b Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Fri, 6 Sep 2019 11:00:37 -0400 Subject: initial transfer commit --- src/client/views/animationtimeline/Track.tsx | 175 ++++++++++++++------------- 1 file changed, 90 insertions(+), 85 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 13fe55e80..8f0e2d1cc 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -10,15 +10,16 @@ import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; import { Transform } from "../../util/Transform"; import { RichTextField } from "../../../new_fields/RichTextField"; import { DateField } from "../../../new_fields/DateField"; +import { Copy } from "../../../new_fields/FieldSymbols"; interface IProps { node: Doc; currentBarX: number; transform: Transform; - collection: Doc; - time: number; - tickIncrement: number; - tickSpacing: number; + collection: Doc; + time: number; + tickIncrement: number; + tickSpacing: number; changeCurrentBarX: (x: number) => void; } @@ -26,11 +27,11 @@ interface IProps { export class Track extends React.Component { @observable private _inner = React.createRef(); @observable private _reactionDisposers: IReactionDisposer[] = []; - @observable private _currentBarXReaction: any; - @observable private _isOnKeyframe: boolean = false; - @observable private _onKeyframe: (Doc | undefined) = undefined; - @observable private _onRegionData : ( Doc | undefined) = undefined; - @observable private _leftCurrKeyframe: (Doc | undefined) = undefined; + @observable private _currentBarXReaction: any; + @observable private _isOnKeyframe: boolean = false; + @observable private _onKeyframe: (Doc | undefined) = undefined; + @observable private _onRegionData: (Doc | undefined) = undefined; + @observable private _leftCurrKeyframe: (Doc | undefined) = undefined; @computed private get regions() { @@ -59,36 +60,36 @@ export class Track extends React.Component { } @action - saveKeyframe = async (ref:Doc, regiondata:Doc) => { - let keyframes:List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); - let kfIndex:number = keyframes.indexOf(ref); - let kf = keyframes[kfIndex] as Doc; + saveKeyframe = async (ref: Doc, regiondata: Doc) => { + let keyframes: List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); + let kfIndex: number = keyframes.indexOf(ref); + let kf = keyframes[kfIndex] as Doc; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); // lef keyframe, if it exists let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), leftkf!); + let edge: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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!,KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), rightkf!); + let edge: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), 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; - } + } } - keyframes[kfIndex] = kf; - this._onKeyframe = undefined; - this._onRegionData = undefined; - this._isOnKeyframe = false; + keyframes[kfIndex] = kf; + this._onKeyframe = undefined; + this._onRegionData = undefined; + this._isOnKeyframe = false; } - - @action + + @action currentBarXReaction = () => { return reaction(() => this.props.currentBarX, async () => { let regiondata: (Doc | undefined) = await this.findRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); @@ -96,8 +97,8 @@ export class Track extends React.Component { this.props.node.hidden = false; await this.timeChange(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } else { - this.props.node.hidden = true; - this.props.node.opacity = 0; + this.props.node.hidden = true; + this.props.node.opacity = 0; } }, { fireImmediately: true }); } @@ -105,8 +106,8 @@ export class Track extends React.Component { @action timeChange = async (time: number) => { - if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { - await this.saveKeyframe(this._onKeyframe, this._onRegionData); + if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { + await this.saveKeyframe(this._onKeyframe, this._onRegionData); } let regiondata = await this.findRegion(Math.round(time)); //finds a region that the scrubber is on if (regiondata) { @@ -115,10 +116,10 @@ export class Track extends React.Component { let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { await this.applyKeys(currentkf); - this._leftCurrKeyframe = currentkf; - this._isOnKeyframe = true; - this._onKeyframe = currentkf; - this._onRegionData = regiondata; + this._leftCurrKeyframe = currentkf; + this._isOnKeyframe = true; + this._onKeyframe = currentkf; + this._onRegionData = regiondata; } else if (leftkf && rightkf) { await this.interpolate(leftkf, rightkf, regiondata); } @@ -127,28 +128,32 @@ export class Track extends React.Component { @action private applyKeys = async (kf: Doc) => { - let kfNode = await Cast(kf.key, Doc) as Doc; - let docFromApply = kfNode; - console.log(Doc.allKeys(docFromApply)); - if (this.filterKeys(Doc.allKeys(this.props.node)).length > this.filterKeys(Doc.allKeys(kfNode)).length) docFromApply = this.props.node; + let kfNode = await Cast(kf.key, Doc) as Doc; + let docFromApply = kfNode; + console.log(Doc.allKeys(docFromApply)); + 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 => { console.log(key); if (!kfNode[key]) { - this.props.node[key] = undefined; + this.props.node[key] = undefined; } else { if (key === "data") { - if (this.props.node.type === "text"){ - let nodeData = (kfNode[key] as RichTextField).Data; - this.props.node[key] = new RichTextField(nodeData); + if (this.props.node.type === "text") { + let nodeData = (kfNode[key] as RichTextField).Data; + this.props.node[key] = new RichTextField(nodeData); } } else if (key === "creationDate") { - this.props.node[key] = new DateField(); - } else { - this.props.node[key] = kfNode[key]; + this.props.node[key] = new DateField(); + } else { + let stored = kfNode[key]; + if (stored instanceof DateField) { + stored = stored[Copy](); + } + this.props.node[key] = stored; } } - + }); } @@ -156,7 +161,7 @@ export class Track extends React.Component { @action private filterKeys = (keys: string[]): string[] => { return keys.reduce((acc: string[], key: string) => { - if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns" && key !== "creationDate") acc.push(key); + if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns" && key !== "creationDate") acc.push(key); return acc; }, []); } @@ -164,57 +169,57 @@ export class Track extends React.Component { @action calcCurrent = async (region: Doc) => { let currentkf: (Doc | undefined) = undefined; - let keyframes = await DocListCastAsync(region.keyframes!); + let keyframes = await DocListCastAsync(region.keyframes!); keyframes!.forEach((kf) => { if (NumCast(kf.time) === Math.round(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement))) currentkf = kf; }); return currentkf; } - + @action - interpolate = async (left: Doc, right: Doc, regiondata:Doc) => { + interpolate = async (left: Doc, right: Doc, regiondata: Doc) => { let leftNode = left.key as Doc; let rightNode = right.key as Doc; const dif_time = NumCast(right.time) - NumCast(left.time); const timeratio = (KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement) - NumCast(left.time)) / dif_time; //linear - let keyframes = (await DocListCastAsync(regiondata.keyframes!))!; - let indexLeft = keyframes.indexOf(left); - let interY:List = (await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List)!; - let realIndex = (interY.length - 1) * timeratio; - let xIndex = Math.floor(realIndex); - let yValue = interY[xIndex]; - let secondYOffset:number = yValue; + let keyframes = (await DocListCastAsync(regiondata.keyframes!))!; + let indexLeft = keyframes.indexOf(left); + let interY: List = (await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List)!; + 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 = await ((regiondata.functions as List)[indexLeft] as Doc).pathX as List; - let pathY:List = await ((regiondata.functions as List)[indexLeft] as Doc).pathY as List; - let proposedX = 0; - let proposedY = 0; + secondYOffset = interY[xIndex] + ((realIndex - xIndex) / 1) * (interY[xIndex + 1] - interY[xIndex]) - minY; + } + let finalRatio = secondYOffset / (maxY - minY); + let pathX: List = await ((regiondata.functions as List)[indexLeft] as Doc).pathX as List; + let pathY: List = await ((regiondata.functions as List)[indexLeft] as Doc).pathY as List; + let proposedX = 0; + let proposedY = 0; if (pathX.length !== 0) { - let realPathCorrespondingIndex = finalRatio * (pathX.length - 1); - let pathCorrespondingIndex = Math.floor(realPathCorrespondingIndex); + 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]; + 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]); + 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; + if ((key === "x" || key === "y") && pathX.length !== 0) { + if (key === "x") this.props.node[key] = proposedX; + if (key === "y") this.props.node[key] = proposedY; } else { const diff = NumCast(rightNode[key]) - NumCast(leftNode[key]); const adjusted = diff * finalRatio; @@ -222,13 +227,13 @@ export class Track extends React.Component { } } else { if (key === "data") { - if (this.props.node.type === "text"){ - let nodeData = StrCast((leftNode[key] as RichTextField).Data); - let currentNodeData = StrCast((this.props.node[key] as RichTextField).Data); + if (this.props.node.type === "text") { + let nodeData = StrCast((leftNode[key] as RichTextField).Data); + let currentNodeData = StrCast((this.props.node[key] as RichTextField).Data); if (nodeData !== currentNodeData) { - this.props.node[key] = new RichTextField(nodeData); + this.props.node[key] = new RichTextField(nodeData); } - } + } } else if (key === "creationDate") { } else { @@ -239,9 +244,9 @@ export class Track extends React.Component { } @action - findRegion = async (time: number) => { - let foundRegion:(Doc | undefined) = undefined; - let regions = await DocListCastAsync(this.regions); + 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))) { @@ -262,15 +267,15 @@ export class Track extends React.Component { let regiondata = KeyframeFunc.defaultKeyframe(); regiondata.position = position; let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); - + if (rightRegion && rightRegion.position - regiondata.position <= 4000) { regiondata.duration = rightRegion.position - regiondata.position; - } - if(this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))){ + } + if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { this.regions.push(regiondata); return regiondata; } - + } @@ -280,7 +285,7 @@ export class Track extends React.Component {
    {DocListCast(this.regions).map((region) => { - return ; + return ; })}
    -- cgit v1.2.3-70-g09d2 From 96551aa8ef23485f48f6090192f18451ff66b557 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 15 Sep 2019 17:01:13 -0400 Subject: changes --- src/client/views/animationtimeline/Keyframe.tsx | 48 +++++----- src/client/views/animationtimeline/Timeline.tsx | 10 +-- src/client/views/animationtimeline/Track.tsx | 100 ++++++++++++--------- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + 4 files changed, 88 insertions(+), 71 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 2f0a968b9..253515dfd 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -410,14 +410,17 @@ export class Keyframe extends React.Component { @action makeKeyframeMenu = (kf :Doc, e:MouseEvent) => { - TimelineMenu.Instance.addItem("button", "Show Data", () => { runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); CollectionDockingView.Instance.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); }), - TimelineMenu.Instance.addItem("button", "Delete", () => {}), + TimelineMenu.Instance.addItem("button", "Delete", () => { + runInAction(() => { + console.log(this.keyframes.indexOf(kf)); + this.keyframes.splice(this.keyframes.indexOf(kf), 1); + }); + }), TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}); - TimelineMenu.Instance.addMenu("Keyframe"); TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } @@ -425,7 +428,8 @@ export class Keyframe extends React.Component { @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), - TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), + TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), + TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{this.regions.splice(this.regions.indexOf(this.regiondata), 1);}), TimelineMenu.Instance.addItem("input", "fadeIn", (val) => {this.regiondata.fadeIn = parseInt(val, 10);}), TimelineMenu.Instance.addItem("input", "fadeOut", (val) => {this.regiondata.fadeOut = parseInt(val, 10);}), TimelineMenu.Instance.addItem("input", "position", (val) => {this.regiondata.position = parseInt(val, 10);}), @@ -435,23 +439,7 @@ export class Keyframe extends React.Component { } @action private createKeyframeJSX = (kf: Doc, type = KeyframeFunc.KeyframeType.default) => { - if (type === KeyframeFunc.KeyframeType.default) { - return ( -
    -
    -
    { this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - this.makeKeyframeMenu(kf, e.nativeEvent); - }}>
    -
    - ); - } - return ( -
    -
    -
    - ); + } onContainerOver = (e: React.PointerEvent, ref: React.RefObject) => { @@ -561,7 +549,23 @@ export class Keyframe extends React.Component {
    {this.keyframes.map(kf => { - return this.createKeyframeJSX(kf, kf.type as KeyframeFunc.KeyframeType); + if (kf.type as KeyframeFunc.KeyframeType === KeyframeFunc.KeyframeType.default) { + return ( +
    +
    +
    { this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + this.makeKeyframeMenu(kf, e.nativeEvent); + }}>
    +
    + ); + } + return ( +
    +
    +
    + ); })} {this.keyframes.map( kf => { if(this.keyframes.indexOf(kf ) !== this.keyframes.length - 1) { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 4eb5958b5..187c9396d 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -3,7 +3,7 @@ import "./Timeline.scss"; import { listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; -import { observable, reaction, action, IReactionDisposer, computed, runInAction, observe } from "mobx"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction, observe, toJS } from "mobx"; import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Doc, DocListCast } from "../../../new_fields/Doc"; @@ -61,14 +61,12 @@ export class Timeline extends React.Component { @observable private _timelineVisible = false; @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; - @observable private _mutationDisposer:MutationObserver[] = []; @observable private _reactionDisposer:IReactionDisposer[] = []; @computed private get children(): List { 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; @@ -86,7 +84,7 @@ export class Timeline extends React.Component { componentDidMount() { if (StrCast(this.props.Document.type) === "video") { - console.log("ran"); + console.log("video"); console.log(this.props.Document.duration); if (this.props.Document.duration) { this._time = Math.round(NumCast(this.props.Document.duration)) * 1000; @@ -358,7 +356,7 @@ export class Timeline extends React.Component { e.deltaY < 0 ? this.zoom(true) : this.zoom(false); let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); this._infoContainer.current!.scrollLeft = currPixel - offset; - this._visibleStart = currPixel - offset; + this._visibleStart = currPixel - offset; } @action @@ -418,7 +416,7 @@ export class Timeline extends React.Component {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => )}
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 8f0e2d1cc..e99da6648 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -8,9 +8,8 @@ import { FieldValue, Cast, NumCast, BoolCast, StrCast } from "../../../new_field import { List } from "../../../new_fields/List"; import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; import { Transform } from "../../util/Transform"; -import { RichTextField } from "../../../new_fields/RichTextField"; -import { DateField } from "../../../new_fields/DateField"; import { Copy } from "../../../new_fields/FieldSymbols"; +import { ObjectField } from "../../../new_fields/ObjectField"; interface IProps { node: Doc; @@ -20,19 +19,20 @@ interface IProps { time: number; tickIncrement: number; tickSpacing: number; + timelineVisible: boolean; changeCurrentBarX: (x: number) => void; } @observer export class Track extends React.Component { @observable private _inner = React.createRef(); - @observable private _reactionDisposers: IReactionDisposer[] = []; @observable private _currentBarXReaction: any; + @observable private _timelineVisibleReaction: any; @observable private _isOnKeyframe: boolean = false; @observable private _onKeyframe: (Doc | undefined) = undefined; @observable private _onRegionData: (Doc | undefined) = undefined; - @observable private _leftCurrKeyframe: (Doc | undefined) = undefined; - + @observable private _storedState: (Doc | undefined) = undefined; + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; @@ -40,22 +40,31 @@ export class Track extends React.Component { componentWillMount() { if (!this.props.node.regions) { - this.props.node.regions = new List(); + this.props.node.regions = new List(); } - this.props.node.opacity = 1; + + } componentDidMount() { - runInAction(() => { + runInAction(async () => { + this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); - this.props.node.hidden = false; + this.props.node.hidden = false; + this.props.node.opacity = 1; + let state = new Doc(); + state.key = Doc.MakeCopy(await this.props.node, true); + console.log(this.props.node.x); + this._storedState = state; }); + } componentWillUnmount() { runInAction(() => { if (this._currentBarXReaction) this._currentBarXReaction(); + if (this._timelineVisibleReaction) this._timelineVisibleReaction(); }); } @@ -89,6 +98,15 @@ export class Track extends React.Component { this._isOnKeyframe = false; } + @action + revertState = () => { + let copyDoc = Doc.MakeCopy(this.props.node, true); + this.applyKeys(this._storedState!); + let newState = new Doc(); + newState.key = copyDoc; + this._storedState = newState; + } + @action currentBarXReaction = () => { return reaction(() => this.props.currentBarX, async () => { @@ -100,9 +118,16 @@ export class Track extends React.Component { this.props.node.hidden = true; this.props.node.opacity = 0; } - }, { fireImmediately: true }); + }); + } + @action + timelineVisibleReaction = () => { + return reaction(() => { + return this.props.timelineVisible; + }, isVisible => { + this.revertState(); + }); } - @action timeChange = async (time: number) => { @@ -116,7 +141,6 @@ export class Track extends React.Component { let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { await this.applyKeys(currentkf); - this._leftCurrKeyframe = currentkf; this._isOnKeyframe = true; this._onKeyframe = currentkf; this._onRegionData = regiondata; @@ -129,39 +153,38 @@ export class Track extends React.Component { @action private applyKeys = async (kf: Doc) => { let kfNode = await Cast(kf.key, Doc) as Doc; - let docFromApply = kfNode; - console.log(Doc.allKeys(docFromApply)); + 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 => { - console.log(key); if (!kfNode[key]) { this.props.node[key] = undefined; } else { - if (key === "data") { - if (this.props.node.type === "text") { - let nodeData = (kfNode[key] as RichTextField).Data; - this.props.node[key] = new RichTextField(nodeData); - } - } else if (key === "creationDate") { - this.props.node[key] = new DateField(); + let stored = kfNode[key]; + if(stored instanceof ObjectField){ + this.props.node[key] = stored[Copy](); } else { - let stored = kfNode[key]; - if (stored instanceof DateField) { - stored = stored[Copy](); - } - this.props.node[key] = stored; + this.props.node[key] = stored; } - } - }); } + private filterList = [ + "regions", + "cursors", + "hidden", + "nativeHeight", + "nativeWidth", + "schemaColumns", + "baseLayout", + "backgroundLayout", + "layout", + ]; @action private filterKeys = (keys: string[]): string[] => { return keys.reduce((acc: string[], key: string) => { - if (key !== "regions" && key !== "cursors" && key !== "hidden" && key !== "nativeHeight" && key !== "nativeWidth" && key !== "schemaColumns" && key !== "creationDate") acc.push(key); + if (!this.filterList.includes(key)) acc.push(key); return acc; }, []); } @@ -226,18 +249,11 @@ export class Track extends React.Component { this.props.node[key] = NumCast(leftNode[key]) + adjusted; } } else { - if (key === "data") { - if (this.props.node.type === "text") { - let nodeData = StrCast((leftNode[key] as RichTextField).Data); - let currentNodeData = StrCast((this.props.node[key] as RichTextField).Data); - if (nodeData !== currentNodeData) { - this.props.node[key] = new RichTextField(nodeData); - } - } - } else if (key === "creationDate") { - + let stored = leftNode[key]; + if(stored instanceof ObjectField){ + this.props.node[key] = stored[Copy](); } else { - this.props.node[key] = leftNode[key]; + this.props.node[key] = stored; } } }); @@ -277,8 +293,6 @@ export class Track extends React.Component { } } - - render() { return (
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d9fc388cd..67d700f1a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -912,6 +912,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (this.childDocs.some(d => BoolCast(d.isTemplate))) { layoutItems.push({ description: "Template Layout Instance", event: () => this.props.addDocTab && this.props.addDocTab(Doc.ApplyTemplate(this.props.Document)!, undefined, "onRight"), icon: "project-diagram" }); } + this._timelineRef.current!.timelineContextMenu(e.nativeEvent); layoutItems.push({ description: "reset view", event: () => { this.props.Document.panX = this.props.Document.panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); layoutItems.push({ description: `${this.fitToBox ? "Unset" : "Set"} Fit To Container`, event: this.fitToContainer, icon: !this.fitToBox ? "expand-arrows-alt" : "compress-arrows-alt" }); layoutItems.push({ -- cgit v1.2.3-70-g09d2 From 53c4f6ddad5534101d7a7482332cddb02ba99c21 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 17 Sep 2019 18:01:36 -0400 Subject: zoom, contextmenu fix --- src/client/views/MainView.tsx | 3 +-- src/client/views/animationtimeline/Timeline.tsx | 7 +++++-- src/client/views/animationtimeline/TimelineMenu.tsx | 6 +++++- src/client/views/animationtimeline/Track.tsx | 6 +----- src/new_fields/Doc.ts | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6331763f1..139ac11db 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -206,8 +206,7 @@ export class MainView extends React.Component { if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { ContextMenu.Instance.closeMenu(); } - console.log(targets.toString()); - if (targets && targets.length && targets[0].className.toString().indexOf("timeline-menu-desc") === -1 || targets[0].className.toString().indexOf("timeline-menu-item") === -1 || targets[0].className.toString().indexOf("timeline-menu-item") === -1 || targets[0].className.toString().indexOf("timeline-menu-input") === -1){ + if (targets && (targets.length && targets[0].className.toString() !== "timeline-menu-desc" && targets[0].className.toString() !== "timeline-menu-item" && targets[0].className.toString() !=="timeline-menu-input")){ TimelineMenu.Instance.closeMenu(); } }); diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 187c9396d..c50ffa51b 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -352,11 +352,14 @@ export class Timeline extends React.Component { e.preventDefault(); e.stopPropagation(); let offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left; - let prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement); + let prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement); + let prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX,"mili", "time", this._tickSpacing, this._tickIncrement); e.deltaY < 0 ? this.zoom(true) : this.zoom(false); let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); + let currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement); this._infoContainer.current!.scrollLeft = currPixel - offset; - this._visibleStart = currPixel - offset; + this._visibleStart = currPixel - offset; + this.changeCurrentBarX(currCurrent); } @action diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 572b35b90..1fd97c6c1 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -44,12 +44,16 @@ export class TimelineMenu extends React.Component { document.addEventListener("keypress", (e:KeyboardEvent) => { if (e.keyCode === 13) { event(text); + this.closeMenu(); } }); }}/>
    ); } else if (type === "button") { let buttonRef = React.createRef(); - this._currentMenu.push(

    {title}

    ); + this._currentMenu.push(

    { + event(e); + this.closeMenu(); + }}>{title}

    ); } } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index e99da6648..89533c4df 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -53,10 +53,6 @@ export class Track extends React.Component { if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); this.props.node.hidden = false; this.props.node.opacity = 1; - let state = new Doc(); - state.key = Doc.MakeCopy(await this.props.node, true); - console.log(this.props.node.x); - this._storedState = state; }); } @@ -101,7 +97,7 @@ export class Track extends React.Component { @action revertState = () => { let copyDoc = Doc.MakeCopy(this.props.node, true); - this.applyKeys(this._storedState!); + if (this._storedState) this.applyKeys(this._storedState); let newState = new Doc(); newState.key = copyDoc; this._storedState = newState; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index e3b5f78a7..01a064b69 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -334,7 +334,7 @@ export namespace Doc { } export function IndexOf(toFind: Doc, list: Doc[]) { - return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)) + return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); } export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) { if (target[key] === undefined) { -- cgit v1.2.3-70-g09d2 From 4ae11ac54b25a5bfe935a934d3ce37084906ccdb Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 21 Sep 2019 16:55:18 -0400 Subject: changed boundaries --- src/client/views/animationtimeline/Keyframe.tsx | 36 ++++++++++++++++------ .../views/animationtimeline/TimelineMenu.scss | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 4 files changed, 30 insertions(+), 12 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 253515dfd..20fc8470d 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -412,7 +412,7 @@ export class Keyframe extends React.Component { makeKeyframeMenu = (kf :Doc, e:MouseEvent) => { TimelineMenu.Instance.addItem("button", "Show Data", () => { runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); - CollectionDockingView.Instance.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); + CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); }), TimelineMenu.Instance.addItem("button", "Delete", () => { runInAction(() => { @@ -430,16 +430,34 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{this.regions.splice(this.regions.indexOf(this.regiondata), 1);}), - TimelineMenu.Instance.addItem("input", "fadeIn", (val) => {this.regiondata.fadeIn = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "fadeOut", (val) => {this.regiondata.fadeOut = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "position", (val) => {this.regiondata.position = parseInt(val, 10);}), - TimelineMenu.Instance.addItem("input", "duration", (val) => {this.regiondata.duration = parseInt(val, 10);}), + TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => {runInAction(() => { + this.regiondata.fadeIn = parseInt(val, 10); + });}), + TimelineMenu.Instance.addItem("input", `fadeOut: ${this.regiondata.fadeOut}ms`, (val) => {runInAction(() => { + this.regiondata.fadeOut = parseInt(val, 10); + });}), + TimelineMenu.Instance.addItem("input", `position: ${this.regiondata.position}ms`, (val) => {runInAction(() => { + if (this.checkInput(val)){ + DocListCast(this.regions).forEach(region => { + if (region !== this.regiondata){ + if (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration))){ + return undefined; + } + } + }); + this.regiondata.position = val; + } + this.regiondata.position = parseInt(val, 10); + });}), + TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => {runInAction(() => { + this.regiondata.duration = parseInt(val, 10); + });}), TimelineMenu.Instance.addMenu("Region"); TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } - @action - private createKeyframeJSX = (kf: Doc, type = KeyframeFunc.KeyframeType.default) => { - + + checkInput = (val: any) => { + return typeof(val === "number") } onContainerOver = (e: React.PointerEvent, ref: React.RefObject) => { @@ -544,7 +562,7 @@ export class Keyframe extends React.Component {
    diff --git a/src/client/views/animationtimeline/TimelineMenu.scss b/src/client/views/animationtimeline/TimelineMenu.scss index 90cc53b4c..7ee0a43d5 100644 --- a/src/client/views/animationtimeline/TimelineMenu.scss +++ b/src/client/views/animationtimeline/TimelineMenu.scss @@ -8,7 +8,7 @@ flex-direction: column; background: whitesmoke; z-index: 10000; - width: 150px; + width: 200px; padding-bottom: 10px; border-radius: 15px; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9685f9bca..fcf483659 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -110,7 +110,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(Docu
    Date: Sun, 22 Sep 2019 17:24:05 -0400 Subject: fixed zooming and context menu - stable --- src/client/views/animationtimeline/Keyframe.tsx | 121 ++++++++++++++++----- .../views/animationtimeline/TimelineMenu.tsx | 22 ++-- src/client/views/animationtimeline/Track.tsx | 1 + 3 files changed, 109 insertions(+), 35 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 20fc8470d..7197f4b49 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -416,48 +416,115 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("button", "Delete", () => { runInAction(() => { - console.log(this.keyframes.indexOf(kf)); - this.keyframes.splice(this.keyframes.indexOf(kf), 1); + (this.regiondata.keyframes as List).splice(this.keyframes.indexOf(kf), 1); }); }), - TimelineMenu.Instance.addItem("input", "Move", (val) => {kf.time = parseInt(val, 10);}); + TimelineMenu.Instance.addItem("input", "Move", (val) => { + runInAction(() => { + if (this.checkInput(val)){ + let cannotMove:boolean = false; + let kfIndex:number = this.keyframes.indexOf(kf); + if (val < 0 ||( val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time)) ){ + cannotMove = true; + } + if (!cannotMove){ + this.keyframes[kfIndex].time = parseInt(val, 10); + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + } + } + }); + }); TimelineMenu.Instance.addMenu("Keyframe"); TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { - TimelineMenu.Instance.addItem("button", "Add Ease", () => {this.onContainerDown(kf, "interpolate");}), - TimelineMenu.Instance.addItem("button", "Add Path", () => {this.onContainerDown(kf, "path");}), + TimelineMenu.Instance.addItem("button", "Add Ease", () => { + // this.onContainerDown(kf, "interpolate"); + }), + TimelineMenu.Instance.addItem("button", "Add Path", () => { + // this.onContainerDown(kf, "path"); + }), TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{this.regions.splice(this.regions.indexOf(this.regiondata), 1);}), - TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => {runInAction(() => { - this.regiondata.fadeIn = parseInt(val, 10); + TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)){ + let cannotMove:boolean = false; + if (val < 0 || val > NumCast(this.keyframes[2].time) - this.regiondata.position){ + cannotMove = true; + } + if (!cannotMove){ + this.regiondata.fadeIn = parseInt(val, 10); + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + } + } });}), - TimelineMenu.Instance.addItem("input", `fadeOut: ${this.regiondata.fadeOut}ms`, (val) => {runInAction(() => { - this.regiondata.fadeOut = parseInt(val, 10); + TimelineMenu.Instance.addItem("input", `fadeOut: ${this.regiondata.fadeOut}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)){ + let cannotMove:boolean = false; + if (val < 0 || val > this.regiondata.position + this.regiondata.duration - NumCast(this.keyframes[this.keyframes.length - 3].time)){ + cannotMove = true; + } + if (!cannotMove){ + this.regiondata.fadeOut = parseInt(val, 10); + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - val; + } + } });}), - TimelineMenu.Instance.addItem("input", `position: ${this.regiondata.position}ms`, (val) => {runInAction(() => { - if (this.checkInput(val)){ + TimelineMenu.Instance.addItem("input", `position: ${this.regiondata.position}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)){ + let prevPosition = this.regiondata.position; + let cannotMove:boolean = false; DocListCast(this.regions).forEach(region => { - if (region !== this.regiondata){ - if (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration))){ - return undefined; + if (NumCast(region.position) !== this.regiondata.position){ + if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration)))){ + cannotMove = true; } } }); - this.regiondata.position = val; + if (!cannotMove) { + this.regiondata.position = parseInt(val, 10); + this.updateKeyframes(this.regiondata.position - prevPosition ); + } } - this.regiondata.position = parseInt(val, 10); });}), - TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => {runInAction(() => { - this.regiondata.duration = parseInt(val, 10); + TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)){ + let cannotMove:boolean = false; + DocListCast(this.regions).forEach(region => { + if (NumCast(region.position) !== this.regiondata.position){ + val += this.regiondata.position; + if ((val < 0 ) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration))){ + cannotMove = true; + } + } + }); + if (!cannotMove) { + this.regiondata.duration = parseInt(val, 10); + this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; + } + } });}), TimelineMenu.Instance.addMenu("Region"); TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } checkInput = (val: any) => { - return typeof(val === "number") + return typeof(val === "number"); + } + + @action + updateKeyframes = (incr:number, filter:number[] = []) => { + this.keyframes.forEach(kf => { + if (!filter.includes(this.keyframes.indexOf(kf))){ + kf.time = NumCast(kf.time) + incr; + } + }); } onContainerOver = (e: React.PointerEvent, ref: React.RefObject) => { @@ -495,8 +562,6 @@ export class Keyframe extends React.Component { listenerCreated = true; } }); - - } @action @@ -558,6 +623,7 @@ export class Keyframe extends React.Component { } } render() { + console.log("RERENDERING"); return (
    {
    ); } - return ( -
    -
    -
    - ); + else { + return ( +
    +
    +
    + ); + } + })} {this.keyframes.map( kf => { if(this.keyframes.indexOf(kf ) !== this.keyframes.length - 1) { diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 1fd97c6c1..f3b985297 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -36,21 +36,25 @@ export class TimelineMenu extends React.Component { } @action - addItem = (type: "input" | "button", title: string, event: (e:any) => void) => { + addItem = (type: "input" | "button", title: string, event: (e:any, ...args:any[]) => void) => { if (type === "input"){ let inputRef = React.createRef(); - this._currentMenu.push(
    { - let text = e.target.value; - document.addEventListener("keypress", (e:KeyboardEvent) => { - if (e.keyCode === 13) { - event(text); - this.closeMenu(); - } - }); + let text = ""; + this._currentMenu.push(
    { + e.stopPropagation(); + text = e.target.value; + }} onKeyDown={(e) => { + if (e.keyCode === 13) { + event(text); + this.closeMenu(); + e.stopPropagation(); + } }}/>
    ); } else if (type === "button") { let buttonRef = React.createRef(); this._currentMenu.push(

    { + e.preventDefault(); + e.stopPropagation(); event(e); this.closeMenu(); }}>{title}

    ); diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 89533c4df..c68d9bb3a 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -69,6 +69,7 @@ export class Track extends React.Component { let keyframes: List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); let kfIndex: number = keyframes.indexOf(ref); let kf = keyframes[kfIndex] as Doc; + if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); // lef keyframe, if it exists -- cgit v1.2.3-70-g09d2 From bf8907cfc3e005f2ce6756820d9b3f9de35f1807 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 24 Sep 2019 16:50:23 -0400 Subject: brushing --- src/client/views/animationtimeline/Keyframe.tsx | 3 ++- src/client/views/animationtimeline/Timeline.tsx | 2 +- src/client/views/animationtimeline/Track.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 7197f4b49..9728c2462 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -532,6 +532,7 @@ export class Keyframe extends React.Component { e.stopPropagation(); let div = ref.current!; div.style.opacity = "1"; + Doc.BrushDoc(this.props.node); } onContainerOut = (e: React.PointerEvent, ref: React.RefObject) => { @@ -539,6 +540,7 @@ export class Keyframe extends React.Component { e.stopPropagation(); let div = ref.current!; div.style.opacity = "0"; + Doc.UnBrushDoc(this.props.node); } @@ -623,7 +625,6 @@ export class Keyframe extends React.Component { } } render() { - console.log("RERENDERING"); return (
    {
    - {DocListCast(this.children).map(doc =>

    {doc.title}

    )} + {DocListCast(this.children).map(doc =>
    {Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

    {doc.title}

    )}
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index c68d9bb3a..274b215d9 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -294,7 +294,7 @@ export class Track extends React.Component { return (
    -
    +
    {Doc.BrushDoc(this.props.node);}}onPointerOut={() => {Doc.UnBrushDoc(this.props.node);}}> {DocListCast(this.regions).map((region) => { return ; })} -- cgit v1.2.3-70-g09d2 From 8c018a7a686a752ef1b86b852ec2d298792aa354 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 28 Sep 2019 17:00:47 -0400 Subject: toggle --- src/client/views/animationtimeline/Keyframe.tsx | 100 ++++------------- src/client/views/animationtimeline/Timeline.scss | 20 ++++ src/client/views/animationtimeline/Timeline.tsx | 121 ++++++--------------- .../views/animationtimeline/TimelineMenu.tsx | 11 +- .../views/animationtimeline/TimelineOverview.tsx | 2 + src/client/views/animationtimeline/Track.tsx | 40 +++---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 +- 7 files changed, 102 insertions(+), 195 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 9728c2462..66ad6a76d 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -8,7 +8,6 @@ import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { Cast, 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 { TimelineMenu } from "./TimelineMenu"; @@ -138,48 +137,13 @@ export class Keyframe extends React.Component { @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; - @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; - } - - @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 regiondata() { return RegionData(this.regions[this.regions.indexOf(this.props.RegionData)] as Doc);} + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} + @computed private get keyframes(){ return DocListCast(this.regiondata.keyframes); } + @computed private get pixelPosition(){ return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement);} + @computed private get pixelDuration(){ return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } + @computed private get pixelFadeIn() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } + @computed private get pixelFadeOut(){ return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get inks() { if (this.props.collection.data_ext) { @@ -191,38 +155,18 @@ export class Keyframe extends React.Component { } } - @computed - private get pixelPosition(){ - return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - @computed - private get pixelDuration(){ - return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - @computed - private get pixelFadeIn() { - return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - @computed - private get pixelFadeOut(){ - return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - async componentWillMount() { - if (!this.regiondata.keyframes) { - this.regiondata.keyframes = new List(); - } - 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; + componentWillMount() { + runInAction(async () => { + if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); + 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; + }); } @action @@ -336,7 +280,7 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions); - let firstkf: (Doc | undefined) = this.firstKeyframe; + let firstkf: (Doc | undefined) = this.keyframes[0]; if (firstkf && this.regiondata.position + this.regiondata.fadeIn + offset >= NumCast(firstkf!.time)) { let dif = NumCast(firstkf!.time) - (this.pixelPosition + this.pixelFadeIn); this.regiondata.position = NumCast(firstkf!.time) - this.regiondata.fadeIn; @@ -364,8 +308,8 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); 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); + if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= NumCast((this.keyframes[this.keyframes.length - 1]).time)) { + let dif = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut - NumCast((this.keyframes[this.keyframes.length - 1]).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; diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 1457d5a84..990bc681c 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -119,4 +119,24 @@ width: 100%; background-color: grey; } +} + +.round-toggle { + position: absolute; + height: 40px; + width: 80px; + background-color: white; + border: 2px solid purple; + border-radius: 20px; + input{ + opacity: 0; + height: 0; + width: 0; + } + .slider{ + height: 40px; + width: 40px; + background-color: black; + border-radius: 20px; + } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 875a0b8f3..8f96315a0 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -15,17 +15,6 @@ import { TimelineOverview } from "./TimelineOverview"; import { FieldViewProps } from "../nodes/FieldView"; import { KeyframeFunc } from "./Keyframe"; - - -export interface FlyoutProps { - x?: number; - y?: number; - display?: string; - regiondata?: Doc; - regions?: List; -} - - @observer export class Timeline extends React.Component { @@ -35,26 +24,21 @@ export class Timeline extends React.Component { 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(); - @observable private _scrubber = React.createRef(); @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); - @observable private _timelineWrapper = React.createRef(); @observable private _infoContainer = React.createRef(); @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @observable private _isPlaying: boolean = false; //scrubber playing - @observable private _isFrozen: boolean = true; //timeline freeze @observable private _totalLength: number = 0; @observable private _visibleLength: number = 0; @observable private _visibleStart: number = 0; - @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; + @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; + @observable private _tickSpacing = this.DEFAULT_TICK_SPACING; + @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; @observable private _time = 100000; //DEFAULT @observable private _ticks: number[] = []; @observable private _playButton = faPlayCircle; @@ -273,39 +257,7 @@ export class Timeline extends React.Component { } } - @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 toReadTime = (time: number): string => { @@ -321,21 +273,6 @@ export class Timeline extends React.Component { timelineContextMenu = (e:MouseEvent): void => { let subitems: ContextMenuProps[] = []; - let timelineContainer = this._timelineWrapper.current!; - subitems.push({ - description: "Pin to Top", event: action(() => { - if (!this._isFrozen) { - timelineContainer.style.left = "0px"; - timelineContainer.style.top = "0px"; - timelineContainer.style.transition = "none"; - } - }), icon: faArrowUp - }); - subitems.push({ - description: this._isFrozen ? "Unfreeze Timeline" : "Freeze Timeline", event: action(() => { - this._isFrozen = !this._isFrozen; - }), icon: "thumbtack" - }); subitems.push({ description: this._timelineVisible ? "Hide Timeline" : "Show Timeline", event: action(() => { this._timelineVisible = !this._timelineVisible; @@ -358,7 +295,8 @@ export class Timeline extends React.Component { let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); let currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement); this._infoContainer.current!.scrollLeft = currPixel - offset; - this._visibleStart = currPixel - offset; + this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0; + this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) :0; this.changeCurrentBarX(currCurrent); } @@ -402,35 +340,42 @@ export class Timeline extends React.Component {
    ); } + render() { return ( -
    -
    - -
    - {this.timelineToolBox(0.5)} -
    -
    - {this._ticks.map(element => { - if(element % this._tickIncrement === 0) return

    {this.toReadTime(element)}

    ; - })} +
    +
    +
    +
    + {this.timelineToolBox(0.5)} +
    +
    + {this._ticks.map(element => { + if(element % this._tickIncrement === 0) return

    {this.toReadTime(element)}

    ; + })} +
    +
    +
    +
    +
    + {DocListCast(this.children).map(doc => )} +
    -
    -
    +
    + {DocListCast(this.children).map(doc =>
    {Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

    {doc.title}

    )}
    -
    - {DocListCast(this.children).map(doc => )} +
    +
    -
    - {DocListCast(this.children).map(doc =>
    {Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

    {doc.title}

    )} -
    -
    - -
    + {BoolCast(this.props.Document.isAnimating) ?
    : this.timelineToolBox(1) }
    - {BoolCast(this.props.Document.isAnimating) ?
    : this.timelineToolBox(1) } + +
    ); } diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index f3b985297..59c25596e 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -4,6 +4,7 @@ import {observer} from "mobx-react"; import "./TimelineMenu.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons"; +import { Utils } from "../../../Utils"; @observer @@ -40,7 +41,7 @@ export class TimelineMenu extends React.Component { if (type === "input"){ let inputRef = React.createRef(); let text = ""; - this._currentMenu.push(
    { + this._currentMenu.push(
    { e.stopPropagation(); text = e.target.value; }} onKeyDown={(e) => { @@ -52,23 +53,23 @@ export class TimelineMenu extends React.Component { }}/>
    ); } else if (type === "button") { let buttonRef = React.createRef(); - this._currentMenu.push(

    { + this._currentMenu.push(

    { e.preventDefault(); e.stopPropagation(); event(e); this.closeMenu(); }}>{title}

    ); - } + } } @action addMenu = (title:string) => { - this._currentMenu.unshift(

    {title}

    ); + this._currentMenu.unshift(

    {title}

    ); } render() { return ( -
    +
    {this._currentMenu}
    ); diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 38b823cbc..a8f7faba8 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -78,6 +78,8 @@ export class TimelineOverview extends React.Component{ } render(){ + console.log("rendered"); + console.log(this.props.visibleStart); return(
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 274b215d9..3ec410216 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -32,18 +32,24 @@ export class Track extends React.Component { @observable private _onKeyframe: (Doc | undefined) = undefined; @observable private _onRegionData: (Doc | undefined) = undefined; @observable private _storedState: (Doc | undefined) = undefined; - - @computed - private get regions() { - return Cast(this.props.node.regions, listSpec(Doc)) as List; - } + @observable private filterList = [ + "regions", + "cursors", + "hidden", + "nativeHeight", + "nativeWidth", + "schemaColumns", + "baseLayout", + "backgroundLayout", + "layout", + ]; + + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} componentWillMount() { - if (!this.props.node.regions) { - this.props.node.regions = new List(); - } - - + runInAction(() => { + if (!this.props.node.regions) this.props.node.regions = new List(); + }); } componentDidMount() { @@ -54,11 +60,11 @@ export class Track extends React.Component { this.props.node.hidden = false; this.props.node.opacity = 1; }); - } componentWillUnmount() { runInAction(() => { + //disposing reactions if (this._currentBarXReaction) this._currentBarXReaction(); if (this._timelineVisibleReaction) this._timelineVisibleReaction(); }); @@ -166,17 +172,7 @@ export class Track extends React.Component { }); } - private filterList = [ - "regions", - "cursors", - "hidden", - "nativeHeight", - "nativeWidth", - "schemaColumns", - "baseLayout", - "backgroundLayout", - "layout", - ]; + @action private filterKeys = (keys: string[]): string[] => { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index af2651dc8..bbfc5eb51 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -115,8 +115,7 @@ export class CollectionFreeFormDocumentView extends DocComponent Date: Tue, 1 Oct 2019 16:24:18 -0400 Subject: toggle button --- src/client/views/animationtimeline/Timeline.scss | 44 +++++++++++++++------ src/client/views/animationtimeline/Timeline.tsx | 45 ++++++++++++++++------ .../views/animationtimeline/TimelineOverview.tsx | 2 - 3 files changed, 65 insertions(+), 26 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 990bc681c..09fc593fc 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -1,11 +1,6 @@ @import "./../globalCssVariables.scss"; -.minimize{ - position:relative; - z-index: 1000; - height: 30px; - width: 100px; -} + .timeline-toolbox{ position:absolute; @@ -17,6 +12,8 @@ margin-left:10px; } } + + .timeline-container{ width:100%; height:300px; @@ -37,12 +34,10 @@ background-color: transparent; height: 30px; width:100%; - .tick{ height:100%; width: 1px; background-color:black; - } } .scrubber{ @@ -128,15 +123,40 @@ background-color: white; border: 2px solid purple; border-radius: 20px; + animation-fill-mode: forwards; + animation-duration: 500ms; input{ + position:absolute; opacity: 0; height: 0; width: 0; } - .slider{ - height: 40px; - width: 40px; - background-color: black; + .round-toggle-slider{ + position:absolute; + height: 35px; + width: 35px; + top: 0.5px; + background-color: white; + border:1px solid grey; border-radius: 20px; + transition: transform 500ms ease-in-out; + + } + +} +@keyframes turnon{ + from{ + background-color: white; + } + to{ + background-color: purple; + } +} +@keyframes turnoff{ + from{ + background-color: purple; + } + to{ + background-color: white; } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 8f96315a0..8127e4de2 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -29,6 +29,8 @@ export class Timeline extends React.Component { @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); @observable private _infoContainer = React.createRef(); + @observable private _roundToggleRef = React.createRef(); + @observable private _roundToggleContainerRef = React.createRef(); @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @@ -331,23 +333,45 @@ export class Timeline extends React.Component { private timelineToolBox = (scale:number) => { let size = 50 * scale; //50 is default - return ( + return ( +
    -
    -
    -
    - +
    +
    +
    + +
    +
    +
    ); } + @action + private toggleChecked = (e:React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + let roundToggle = this._roundToggleRef.current!; + let roundToggleContainer = this._roundToggleContainerRef.current!; + if (BoolCast(this.props.Document.isAnimating)){ + roundToggle.style.transform = "translate(0px, 0px)"; + roundToggle.style.animationName = "turnoff"; + roundToggleContainer.style.animationName = "turnoff"; + + this.props.Document.isAnimating = false; + } else { + roundToggle.style.transform = "translate(45px, 0px)"; + roundToggle.style.animationName = "turnon"; + roundToggleContainer.style.animationName = "turnon"; + this.props.Document.isAnimating = true; + } + } render() { return (
    - {this.timelineToolBox(0.5)}
    {this._ticks.map(element => { @@ -365,16 +389,13 @@ export class Timeline extends React.Component { {DocListCast(this.children).map(doc =>
    {Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

    {doc.title}

    )}
    - +
    - {BoolCast(this.props.Document.isAnimating) ?
    : this.timelineToolBox(1) } + { this.timelineToolBox(1) }
    - +
    ); diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index a8f7faba8..38b823cbc 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -78,8 +78,6 @@ export class TimelineOverview extends React.Component{ } render(){ - console.log("rendered"); - console.log(this.props.visibleStart); return(
    -- cgit v1.2.3-70-g09d2 From 7ccce9564e11ede82bec01b9eea3889da4dc1bc2 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 1 Oct 2019 17:10:17 -0400 Subject: changes with toggling --- src/client/views/animationtimeline/Timeline.scss | 11 +++++------ src/client/views/animationtimeline/Timeline.tsx | 8 +++++--- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 09fc593fc..71fac876d 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -22,7 +22,7 @@ box-shadow: 0px 10px 20px; .info-container{ - margin-top: 50px; + margin-top: 80px; right:20px; position:absolute; height: calc(100% - 100px); @@ -72,7 +72,7 @@ } .title-container{ - margin-top: 80px; + margin-top: 110px; margin-left: 20px; height: calc(100% - 100px - 30px); width: 100px; @@ -117,7 +117,6 @@ } .round-toggle { - position: absolute; height: 40px; width: 80px; background-color: white; @@ -125,6 +124,7 @@ border-radius: 20px; animation-fill-mode: forwards; animation-duration: 500ms; + top: 20px; input{ position:absolute; opacity: 0; @@ -132,15 +132,14 @@ width: 0; } .round-toggle-slider{ - position:absolute; height: 35px; width: 35px; - top: 0.5px; background-color: white; border:1px solid grey; border-radius: 20px; transition: transform 500ms ease-in-out; - + margin-left: 0px; + margin-top: 0.5px; } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 8127e4de2..bc48f422a 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -18,7 +18,7 @@ import { KeyframeFunc } from "./Keyframe"; @observer export class Timeline extends React.Component { - private readonly DEFAULT_CONTAINER_HEIGHT: number = 300; + private readonly DEFAULT_CONTAINER_HEIGHT: number = 330; private readonly DEFAULT_TICK_SPACING: number = 50; private readonly MIN_CONTAINER_HEIGHT: number = 205; private readonly MAX_CONTAINER_HEIGHT: number = 800; @@ -353,16 +353,18 @@ export class Timeline extends React.Component { e.stopPropagation(); let roundToggle = this._roundToggleRef.current!; let roundToggleContainer = this._roundToggleContainerRef.current!; + let timelineContainer = this._timelineContainer.current!; if (BoolCast(this.props.Document.isAnimating)){ roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; - + timelineContainer.style.transform = `translate(0px, ${this._containerHeight}px)`; this.props.Document.isAnimating = false; } else { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; + timelineContainer.style.transform = `translate(0px, ${-this._containerHeight}px)`; this.props.Document.isAnimating = true; } } @@ -371,7 +373,7 @@ export class Timeline extends React.Component {
    -
    +
    {this._ticks.map(element => { -- cgit v1.2.3-70-g09d2 From 3143c2775cdf7bdafa44f047ea9b6ebbd090b28e Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 5 Oct 2019 17:26:10 -0400 Subject: animation --- src/client/views/animationtimeline/Timeline.scss | 1 + src/client/views/animationtimeline/Timeline.tsx | 251 ++++++++++++----------- 2 files changed, 131 insertions(+), 121 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 71fac876d..fbdb2a200 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -20,6 +20,7 @@ position:absolute; background-color: $light-color-secondary; box-shadow: 0px 10px 20px; + transition: transform 500ms ease; .info-container{ margin-top: 80px; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index bc48f422a..8a41a5943 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -29,25 +29,25 @@ export class Timeline extends React.Component { @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); @observable private _infoContainer = React.createRef(); - @observable private _roundToggleRef = React.createRef(); - @observable private _roundToggleContainerRef = React.createRef(); + @observable private _roundToggleRef = React.createRef(); + @observable private _roundToggleContainerRef = React.createRef(); @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @observable private _isPlaying: boolean = false; //scrubber playing @observable private _totalLength: number = 0; - @observable private _visibleLength: number = 0; - @observable private _visibleStart: number = 0; - @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; + @observable private _visibleLength: number = 0; + @observable private _visibleStart: number = 0; + @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; @observable private _tickSpacing = this.DEFAULT_TICK_SPACING; @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; @observable private _time = 100000; //DEFAULT @observable private _ticks: number[] = []; - @observable private _playButton = faPlayCircle; - @observable private _timelineVisible = false; - @observable private _mouseToggled = false; - @observable private _doubleClickEnabled = false; - @observable private _reactionDisposer:IReactionDisposer[] = []; + @observable private _playButton = faPlayCircle; + @observable private _timelineVisible = false; + @observable private _mouseToggled = false; + @observable private _doubleClickEnabled = false; + @observable private _reactionDisposer: IReactionDisposer[] = []; @computed @@ -65,7 +65,7 @@ export class Timeline extends React.Component { componentWillMount() { - this.props.Document.isAnimating ? this.props.Document.isAnimating = true : this.props.Document.isAnimating = false; + this.props.Document.isAnimating ? this.props.Document.isAnimating = true : this.props.Document.isAnimating = false; } componentDidMount() { @@ -78,29 +78,29 @@ export class Timeline extends React.Component { return NumCast(this.props.Document.curPage); }, curPage => { if (!this._isPlaying) { - this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); - this.props.Document.curPage = this._currentBarX; - this.play(); + this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); + this.props.Document.curPage = this._currentBarX; + this.play(); } })); } } runInAction(() => { this._reactionDisposer.push(reaction(() => { - return this._time; + return this._time; }, () => { this._ticks = []; for (let i = 0; i < this._time;) { this._ticks.push(i); i += 1000; } - this._totalLength = this._tickSpacing * (this._time/ this._tickIncrement); - }, {fireImmediately:true})); - this._totalLength = this._tickSpacing * (this._ticks.length/ this._tickIncrement); - this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; - this._visibleStart = this._infoContainer.current!.scrollLeft; + this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); + }, { fireImmediately: true })); + this._totalLength = this._tickSpacing * (this._ticks.length / this._tickIncrement); + this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; + this._visibleStart = this._infoContainer.current!.scrollLeft; }); - + } @@ -112,30 +112,30 @@ export class Timeline extends React.Component { //for playing @action onPlay = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - this.play(); + e.preventDefault(); + e.stopPropagation(); + this.play(); } @action play = () => { if (this._isPlaying) { this._isPlaying = false; - this._playButton = faPlayCircle; + this._playButton = faPlayCircle; } else { this._isPlaying = true; - this._playButton = faPauseCircle; + this._playButton = faPauseCircle; const playTimeline = () => { - if (this._isPlaying){ + if (this._isPlaying) { if (this._currentBarX >= this._totalLength) { - this.changeCurrentBarX(0); + this.changeCurrentBarX(0); } else { this.changeCurrentBarX(this._currentBarX + this._windSpeed); - } - setTimeout(playTimeline, 15); + } + setTimeout(playTimeline, 15); } - }; - playTimeline(); + }; + playTimeline(); } } @@ -143,8 +143,8 @@ export class Timeline extends React.Component { @action windForward = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); + e.preventDefault(); + e.stopPropagation(); if (this._windSpeed < 64) { //max speed is 32 this._windSpeed = this._windSpeed * 2; } @@ -152,8 +152,8 @@ export class Timeline extends React.Component { @action windBackward = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); + e.preventDefault(); + e.stopPropagation(); if (this._windSpeed > 1 / 16) { // min speed is 1/8 this._windSpeed = this._windSpeed / 2; } @@ -177,7 +177,7 @@ export class Timeline extends React.Component { let scrubberbox = this._scrubberbox.current!; let left = scrubberbox.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; - this.changeCurrentBarX(offsetX); + this.changeCurrentBarX(offsetX); } @action @@ -186,7 +186,7 @@ export class Timeline extends React.Component { e.stopPropagation(); let scrubberbox = this._scrubberbox.current!; let offsetX = (e.clientX - scrubberbox.getBoundingClientRect().left) * this.props.ScreenToLocalTransform().Scale; - this.changeCurrentBarX(offsetX); + this.changeCurrentBarX(offsetX); } @@ -195,19 +195,21 @@ export class Timeline extends React.Component { onPanDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let clientX = e.clientX; - if (this._doubleClickEnabled){ - this._doubleClickEnabled = false; + let clientX = e.clientX; + if (this._doubleClickEnabled) { + this._doubleClickEnabled = false; } else { - setTimeout(() => {if(!this._mouseToggled && this._doubleClickEnabled) this.changeCurrentBarX(this._trackbox.current!.scrollLeft + clientX - this._trackbox.current!.getBoundingClientRect().left); + setTimeout(() => { + if (!this._mouseToggled && this._doubleClickEnabled) this.changeCurrentBarX(this._trackbox.current!.scrollLeft + clientX - this._trackbox.current!.getBoundingClientRect().left); this._mouseToggled = false; - this._doubleClickEnabled = false;}, 200); - this._doubleClickEnabled = true; + this._doubleClickEnabled = false; + }, 200); + this._doubleClickEnabled = true; document.addEventListener("pointermove", this.onPanMove); document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onPanMove); if (!this._doubleClickEnabled) { - this._mouseToggled = false; + this._mouseToggled = false; } }); @@ -219,7 +221,7 @@ export class Timeline extends React.Component { e.preventDefault(); e.stopPropagation(); if (e.movementX !== 0 || e.movementY !== 0) { - this._mouseToggled = true; + this._mouseToggled = true; } let trackbox = this._trackbox.current!; let titleContainer = this._titleContainer.current!; @@ -227,10 +229,10 @@ export class Timeline extends React.Component { trackbox.scrollTop = trackbox.scrollTop - e.movementY; titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; } - @action - movePanX = (pixel:number) => { + @action + movePanX = (pixel: number) => { let infoContainer = this._infoContainer.current!; - infoContainer.scrollLeft = pixel; + infoContainer.scrollLeft = pixel; this._visibleStart = infoContainer.scrollLeft; } @@ -273,131 +275,138 @@ export class Timeline extends React.Component { return `${min}:${sec}`; } - timelineContextMenu = (e:MouseEvent): void => { + timelineContextMenu = (e: MouseEvent): void => { let subitems: ContextMenuProps[] = []; subitems.push({ description: this._timelineVisible ? "Hide Timeline" : "Show Timeline", event: action(() => { - this._timelineVisible = !this._timelineVisible; + this._timelineVisible = !this._timelineVisible; }), icon: this._timelineVisible ? faEyeSlash : "eye" - }); - subitems.push({ description: BoolCast(this.props.Document.isAnimating) ? "Enter Play Mode" : "Enter Authoring Mode", event: () => { - BoolCast(this.props.Document.isAnimating) ? this.props.Document.isAnimating = false : this.props.Document.isAnimating = true;} - , icon:BoolCast(this.props.Document.isAnimating) ? "play" : "edit"}); + }); + subitems.push({ + description: BoolCast(this.props.Document.isAnimating) ? "Enter Play Mode" : "Enter Authoring Mode", event: () => { + BoolCast(this.props.Document.isAnimating) ? this.props.Document.isAnimating = false : this.props.Document.isAnimating = true; + } + , icon: BoolCast(this.props.Document.isAnimating) ? "play" : "edit" + }); ContextMenu.Instance.addItem({ description: "Timeline Funcs...", subitems: subitems, icon: faClock }); } @action onWheelZoom = (e: React.WheelEvent) => { - e.preventDefault(); - e.stopPropagation(); - let offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left; + e.preventDefault(); + e.stopPropagation(); + let offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left; let prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement); - let prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX,"mili", "time", this._tickSpacing, this._tickIncrement); - e.deltaY < 0 ? this.zoom(true) : this.zoom(false); - let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); - let currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement); - this._infoContainer.current!.scrollLeft = currPixel - offset; + let prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); + e.deltaY < 0 ? this.zoom(true) : this.zoom(false); + let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); + let currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement); + this._infoContainer.current!.scrollLeft = currPixel - offset; this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0; - this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) :0; - this.changeCurrentBarX(currCurrent); + this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) : 0; + this.changeCurrentBarX(currCurrent); } @action zoom = (dir: boolean) => { - let spacingChange = this._tickSpacing; - let incrementChange = this._tickIncrement; - if (dir){ - if (!(this._tickSpacing === 100 && this._tickIncrement === 1000)){ + let spacingChange = this._tickSpacing; + let incrementChange = this._tickIncrement; + if (dir) { + if (!(this._tickSpacing === 100 && this._tickIncrement === 1000)) { if (this._tickSpacing >= 100) { - incrementChange /= 2; - spacingChange = 50; + incrementChange /= 2; + spacingChange = 50; } else { - spacingChange += 5; + spacingChange += 5; } - } + } } else { if (this._tickSpacing <= 50) { - spacingChange = 100; - incrementChange *= 2; + spacingChange = 100; + incrementChange *= 2; } else { - spacingChange -= 5; + spacingChange -= 5; } } - let finalLength = spacingChange * (this._time / incrementChange); - if (finalLength >= this._infoContainer.current!.getBoundingClientRect().width){ - this._totalLength = finalLength; - this._tickSpacing = spacingChange; - this._tickIncrement = incrementChange; + let finalLength = spacingChange * (this._time / incrementChange); + if (finalLength >= this._infoContainer.current!.getBoundingClientRect().width) { + this._totalLength = finalLength; + this._tickSpacing = spacingChange; + this._tickIncrement = incrementChange; } } - private timelineToolBox = (scale:number) => { + private timelineToolBox = (scale: number) => { let size = 50 * scale; //50 is default - return ( - -
    -
    -
    -
    - -
    -
    + return ( + +
    +
    +
    +
    + +
    +
    +
    -
    ); } @action - private toggleChecked = (e:React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let roundToggle = this._roundToggleRef.current!; - let roundToggleContainer = this._roundToggleContainerRef.current!; - let timelineContainer = this._timelineContainer.current!; - if (BoolCast(this.props.Document.isAnimating)){ + private toggleChecked = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + let roundToggle = this._roundToggleRef.current!; + let roundToggleContainer = this._roundToggleContainerRef.current!; + let timelineContainer = this._timelineContainer.current!; + if (BoolCast(this.props.Document.isAnimating)) { + roundToggle.style.transform = "translate(0px, 0px)"; - roundToggle.style.animationName = "turnoff"; - roundToggleContainer.style.animationName = "turnoff"; - timelineContainer.style.transform = `translate(0px, ${this._containerHeight}px)`; - this.props.Document.isAnimating = false; + roundToggle.style.animationName = "turnoff"; + roundToggleContainer.style.animationName = "turnoff"; + timelineContainer.style.transform = `translate(0px, ${0}px)`; + setTimeout(() => { + this.props.Document.isAnimating = false; + }, 500); } else { - roundToggle.style.transform = "translate(45px, 0px)"; - roundToggle.style.animationName = "turnon"; - roundToggleContainer.style.animationName = "turnon"; - timelineContainer.style.transform = `translate(0px, ${-this._containerHeight}px)`; - this.props.Document.isAnimating = true; + roundToggle.style.transform = "translate(45px, 0px)"; + roundToggle.style.animationName = "turnon"; + roundToggleContainer.style.animationName = "turnon"; + timelineContainer.style.transform = `translate(0px, ${this._containerHeight}px)`; + this.props.Document.isAnimating = true; + } } render() { return (
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    {this._ticks.map(element => { - if(element % this._tickIncrement === 0) return

    {this.toReadTime(element)}

    ; + if (element % this._tickIncrement === 0) return

    {this.toReadTime(element)}

    ; })}
    -
    - {DocListCast(this.children).map(doc => )} +
    + {DocListCast(this.children).map(doc => )}
    -
    - {DocListCast(this.children).map(doc =>
    {Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

    {doc.title}

    )} +
    + {DocListCast(this.children).map(doc =>
    { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}>

    {doc.title}

    )}
    - +
    - { this.timelineToolBox(1) } + {this.timelineToolBox(1)}
    - +
    ); -- cgit v1.2.3-70-g09d2 From 98cc5ba95454be7d36343917c6e2c28047f109b9 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 6 Oct 2019 16:59:33 -0400 Subject: refactoring and resize right fix --- src/client/views/animationtimeline/Keyframe.tsx | 121 +++++++++++++---------- src/client/views/animationtimeline/Timeline.scss | 12 +-- src/client/views/animationtimeline/Timeline.tsx | 104 +++++-------------- 3 files changed, 98 insertions(+), 139 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 66ad6a76d..587625efa 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -288,7 +288,7 @@ export class Keyframe extends React.Component { } 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) { + } 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; @@ -296,8 +296,8 @@ export class Keyframe extends React.Component { this.regiondata.duration -= offset; this.regiondata.position += offset; } - this.keyframes[0].time = this.regiondata.position; - this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + // this.keyframes[0].time = this.regiondata.position; + // this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; } @@ -308,14 +308,11 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions); - if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= NumCast((this.keyframes[this.keyframes.length - 1]).time)) { - let dif = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut - NumCast((this.keyframes[this.keyframes.length - 1]).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; + console.log(offset); + console.log(this.keyframes[this.keyframes.length - 3].time); + console.log(this.regiondata.position + this.regiondata.duration - offset); + if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut - offset <= NumCast(this.keyframes[this.keyframes.length - 3].time)) { + console.log("HI"); } else { this.regiondata.duration += offset; } @@ -458,6 +455,7 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } + @action checkInput = (val: any) => { return typeof(val === "number"); } @@ -471,6 +469,7 @@ export class Keyframe extends React.Component { }); } + @action onContainerOver = (e: React.PointerEvent, ref: React.RefObject) => { e.preventDefault(); e.stopPropagation(); @@ -479,6 +478,7 @@ export class Keyframe extends React.Component { Doc.BrushDoc(this.props.node); } + @action onContainerOut = (e: React.PointerEvent, ref: React.RefObject) => { e.preventDefault(); e.stopPropagation(); @@ -568,6 +568,60 @@ export class Keyframe extends React.Component { document.removeEventListener("pointerup", this.onReactionListen); } } + + @action + drawKeyframes = () => { + let keyframeDivs:JSX.Element[] = []; + this.keyframes.forEach( kf => { + if (kf.type as KeyframeFunc.KeyframeType === KeyframeFunc.KeyframeType.default) { + keyframeDivs.push( +
    +
    +
    { this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + this.makeKeyframeMenu(kf, e.nativeEvent); + }}>
    +
    + ); + } + else { + keyframeDivs.push( +
    +
    +
    + ); + } + }); + return keyframeDivs; + } + + @action + drawKeyframeDividers = () => { + let keyframeDividers:JSX.Element[] = []; + this.keyframes.forEach(kf => { + if(this.keyframes.indexOf(kf ) !== this.keyframes.length - 1) { + let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; + let bodyRef = React.createRef(); + let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + let leftPos = KeyframeFunc.convertPixelTime(NumCast(left!.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + return ( +
    { this.onContainerOver(e, bodyRef); }} + onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} + onContextMenu={(e) => { + e.preventDefault(); + e.stopPropagation(); + this._mouseToggled = true; + this.makeRegionMenu(kf, e.nativeEvent); + }}> +
    + ); + } + }); + return keyframeDividers; + } + render() { return (
    @@ -577,49 +631,8 @@ export class Keyframe extends React.Component { onPointerDown={this.onBarPointerDown}>
    - {this.keyframes.map(kf => { - if (kf.type as KeyframeFunc.KeyframeType === KeyframeFunc.KeyframeType.default) { - return ( -
    -
    -
    { this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - this.makeKeyframeMenu(kf, e.nativeEvent); - }}>
    -
    - ); - } - else { - return ( -
    -
    -
    - ); - } - - })} - {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(); - let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - let leftPos = KeyframeFunc.convertPixelTime(NumCast(left!.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - return ( -
    { this.onContainerOver(e, bodyRef); }} - onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} - onContextMenu={(e) => { - e.preventDefault(); - e.stopPropagation(); - this._mouseToggled = true; - this.makeRegionMenu(kf, e.nativeEvent); - }}> -
    - ); - } - })} - + {this.drawKeyframes()} + {this.drawKeyframeDividers()}
    ); diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index fbdb2a200..cf6b4b3d5 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -13,7 +13,11 @@ } } - +.tick{ + height:100%; + width: 1px; + background-color:black; +} .timeline-container{ width:100%; height:300px; @@ -35,11 +39,7 @@ background-color: transparent; height: 30px; width:100%; - .tick{ - height:100%; - width: 1px; - background-color:black; - } + } .scrubber{ top:30px; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 8a41a5943..9ac5d98d2 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -8,12 +8,13 @@ import { Cast, NumCast, StrCast, BoolCast } 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, faEyeSlash } from "@fortawesome/free-solid-svg-icons"; +import { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDown, faClock, faPauseCircle, faEyeSlash, faTimes } from "@fortawesome/free-solid-svg-icons"; import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; import { FieldViewProps } from "../nodes/FieldView"; import { KeyframeFunc } from "./Keyframe"; +import { Utils } from "../../../Utils"; @observer export class Timeline extends React.Component { @@ -24,7 +25,6 @@ export class Timeline extends React.Component { private readonly MAX_CONTAINER_HEIGHT: number = 800; private readonly DEFAULT_TICK_INCREMENT: number = 1000; - @observable private _scrubberbox = React.createRef(); @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); @@ -42,13 +42,10 @@ export class Timeline extends React.Component { @observable private _tickSpacing = this.DEFAULT_TICK_SPACING; @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; @observable private _time = 100000; //DEFAULT - @observable private _ticks: number[] = []; @observable private _playButton = faPlayCircle; @observable private _timelineVisible = false; @observable private _mouseToggled = false; - @observable private _doubleClickEnabled = false; - @observable private _reactionDisposer: IReactionDisposer[] = []; - + @observable private _doubleClickEnabled = false; @computed private get children(): List { @@ -63,47 +60,28 @@ export class Timeline extends React.Component { return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; } - - componentWillMount() { - this.props.Document.isAnimating ? this.props.Document.isAnimating = true : this.props.Document.isAnimating = false; - } - componentDidMount() { - if (StrCast(this.props.Document.type) === "video") { - console.log("video"); - console.log(this.props.Document.duration); - if (this.props.Document.duration) { - this._time = Math.round(NumCast(this.props.Document.duration)) * 1000; - this._reactionDisposer.push(reaction(() => { - return NumCast(this.props.Document.curPage); - }, curPage => { - if (!this._isPlaying) { - this.changeCurrentBarX(curPage * this._tickIncrement / this._tickSpacing); - this.props.Document.curPage = this._currentBarX; - this.play(); - } - })); - } - } runInAction(() => { - this._reactionDisposer.push(reaction(() => { - return this._time; - }, () => { - this._ticks = []; - for (let i = 0; i < this._time;) { - this._ticks.push(i); - i += 1000; - } - this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); - }, { fireImmediately: true })); - this._totalLength = this._tickSpacing * (this._ticks.length / this._tickIncrement); + this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; this._visibleStart = this._infoContainer.current!.scrollLeft; }); + } - + /** + * React Functional Component + * Purpose: For drawing Tick marks across the timeline in authoring mode + */ + @action + drawTicks = () => { + let ticks = []; + for (let i = 0; i < this._time / this._tickIncrement; i++){ + ticks.push(

    {this.toReadTime(i * this._tickIncrement)}

    ); + } + return ticks; } + @action changeCurrentBarX = (pixel: number) => { pixel <= 0 ? this._currentBarX = 0 : pixel >= this._totalLength ? this._currentBarX = this._totalLength : this._currentBarX = pixel; @@ -174,23 +152,12 @@ export class Timeline extends React.Component { onScrubberMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let scrubberbox = this._scrubberbox.current!; + let scrubberbox = this._infoContainer.current!; let left = scrubberbox.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; this.changeCurrentBarX(offsetX); } - @action - onScrubberClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - let scrubberbox = this._scrubberbox.current!; - let offsetX = (e.clientX - scrubberbox.getBoundingClientRect().left) * this.props.ScreenToLocalTransform().Scale; - this.changeCurrentBarX(offsetX); - } - - - @action onPanDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -236,7 +203,6 @@ export class Timeline extends React.Component { this._visibleStart = infoContainer.scrollLeft; } - @action onResizeDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -261,8 +227,6 @@ export class Timeline extends React.Component { } } - - @action toReadTime = (time: number): string => { const inSeconds = time / 1000; @@ -276,21 +240,12 @@ export class Timeline extends React.Component { } timelineContextMenu = (e: MouseEvent): void => { - let subitems: ContextMenuProps[] = []; - subitems.push({ - description: this._timelineVisible ? "Hide Timeline" : "Show Timeline", event: action(() => { - this._timelineVisible = !this._timelineVisible; - }), icon: this._timelineVisible ? faEyeSlash : "eye" - }); - subitems.push({ - description: BoolCast(this.props.Document.isAnimating) ? "Enter Play Mode" : "Enter Authoring Mode", event: () => { - BoolCast(this.props.Document.isAnimating) ? this.props.Document.isAnimating = false : this.props.Document.isAnimating = true; - } - , icon: BoolCast(this.props.Document.isAnimating) ? "play" : "edit" - }); - ContextMenu.Instance.addItem({ description: "Timeline Funcs...", subitems: subitems, icon: faClock }); + ContextMenu.Instance.addItem({description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { + this._timelineVisible = !this._timelineVisible; + }), icon: faTimes}); } + @action onWheelZoom = (e: React.WheelEvent) => { e.preventDefault(); @@ -339,14 +294,13 @@ export class Timeline extends React.Component { private timelineToolBox = (scale: number) => { let size = 50 * scale; //50 is default return ( -
    -
    -
    +
    +
    ); @@ -360,7 +314,6 @@ export class Timeline extends React.Component { let roundToggleContainer = this._roundToggleContainerRef.current!; let timelineContainer = this._timelineContainer.current!; if (BoolCast(this.props.Document.isAnimating)) { - roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; @@ -374,7 +327,6 @@ export class Timeline extends React.Component { roundToggleContainer.style.animationName = "turnon"; timelineContainer.style.transform = `translate(0px, ${this._containerHeight}px)`; this.props.Document.isAnimating = true; - } } render() { @@ -384,11 +336,7 @@ export class Timeline extends React.Component {
    -
    - {this._ticks.map(element => { - if (element % this._tickIncrement === 0) return

    {this.toReadTime(element)}

    ; - })} -
    + {this.drawTicks()}
    @@ -406,8 +354,6 @@ export class Timeline extends React.Component {
    {this.timelineToolBox(1)}
    - -
    ); } -- cgit v1.2.3-70-g09d2 From 7d63153af4933b480673f29ffb1331feeb965f49 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 12 Oct 2019 17:02:36 -0400 Subject: resize bug fix, more edge cases, starting interact --- src/client/views/animationtimeline/Keyframe.tsx | 64 ++++++++++--------------- 1 file changed, 25 insertions(+), 39 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 587625efa..810f9a057 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -13,6 +13,7 @@ import { InkField, StrokeData } from "../../../new_fields/InkField"; import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { thisTypeAnnotation } from "babel-types"; export namespace KeyframeFunc { export enum KeyframeType { @@ -280,24 +281,18 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions); - let firstkf: (Doc | undefined) = this.keyframes[0]; - if (firstkf && this.regiondata.position + this.regiondata.fadeIn + offset >= NumCast(firstkf!.time)) { - let dif = NumCast(firstkf!.time) - (this.pixelPosition + this.pixelFadeIn); - 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; + if (leftRegion && this.regiondata.position + offset <= leftRegion.position + leftRegion.duration) { + this.regiondata.position = leftRegion.position + leftRegion.duration; + this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - (leftRegion.position + leftRegion.duration); + } else if (NumCast(this.keyframes[1].time) + offset >= NumCast(this.keyframes[2].time)) { + this.regiondata.position = NumCast(this.keyframes[2].time) - this.regiondata.fadeIn; + this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - NumCast(this.keyframes[2].time) + this.regiondata.fadeIn; } else { this.regiondata.duration -= offset; this.regiondata.position += offset; } - // this.keyframes[0].time = this.regiondata.position; - // this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + this.keyframes[0].time = this.regiondata.position; + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; } @@ -308,17 +303,16 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions); - console.log(offset); - console.log(this.keyframes[this.keyframes.length - 3].time); - console.log(this.regiondata.position + this.regiondata.duration - offset); - if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut - offset <= NumCast(this.keyframes[this.keyframes.length - 3].time)) { - console.log("HI"); + let fadeOutKeyframeTime = NumCast(this.keyframes[this.keyframes.length - 3].time); + if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= fadeOutKeyframeTime) { //case 1: when third to last keyframe is in the way + this.regiondata.duration = fadeOutKeyframeTime - this.regiondata.position + this.regiondata.fadeOut; + } else if (rightRegion && (this.regiondata.position + this.regiondata.duration + offset >= rightRegion.position)){ + this.regiondata.duration = rightRegion.position - this.regiondata.position; } else { this.regiondata.duration += offset; } this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; - } @action @@ -382,10 +376,10 @@ export class Keyframe extends React.Component { @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Add Ease", () => { - // this.onContainerDown(kf, "interpolate"); + this.onContainerDown(kf, "interpolate"); }), TimelineMenu.Instance.addItem("button", "Add Path", () => { - // this.onContainerDown(kf, "path"); + this.onContainerDown(kf, "path"); }), TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{this.regions.splice(this.regions.indexOf(this.regiondata), 1);}), TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { @@ -514,16 +508,6 @@ export class Keyframe extends React.Component { 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(); @@ -605,10 +589,10 @@ export class Keyframe extends React.Component { let bodyRef = React.createRef(); let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); let leftPos = KeyframeFunc.convertPixelTime(NumCast(left!.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - return ( + keyframeDividers.push (
    { this.onContainerOver(e, bodyRef); }} - onPointerOut={(e) => { this.onContainerOut(e, bodyRef); }} + onPointerOver={(e) => { e.preventDefault(); e.stopPropagation(); this.onContainerOver(e, bodyRef); }} + onPointerOut={(e) => { e.preventDefault(); e.stopPropagation(); this.onContainerOut(e, bodyRef); }} onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); @@ -625,10 +609,12 @@ export class Keyframe extends React.Component { render() { return (
    -
    +
    {this.drawKeyframes()} -- cgit v1.2.3-70-g09d2 From 9e914734438a52c5f9bb9a43421418c8ed5f903a Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 19 Oct 2019 16:59:01 -0400 Subject: lots of bug fixes --- src/client/views/animationtimeline/Keyframe.tsx | 25 +++++++++++++++---------- src/client/views/animationtimeline/Timeline.tsx | 16 +++++++++------- src/client/views/animationtimeline/Track.tsx | 1 + src/new_fields/util.ts | 2 +- 4 files changed, 26 insertions(+), 18 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 810f9a057..c872b8740 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -13,7 +13,6 @@ import { InkField, StrokeData } from "../../../new_fields/InkField"; import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { thisTypeAnnotation } from "babel-types"; export namespace KeyframeFunc { export enum KeyframeType { @@ -167,6 +166,7 @@ export class Keyframe extends React.Component { (fadeOut.key! as Doc).opacity = 1; (start.key! as Doc).opacity = 0.1; (finish.key! as Doc).opacity = 0.1; + this.forceUpdate(); }); } @@ -315,6 +315,7 @@ export class Keyframe extends React.Component { this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; } + @action createKeyframe = async (clientX:number) => { this._mouseToggled = true; @@ -327,7 +328,7 @@ export class Keyframe extends React.Component { } } - + @action moveKeyframe = async (e: React.MouseEvent, kf: Doc) => { e.preventDefault(); @@ -346,12 +347,13 @@ export class Keyframe extends React.Component { @action makeKeyframeMenu = (kf :Doc, e:MouseEvent) => { TimelineMenu.Instance.addItem("button", "Show Data", () => { - runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); - CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); - }), + runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); + CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); + }), TimelineMenu.Instance.addItem("button", "Delete", () => { runInAction(() => { (this.regiondata.keyframes as List).splice(this.keyframes.indexOf(kf), 1); + this.forceUpdate(); }); }), TimelineMenu.Instance.addItem("input", "Move", (val) => { @@ -381,7 +383,10 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.addItem("button", "Add Path", () => { this.onContainerDown(kf, "path"); }), - TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{this.regions.splice(this.regions.indexOf(this.regiondata), 1);}), + TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{ + runInAction(() => { + this.regions.splice(this.regions.indexOf(this.regiondata), 1);} + );}), TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { runInAction(() => { if (this.checkInput(val)){ @@ -554,9 +559,9 @@ export class Keyframe extends React.Component { } @action - drawKeyframes = () => { + drawKeyframes = () => { let keyframeDivs:JSX.Element[] = []; - this.keyframes.forEach( kf => { + DocListCast(this.regiondata.keyframes).forEach( kf => { if (kf.type as KeyframeFunc.KeyframeType === KeyframeFunc.KeyframeType.default) { keyframeDivs.push(
    @@ -581,9 +586,9 @@ export class Keyframe extends React.Component { } @action - drawKeyframeDividers = () => { + drawKeyframeDividers = () => { let keyframeDividers:JSX.Element[] = []; - this.keyframes.forEach(kf => { + DocListCast(this.regiondata.keyframes).forEach(kf => { if(this.keyframes.indexOf(kf ) !== this.keyframes.length - 1) { let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; let bodyRef = React.createRef(); diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 9ac5d98d2..666048a54 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -155,7 +155,7 @@ export class Timeline extends React.Component { let scrubberbox = this._infoContainer.current!; let left = scrubberbox.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; - this.changeCurrentBarX(offsetX); + this.changeCurrentBarX(offsetX + this._visibleStart); } @action @@ -201,6 +201,9 @@ export class Timeline extends React.Component { let infoContainer = this._infoContainer.current!; infoContainer.scrollLeft = pixel; this._visibleStart = infoContainer.scrollLeft; + console.log(infoContainer.scrollLeft); + console.log(this._totalLength); + console.log(this._visibleLength); } @action @@ -317,15 +320,14 @@ export class Timeline extends React.Component { roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; - timelineContainer.style.transform = `translate(0px, ${0}px)`; - setTimeout(() => { - this.props.Document.isAnimating = false; - }, 500); + timelineContainer.style.top = `${-this._containerHeight}px`; + this.props.Document.isAnimating = false; + } else { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; - timelineContainer.style.transform = `translate(0px, ${this._containerHeight}px)`; + timelineContainer.style.top = "0px"; this.props.Document.isAnimating = true; } } @@ -334,7 +336,7 @@ export class Timeline extends React.Component {
    -
    +
    {this.drawTicks()}
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 3ec410216..7a8f42ea7 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -270,6 +270,7 @@ export class Track extends React.Component { let inner = this._inner.current!; let offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); + this.forceUpdate(); } createRegion = (position: number) => { diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 04194509c..a92b1858d 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -69,7 +69,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number } else { target.__fields[prop] = value; } - if (typeof value === "object" && !(value instanceof ObjectField)) debugger; + // if (typeof value === "object" && !(value instanceof ObjectField)) debugger; if (writeToServer) { if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } }); else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } }); -- cgit v1.2.3-70-g09d2 From 8e45ed5a6705becf6708c3d9e9740ce8febe65d9 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 20 Oct 2019 17:16:52 -0400 Subject: changed ui input, and more bug fixes --- src/client/views/animationtimeline/Timeline.scss | 68 ++++++++++++++---------- src/client/views/animationtimeline/Timeline.tsx | 51 +++++++++++++++--- 2 files changed, 85 insertions(+), 34 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index cf6b4b3d5..f6a48d876 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -7,10 +7,48 @@ display:flex; align-items: flex-start; flex-direction: row; + justify-content: space-evenly; + align-items: baseline; top: 10px; div{ margin-left:10px; } + .animation-text{ + font-size: 20px; + height: auto; + width: auto; + } + .round-toggle { + height: 40px; + width: 80px; + background-color: white; + border: 2px solid purple; + border-radius: 20px; + animation-fill-mode: forwards; + animation-duration: 500ms; + top: 30px; + input{ + position:absolute; + opacity: 0; + height: 0; + width: 0; + } + .round-toggle-slider{ + height: 35px; + width: 35px; + background-color: white; + border:1px solid grey; + border-radius: 20px; + transition: transform 500ms ease-in-out; + margin-left: 0px; + margin-top: 0.5px; + } + } + .time-input{ + height: 40px; + width: 100px; + + } } .tick{ @@ -112,38 +150,12 @@ float: left 0px; top: 25%; height: 75%; - width: 100%; + width: 100%; background-color: grey; } } -.round-toggle { - height: 40px; - width: 80px; - background-color: white; - border: 2px solid purple; - border-radius: 20px; - animation-fill-mode: forwards; - animation-duration: 500ms; - top: 20px; - input{ - position:absolute; - opacity: 0; - height: 0; - width: 0; - } - .round-toggle-slider{ - height: 35px; - width: 35px; - background-color: white; - border:1px solid grey; - border-radius: 20px; - transition: transform 500ms ease-in-out; - margin-left: 0px; - margin-top: 0.5px; - } - -} + @keyframes turnon{ from{ background-color: white; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 666048a54..30fa70b12 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -31,6 +31,7 @@ export class Timeline extends React.Component { @observable private _infoContainer = React.createRef(); @observable private _roundToggleRef = React.createRef(); @observable private _roundToggleContainerRef = React.createRef(); + @observable private _timeInputRef = React.createRef(); @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @@ -62,12 +63,27 @@ export class Timeline extends React.Component { componentDidMount() { runInAction(() => { + if (!this.props.Document.AnimationLength){ + this.props.Document.AnimationLength = this._time; + } else { + this._time = NumCast(this.props.Document.AnimationLength); + console.log(this._time); + } this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; this._visibleStart = this._infoContainer.current!.scrollLeft; + this.props.Document.isAnimating = !this.props.Document.isAnimating; + this.toggleHandle(); }); } + componentWillUnmount(){ + runInAction(() => { + console.log(this._time); + this.props.Document.AnimationLength = this._time; + }); + } + /** * React Functional Component * Purpose: For drawing Tick marks across the timeline in authoring mode @@ -194,16 +210,20 @@ export class Timeline extends React.Component { let titleContainer = this._titleContainer.current!; this.movePanX(this._visibleStart - e.movementX); trackbox.scrollTop = trackbox.scrollTop - e.movementY; - titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; + titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; + if (this._visibleStart + this._visibleLength + 20>= this._totalLength){ + this._visibleStart -= e.movementX; + this._totalLength -= e.movementX; + this._time -= KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this._tickSpacing, this._tickIncrement); + this.props.Document.AnimationLength = this._time; + } + } @action movePanX = (pixel: number) => { let infoContainer = this._infoContainer.current!; infoContainer.scrollLeft = pixel; this._visibleStart = infoContainer.scrollLeft; - console.log(infoContainer.scrollLeft); - console.log(this._totalLength); - console.log(this._visibleLength); } @action @@ -300,19 +320,39 @@ export class Timeline extends React.Component {
    -
    +
    +

    Timeline Overview

    +

    Mode: {this.props.Document.isAnimating ? "Authoring" : "Play"}

    +

    Length:

    + +
    ); } + + @action + private onTimeInput = (e: React.KeyboardEvent) => { + if (e.keyCode === 13){ + let timeInput = this._timeInputRef.current!; + this._time = parseInt(timeInput.value, 10); + this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); + this.props.Document.AnimationLength = this._time; + + } + } @action private toggleChecked = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); + this.toggleHandle(); + } + + private toggleHandle = () => { let roundToggle = this._roundToggleRef.current!; let roundToggleContainer = this._roundToggleContainerRef.current!; let timelineContainer = this._timelineContainer.current!; @@ -322,7 +362,6 @@ export class Timeline extends React.Component { roundToggleContainer.style.animationName = "turnoff"; timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isAnimating = false; - } else { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; -- cgit v1.2.3-70-g09d2 From 21621ac406b7703811ac40b429c51dac752dd142 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 26 Oct 2019 16:47:42 -0400 Subject: ui changes, bug fix, icon fix --- src/client/views/animationtimeline/Keyframe.tsx | 1 + src/client/views/animationtimeline/Timeline.scss | 21 +++++++-------------- src/client/views/animationtimeline/Timeline.tsx | 10 ++++++---- 3 files changed, 14 insertions(+), 18 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index c872b8740..6a5163cde 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -125,6 +125,7 @@ interface IProps { tickSpacing: number; tickIncrement: number; time: number; + changeCurrentBarX: (x: number) => void; transform: Transform; } diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index f6a48d876..8317a3606 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -156,19 +156,12 @@ } -@keyframes turnon{ - from{ - background-color: white; - } - to{ - background-color: purple; - } -} -@keyframes turnoff{ - from{ - background-color: purple; - } - to{ - background-color: white; +.timeline-checker{ + height: auto; + width: auto; + position: absolute; + .check{ + width: 100px; + height: 100px; } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 30fa70b12..0b6b06aaa 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -8,7 +8,7 @@ import { Cast, NumCast, StrCast, BoolCast } 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, faEyeSlash, faTimes } from "@fortawesome/free-solid-svg-icons"; +import { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDown, faClock, faPauseCircle, faEyeSlash, faTimes, faEye, faCheck, faCross} from "@fortawesome/free-solid-svg-icons"; import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; @@ -265,7 +265,7 @@ export class Timeline extends React.Component { timelineContextMenu = (e: MouseEvent): void => { ContextMenu.Instance.addItem({description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { this._timelineVisible = !this._timelineVisible; - }), icon: faTimes}); + }), icon: this._timelineVisible ? faEyeSlash : faEye }); } @@ -329,7 +329,6 @@ export class Timeline extends React.Component {

    Length:

    -
    ); } @@ -341,7 +340,6 @@ export class Timeline extends React.Component { this._time = parseInt(timeInput.value, 10); this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); this.props.Document.AnimationLength = this._time; - } } @@ -392,6 +390,10 @@ export class Timeline extends React.Component {
    +
    + + +
    {this.timelineToolBox(1)}
    -- cgit v1.2.3-70-g09d2 From 2b7b564aa051db78c89d14ff48cf38ace2f42b37 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 27 Oct 2019 16:42:34 -0400 Subject: confirm box, regiondata bugfix --- src/client/views/animationtimeline/Keyframe.tsx | 24 ++++++++++------ src/client/views/animationtimeline/Timeline.scss | 16 +++++++++-- src/client/views/animationtimeline/Timeline.tsx | 36 ++++++++++++++++++++---- src/client/views/animationtimeline/Track.tsx | 3 ++ 4 files changed, 62 insertions(+), 17 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 6a5163cde..35d7afc7a 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -125,9 +125,10 @@ interface IProps { tickSpacing: number; tickIncrement: number; time: number; - + check: string; changeCurrentBarX: (x: number) => void; transform: Transform; + checkCallBack: (visible: boolean) => void; } @observer @@ -137,6 +138,7 @@ export class Keyframe extends React.Component { @observable private _gain = 20; //default @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; + @observable private _index:number = 0; @computed private get regiondata() { return RegionData(this.regions[this.regions.indexOf(this.props.RegionData)] as Doc);} @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} @@ -386,7 +388,7 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{ runInAction(() => { - this.regions.splice(this.regions.indexOf(this.regiondata), 1);} + this.regions.splice(this.regions.indexOf(this.props.RegionData), 1);} );}), TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { runInAction(() => { @@ -492,10 +494,12 @@ export class Keyframe extends React.Component { private _plotList: ([string, StrokeData] | undefined) = undefined; private _interpolationKeyframe: (Doc | undefined) = undefined; private _type: string = ""; + @action onContainerDown = (kf: Doc, type: string) => { - let listenerCreated = false; + let listenerCreated = false; + this.props.checkCallBack(true); this._type = type; this.props.collection.backgroundColor = "rgb(0,0,0)"; this._reac = reaction(() => { @@ -504,16 +508,20 @@ export class Keyframe extends React.Component { if (!listenerCreated) { this._plotList = Array.from(data!)[data!.size - 1]!; this._interpolationKeyframe = kf; - document.addEventListener("pointerup", this.onReactionListen); listenerCreated = true; + const reac = reaction(() => { + return this.props.check; + }, () => { + if(this.props.check === "yes") this.onReactionListen(); + reac(); + this.props.checkCallBack(false); + }); } }); } @action - onReactionListen = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); + onReactionListen = () => { if (this._reac && this._plotList && this._interpolationKeyframe) { this.props.collection.backgroundColor = "#FFF"; this._reac(); @@ -554,8 +562,6 @@ export class Keyframe extends React.Component { this._reac = undefined; this._interpolationKeyframe = undefined; this._plotList = undefined; - this._type = ""; - document.removeEventListener("pointerup", this.onReactionListen); } } diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 8317a3606..0f98ed7c4 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -159,9 +159,19 @@ .timeline-checker{ height: auto; width: auto; - position: absolute; + overflow: hidden; + position: absolute; + display: flex; + padding: 10px 10px; + div{ + height: auto; + width: auto; + overflow: hidden; + margin: 0px 10px; + cursor: pointer + } .check{ - width: 100px; - height: 100px; + width: 50px; + height: 50px; } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 0b6b06aaa..6028656dc 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -8,7 +8,7 @@ import { Cast, NumCast, StrCast, BoolCast } 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, faEyeSlash, faTimes, faEye, faCheck, faCross} from "@fortawesome/free-solid-svg-icons"; +import { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDown, faClock, faPauseCircle, faEyeSlash, faTimes, faEye, faCheck, faCross, faCheckCircle, faTimesCircle} from "@fortawesome/free-solid-svg-icons"; import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; @@ -368,6 +368,28 @@ export class Timeline extends React.Component { this.props.Document.isAnimating = true; } } + + + @observable private _check:string = ""; + @observable private _checkVisible:boolean = false; + @action + private onCheckClicked = (type: string) => { + if (type === "yes"){ + this._check = "yes"; + } else if (type === "no"){ + this._check = "no"; + } + } + + + @action + private checkCallBack = (visible:boolean) => { + this._checkVisible = visible; + if (!visible){ //when user confirms + this._check = ""; + } + + } render() { return (
    @@ -380,7 +402,7 @@ export class Timeline extends React.Component {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => )}
    @@ -390,9 +412,13 @@ export class Timeline extends React.Component {
    -
    - - +
    +
    {this.onCheckClicked("yes");}}> + +
    +
    {this.onCheckClicked("no");}}> + +
    {this.timelineToolBox(1)} diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 7a8f42ea7..14e309f68 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -20,7 +20,10 @@ interface IProps { tickIncrement: number; tickSpacing: number; timelineVisible: boolean; + check: string; changeCurrentBarX: (x: number) => void; + checkCallBack: (visible: boolean) => void; + } @observer -- cgit v1.2.3-70-g09d2 From 7977625e5b408d6e47432caaf7382aa315fc9832 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 3 Nov 2019 16:48:56 -0500 Subject: changes to timeline overview --- src/client/views/animationtimeline/Keyframe.tsx | 1 - src/client/views/animationtimeline/Timeline.scss | 35 +++++++---- src/client/views/animationtimeline/Timeline.tsx | 10 +-- .../views/animationtimeline/TimelineOverview.scss | 73 ++++++++++++++++++---- .../views/animationtimeline/TimelineOverview.tsx | 30 ++++++--- 5 files changed, 109 insertions(+), 40 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 35d7afc7a..55a2b2075 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -138,7 +138,6 @@ export class Keyframe extends React.Component { @observable private _gain = 20; //default @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; - @observable private _index:number = 0; @computed private get regiondata() { return RegionData(this.regions[this.regions.indexOf(this.props.RegionData)] as Doc);} @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 0f98ed7c4..6270eed41 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -4,25 +4,33 @@ .timeline-toolbox{ position:absolute; + margin: 0px; + padding: 0px; display:flex; align-items: flex-start; - flex-direction: row; - justify-content: space-evenly; - align-items: baseline; - top: 10px; + flex-direction: row; + justify-content: space-evenly; + align-items: center; + top: 15px; div{ + padding: 0px; margin-left:10px; } .animation-text{ font-size: 20px; height: auto; width: auto; + white-space: nowrap; + font-size: 16px; + color: grey; + letter-spacing: 2px; + text-transform: uppercase; } .round-toggle { height: 40px; width: 80px; background-color: white; - border: 2px solid purple; + border: 2px solid grey; border-radius: 20px; animation-fill-mode: forwards; animation-duration: 500ms; @@ -44,13 +52,18 @@ margin-top: 0.5px; } } - .time-input{ - height: 40px; - width: 100px; - - } + +} +.time-input{ + height: 40px; + width: 100px; + white-space: nowrap; + font-size: 16px; + color: grey; + letter-spacing: 2px; + text-transform: uppercase; + } - .tick{ height:100%; width: 1px; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 6028656dc..94cd0629d 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -321,14 +321,14 @@ export class Timeline extends React.Component {
    -

    Timeline Overview

    - -

    Mode: {this.props.Document.isAnimating ? "Authoring" : "Play"}

    +
    Timeline Overview
    + +
    Mode: {this.props.Document.isAnimating ? "Authoring" : "Play"}
    -

    Length:

    - +
    Length:
    +
    ); } diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index 9e69c2adf..c7f9bd059 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -1,38 +1,85 @@ @import "./../globalCssVariables.scss"; -.timeline-overview-container{ + +.timeline-overview-container{ + padding: 0px; + margin: 0px; width: 300px; height: 40px; - margin-top: 10px; - margin-left: 20px; background: white; + position: relative; border: 2px solid black; - padding: 0px; - display:inline-block; + .timeline-overview-visible{ + position: absolute; height: 100%; background: green; + display: inline-block; margin: 0px; + padding: 0px; } - .timeline-overview-scrubber-container{ + .timeline-overview-scrubber-container{ + margin: 0px; + padding: 0px; + position: absolute; height: 100%; - margin-top: -40px; - margin-left: 0px; width: 2px; + top: 0px; + left: 0px; z-index: 1001; background-color:black; display: inline-block; - .timeline-overview-scrubber-head{ + + .timeline-overview-scrubber-head{ + padding: 0px; + margin: 0px; position:absolute; height: 30px; width: 30px; background-color:transparent; border-radius: 50%; border: 5px solid black; - margin-left: -15px; - top: -15px; - + left: -15px; + top: -30px; } - } +} + + + +.timeline-play-bar{ + position: relative; + padding: 0px; + margin: 0px; + width: 300px; + height: 4px; + background-color: grey; + border-radius: 20px; + cursor: pointer; + + .timeline-play-head{ + position: absolute; + padding: 0px; + margin: 0px; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: white; + border: 3px grey solid; + left: 0px; + top: -10px; + cursor: pointer; + } +} +.timeline-play-tail{ + position: absolute; + padding: 0px; + margin: 0px; + height: 4px; + width: 0px; + z-index: 1000; + background-color: green; + border-radius: 20px; + margin-top: -4px; + cursor: pointer; } \ No newline at end of file diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 38b823cbc..4741969dc 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -6,11 +6,11 @@ import "./TimelineOverview.scss"; interface TimelineOverviewProps{ - scale: number; totalLength: number; visibleLength:number; visibleStart:number; - currentBarX:number; + currentBarX:number; + isAuthoring: boolean; changeCurrentBarX: (pixel:number) => void; movePanX: (pixel:number) => any; } @@ -37,8 +37,8 @@ export class TimelineOverview extends React.Component{ onPanX = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); - let movX = (this.props.visibleStart / this.props.totalLength)* (this.DEFAULT_WIDTH * this.props.scale) + e.movementX; - this.props.movePanX((movX / (this.DEFAULT_WIDTH * this.props.scale)) * this.props.totalLength); + let movX = (this.props.visibleStart / this.props.totalLength)* (this.DEFAULT_WIDTH) + e.movementX; + this.props.movePanX((movX / (this.DEFAULT_WIDTH )) * this.props.totalLength); } @action @@ -66,7 +66,7 @@ export class TimelineOverview extends React.Component{ let scrubberRef = this._scrubberRef.current!; let left = scrubberRef.getBoundingClientRect().left; let offsetX = Math.round(e.clientX - left); - this.props.changeCurrentBarX(((offsetX / (this.DEFAULT_WIDTH * this.props.scale)) * this.props.totalLength) + this.props.currentBarX); + this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); } @action @@ -78,12 +78,22 @@ export class TimelineOverview extends React.Component{ } render(){ + let timeline = this.props.isAuthoring ? [ +
    +
    , +
    +
    +
    +
    + ] : [ +
    +
    +
    , +
    + ]; return( -
    -
    -
    -
    -
    +
    + {timeline}
    ); } -- cgit v1.2.3-70-g09d2 From 4d4db3aecf54a501a981c239feacdfddfe71152c Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 12 Nov 2019 17:20:21 -0500 Subject: bug fix with ui changes --- src/client/views/animationtimeline/Keyframe.tsx | 13 ++++++++----- src/client/views/animationtimeline/Timeline.scss | 5 +++-- src/client/views/animationtimeline/Timeline.tsx | 10 ++++++---- 3 files changed, 17 insertions(+), 11 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 55a2b2075..424f4ed8d 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -572,11 +572,11 @@ export class Keyframe extends React.Component { keyframeDivs.push(
    -
    { this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { +
    { e.preventDefault(); e.stopPropagation(); this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); this.makeKeyframeMenu(kf, e.nativeEvent); - }}>
    + }} onDoubleClick={(e) => {e.preventDefault(); e.stopPropagation(); }}>
    ); } @@ -595,7 +595,8 @@ export class Keyframe extends React.Component { drawKeyframeDividers = () => { let keyframeDividers:JSX.Element[] = []; DocListCast(this.regiondata.keyframes).forEach(kf => { - if(this.keyframes.indexOf(kf ) !== this.keyframes.length - 1) { + let index = this.keyframes.indexOf(kf); + if(index !== this.keyframes.length - 1 ) { let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; let bodyRef = React.createRef(); let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); @@ -606,8 +607,10 @@ export class Keyframe extends React.Component { onPointerOut={(e) => { e.preventDefault(); e.stopPropagation(); this.onContainerOut(e, bodyRef); }} onContextMenu={(e) => { e.preventDefault(); - e.stopPropagation(); - this._mouseToggled = true; + e.stopPropagation(); + if (index !== 0 || index !== this.keyframes.length - 2){ + this._mouseToggled = true; + } this.makeRegionMenu(kf, e.nativeEvent); }}>
    diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 6270eed41..88d602d76 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -11,7 +11,7 @@ flex-direction: row; justify-content: space-evenly; align-items: center; - top: 15px; + top: 20px; div{ padding: 0px; margin-left:10px; @@ -56,12 +56,13 @@ } .time-input{ height: 40px; - width: 100px; + width: 120px; white-space: nowrap; font-size: 16px; color: grey; letter-spacing: 2px; text-transform: uppercase; + padding-left: 5px; } .tick{ diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 94cd0629d..73e6c600f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -315,12 +315,12 @@ export class Timeline extends React.Component { } private timelineToolBox = (scale: number) => { - let size = 50 * scale; //50 is default + let size = 40 * scale; //50 is default return (
    -
    -
    -
    +
    +
    +
    Timeline Overview
    Mode: {this.props.Document.isAnimating ? "Authoring" : "Play"}
    @@ -358,12 +358,14 @@ export class Timeline extends React.Component { roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; + roundToggleContainer.style.backgroundColor = "white"; timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isAnimating = false; } else { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; + roundToggleContainer.style.backgroundColor = "green"; timelineContainer.style.top = "0px"; this.props.Document.isAnimating = true; } -- cgit v1.2.3-70-g09d2 From 13de65ec4ca3fbeef4a687d079e7b480bd691764 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 12 Nov 2019 18:15:32 -0500 Subject: isanimating fix --- src/client/views/animationtimeline/Timeline.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 73e6c600f..43019903e 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -72,7 +72,7 @@ export class Timeline extends React.Component { this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; this._visibleStart = this._infoContainer.current!.scrollLeft; - this.props.Document.isAnimating = !this.props.Document.isAnimating; + this.props.Document.isATOn = !this.props.Document.isATOn; this.toggleHandle(); }); } @@ -322,13 +322,13 @@ export class Timeline extends React.Component {
    Timeline Overview
    - -
    Mode: {this.props.Document.isAnimating ? "Authoring" : "Play"}
    + +
    Mode: {this.props.Document.isATOn ? "Authoring" : "Play"}
    -
    Length:
    - +
    Length:
    +
    ); } @@ -354,20 +354,20 @@ export class Timeline extends React.Component { let roundToggle = this._roundToggleRef.current!; let roundToggleContainer = this._roundToggleContainerRef.current!; let timelineContainer = this._timelineContainer.current!; - if (BoolCast(this.props.Document.isAnimating)) { + if (BoolCast(this.props.Document.isATOn)) { roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; roundToggleContainer.style.backgroundColor = "white"; timelineContainer.style.top = `${-this._containerHeight}px`; - this.props.Document.isAnimating = false; + this.props.Document.isATOn = false; } else { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; roundToggleContainer.style.backgroundColor = "green"; timelineContainer.style.top = "0px"; - this.props.Document.isAnimating = true; + this.props.Document.isATOn = true; } } @@ -396,7 +396,7 @@ export class Timeline extends React.Component { return (
    -
    +
    {this.drawTicks()} -- cgit v1.2.3-70-g09d2 From d7f772d93458e7669a1d2e954ad741e1c67318a5 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 12 Nov 2019 19:01:42 -0500 Subject: undo --- src/client/views/animationtimeline/Keyframe.tsx | 456 +++++++++++++----------- src/new_fields/util.ts | 3 +- 2 files changed, 240 insertions(+), 219 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 424f4ed8d..a2d0a644e 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import "./Keyframe.scss"; import "./Timeline.scss"; import "../globalCssVariables.scss"; -import { observer} from "mobx-react"; +import { observer } from "mobx-react"; import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction } from "mobx"; import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { Cast, NumCast } from "../../../new_fields/Types"; @@ -13,6 +13,7 @@ import { InkField, StrokeData } from "../../../new_fields/InkField"; import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; export namespace KeyframeFunc { export enum KeyframeType { @@ -86,23 +87,23 @@ export namespace KeyframeFunc { regiondata.position = 0; regiondata.fadeIn = 1000; regiondata.fadeOut = 1000; - regiondata.functions = new List(); + regiondata.functions = new List(); return regiondata; }; - export const convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time", tickSpacing:number, tickIncrement:number) => { - let time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement; + export const convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time", tickSpacing: number, tickIncrement: number) => { + let time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement; switch (unit) { case "mili": - return time; + return time; case "sec": - return dir === "pixel" ? time / 1000 : time * 1000; + return dir === "pixel" ? time / 1000 : time * 1000; case "min": - return dir === "pixel" ? time / 60000 : time * 60000; + return dir === "pixel" ? time / 60000 : time * 60000; case "hr": - return dir === "pixel" ? time / 3600000 : time * 3600000; - default: - return time; + return dir === "pixel" ? time / 3600000 : time * 3600000; + default: + return time; } }; } @@ -113,7 +114,7 @@ export const RegionDataSchema = createSchema({ keyframes: listSpec(Doc), fadeIn: defaultSpec("number", 0), fadeOut: defaultSpec("number", 0), - functions: listSpec(Doc) + functions: listSpec(Doc) }); export type RegionData = makeInterface<[typeof RegionDataSchema]>; export const RegionData = makeInterface(RegionDataSchema); @@ -122,13 +123,13 @@ interface IProps { node: Doc; RegionData: Doc; collection: Doc; - tickSpacing: number; - tickIncrement: number; - time: number; - check: string; + tickSpacing: number; + tickIncrement: number; + time: number; + check: string; changeCurrentBarX: (x: number) => void; transform: Transform; - checkCallBack: (visible: boolean) => void; + checkCallBack: (visible: boolean) => void; } @observer @@ -136,16 +137,16 @@ export class Keyframe extends React.Component { @observable private _bar = React.createRef(); @observable private _gain = 20; //default - @observable private _mouseToggled = false; - @observable private _doubleClickEnabled = false; - - @computed private get regiondata() { return RegionData(this.regions[this.regions.indexOf(this.props.RegionData)] as Doc);} - @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} - @computed private get keyframes(){ return DocListCast(this.regiondata.keyframes); } - @computed private get pixelPosition(){ return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement);} - @computed private get pixelDuration(){ return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } + @observable private _mouseToggled = false; + @observable private _doubleClickEnabled = false; + + @computed private get regiondata() { return RegionData(this.regions[this.regions.indexOf(this.props.RegionData)] as Doc); } + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } + @computed private get keyframes() { return DocListCast(this.regiondata.keyframes); } + @computed private get pixelPosition() { return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } + @computed private get pixelDuration() { return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeIn() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } - @computed private get pixelFadeOut(){ return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } + @computed private get pixelFadeOut() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get inks() { if (this.props.collection.data_ext) { @@ -168,12 +169,13 @@ export class Keyframe extends React.Component { (fadeOut.key! as Doc).opacity = 1; (start.key! as Doc).opacity = 0.1; (finish.key! as Doc).opacity = 0.1; - this.forceUpdate(); - }); + this.forceUpdate(); + }); } @action makeKeyData = async (kfpos: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time + const batch = UndoManager.StartBatch("makeKeyData"); let doclist = (await DocListCastAsync(this.regiondata.keyframes))!; let existingkf: (Doc | undefined) = undefined; doclist.forEach(TK => { @@ -183,45 +185,54 @@ export class Keyframe extends React.Component { let TK: Doc = new Doc(); TK.time = kfpos; TK.key = Doc.MakeCopy(this.props.node, true); - TK.type = type; + TK.type = type; this.regiondata.keyframes!.push(TK); - let interpolationFunctions = new Doc(); - interpolationFunctions.interpolationX = new List([0, 1]); - interpolationFunctions.interpolationY = new List([0,100]); - interpolationFunctions.pathX = new List(); - interpolationFunctions.pathY = new List(); + // myObservable++; + // UndoManager.AddEvent({ + // undo: action(() => myObservable--), + // redo: action(() => myObservable++) + // }); + + let interpolationFunctions = new Doc(); + interpolationFunctions.interpolationX = new List([0, 1]); + interpolationFunctions.interpolationY = new List([0, 100]); + interpolationFunctions.pathX = new List(); + interpolationFunctions.pathY = new List(); - this.regiondata.functions!.push(interpolationFunctions); - let found:boolean = false; + this.regiondata.functions!.push(interpolationFunctions); + let found: boolean = false; this.regiondata.keyframes!.forEach(compkf => { - compkf = compkf as Doc; + 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; + this.regiondata.keyframes!.pop(); + found = true; + }); + return; } }); + batch.end(); return TK; } - + @action onBarPointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let clientX = e.clientX; - if (this._doubleClickEnabled){ - this.createKeyframe(clientX); - this._doubleClickEnabled = false; + let clientX = e.clientX; + if (this._doubleClickEnabled) { + this.createKeyframe(clientX); + this._doubleClickEnabled = false; } else { - setTimeout(() => {if(!this._mouseToggled && this._doubleClickEnabled)this.props.changeCurrentBarX(this.pixelPosition + (clientX - this._bar.current!.getBoundingClientRect().left) * this.props.transform.Scale); - this._mouseToggled = false; - this._doubleClickEnabled = false; }, 200); - this._doubleClickEnabled = true; + setTimeout(() => { + if (!this._mouseToggled && this._doubleClickEnabled) this.props.changeCurrentBarX(this.pixelPosition + (clientX - this._bar.current!.getBoundingClientRect().left) * this.props.transform.Scale); + this._mouseToggled = false; + this._doubleClickEnabled = false; + }, 200); + this._doubleClickEnabled = true; document.addEventListener("pointermove", this.onBarPointerMove); document.addEventListener("pointerup", (e: PointerEvent) => { document.removeEventListener("pointermove", this.onBarPointerMove); @@ -235,7 +246,7 @@ export class Keyframe extends React.Component { e.preventDefault(); e.stopPropagation(); if (e.movementX !== 0) { - this._mouseToggled = true; + this._mouseToggled = true; } let left = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions)!; let right = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions!); @@ -249,10 +260,10 @@ export class Keyframe extends React.Component { this.regiondata.position = right.position - this.regiondata.duration; } else { this.regiondata.position = futureX; - } - let movement = this.regiondata.position - prevX; + } + let movement = this.regiondata.position - prevX; this.keyframes.forEach(kf => { - kf.time = NumCast(kf.time) + movement; + kf.time = NumCast(kf.time) + movement; }); } @@ -284,17 +295,17 @@ export class Keyframe extends React.Component { let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions); if (leftRegion && this.regiondata.position + offset <= leftRegion.position + leftRegion.duration) { - this.regiondata.position = leftRegion.position + leftRegion.duration; - this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - (leftRegion.position + leftRegion.duration); + this.regiondata.position = leftRegion.position + leftRegion.duration; + this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - (leftRegion.position + leftRegion.duration); } else if (NumCast(this.keyframes[1].time) + offset >= NumCast(this.keyframes[2].time)) { - this.regiondata.position = NumCast(this.keyframes[2].time) - this.regiondata.fadeIn; - this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - NumCast(this.keyframes[2].time) + this.regiondata.fadeIn; + this.regiondata.position = NumCast(this.keyframes[2].time) - this.regiondata.fadeIn; + this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - NumCast(this.keyframes[2].time) + this.regiondata.fadeIn; } else { this.regiondata.duration -= offset; this.regiondata.position += offset; } - this.keyframes[0].time = this.regiondata.position; - this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + this.keyframes[0].time = this.regiondata.position; + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; } @@ -305,22 +316,22 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions); - let fadeOutKeyframeTime = NumCast(this.keyframes[this.keyframes.length - 3].time); + let fadeOutKeyframeTime = NumCast(this.keyframes[this.keyframes.length - 3].time); if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= fadeOutKeyframeTime) { //case 1: when third to last keyframe is in the way this.regiondata.duration = fadeOutKeyframeTime - this.regiondata.position + this.regiondata.fadeOut; - } else if (rightRegion && (this.regiondata.position + this.regiondata.duration + offset >= rightRegion.position)){ - this.regiondata.duration = rightRegion.position - this.regiondata.position; + } else if (rightRegion && (this.regiondata.position + this.regiondata.duration + offset >= rightRegion.position)) { + this.regiondata.duration = rightRegion.position - this.regiondata.position; } else { this.regiondata.duration += offset; } - this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; - this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; + this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; } @action - createKeyframe = async (clientX:number) => { - this._mouseToggled = true; + createKeyframe = async (clientX: number) => { + this._mouseToggled = true; let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends @@ -330,7 +341,7 @@ export class Keyframe extends React.Component { } } - + @action moveKeyframe = async (e: React.MouseEvent, kf: Doc) => { e.preventDefault(); @@ -346,126 +357,134 @@ export class Keyframe extends React.Component { this.props.node.backgroundColor = "#000000"; } - @action - makeKeyframeMenu = (kf :Doc, e:MouseEvent) => { + @action + makeKeyframeMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Show Data", () => { - runInAction(() => {let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); - CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); - }), - TimelineMenu.Instance.addItem("button", "Delete", () => { - runInAction(() => { - (this.regiondata.keyframes as List).splice(this.keyframes.indexOf(kf), 1); - this.forceUpdate(); - }); - }), - TimelineMenu.Instance.addItem("input", "Move", (val) => { runInAction(() => { - if (this.checkInput(val)){ - let cannotMove:boolean = false; - let kfIndex:number = this.keyframes.indexOf(kf); - if (val < 0 ||( val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time)) ){ - cannotMove = true; - } - if (!cannotMove){ - this.keyframes[kfIndex].time = parseInt(val, 10); - this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); + CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); + }); + }), + TimelineMenu.Instance.addItem("button", "Delete", () => { + runInAction(() => { + (this.regiondata.keyframes as List).splice(this.keyframes.indexOf(kf), 1); + this.forceUpdate(); + }); + }), + TimelineMenu.Instance.addItem("input", "Move", (val) => { + runInAction(() => { + if (this.checkInput(val)) { + let cannotMove: boolean = false; + let kfIndex: number = this.keyframes.indexOf(kf); + if (val < 0 || (val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time))) { + cannotMove = true; + } + if (!cannotMove) { + this.keyframes[kfIndex].time = parseInt(val, 10); + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + } } - } - }); + }); }); - TimelineMenu.Instance.addMenu("Keyframe"); - TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + TimelineMenu.Instance.addMenu("Keyframe"); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } - @action + @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Add Ease", () => { this.onContainerDown(kf, "interpolate"); }), - TimelineMenu.Instance.addItem("button", "Add Path", () => { - this.onContainerDown(kf, "path"); - }), - TimelineMenu.Instance.addItem("button", "Remove Region", ()=>{ - runInAction(() => { - this.regions.splice(this.regions.indexOf(this.props.RegionData), 1);} - );}), - TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { - runInAction(() => { - if (this.checkInput(val)){ - let cannotMove:boolean = false; - if (val < 0 || val > NumCast(this.keyframes[2].time) - this.regiondata.position){ - cannotMove = true; - } - if (!cannotMove){ - this.regiondata.fadeIn = parseInt(val, 10); - this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; - } + TimelineMenu.Instance.addItem("button", "Add Path", () => { + this.onContainerDown(kf, "path"); + }), + TimelineMenu.Instance.addItem("button", "Remove Region", () => { + runInAction(() => { + this.regions.splice(this.regions.indexOf(this.props.RegionData), 1); } - });}), - TimelineMenu.Instance.addItem("input", `fadeOut: ${this.regiondata.fadeOut}ms`, (val) => { - runInAction(() => { - if (this.checkInput(val)){ - let cannotMove:boolean = false; - if (val < 0 || val > this.regiondata.position + this.regiondata.duration - NumCast(this.keyframes[this.keyframes.length - 3].time)){ - cannotMove = true; - } - if (!cannotMove){ - this.regiondata.fadeOut = parseInt(val, 10); - this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - val; + ); + }), + TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)) { + let cannotMove: boolean = false; + if (val < 0 || val > NumCast(this.keyframes[2].time) - this.regiondata.position) { + cannotMove = true; + } + if (!cannotMove) { + this.regiondata.fadeIn = parseInt(val, 10); + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + } } - } - });}), - TimelineMenu.Instance.addItem("input", `position: ${this.regiondata.position}ms`, (val) => { - runInAction(() => { - if (this.checkInput(val)){ - let prevPosition = this.regiondata.position; - let cannotMove:boolean = false; - DocListCast(this.regions).forEach(region => { - if (NumCast(region.position) !== this.regiondata.position){ - if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration)))){ + }); + }), + TimelineMenu.Instance.addItem("input", `fadeOut: ${this.regiondata.fadeOut}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)) { + let cannotMove: boolean = false; + if (val < 0 || val > this.regiondata.position + this.regiondata.duration - NumCast(this.keyframes[this.keyframes.length - 3].time)) { cannotMove = true; } + if (!cannotMove) { + this.regiondata.fadeOut = parseInt(val, 10); + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - val; + } } }); - if (!cannotMove) { - this.regiondata.position = parseInt(val, 10); - this.updateKeyframes(this.regiondata.position - prevPosition ); - } - } - });}), - TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => { - runInAction(() => { - if (this.checkInput(val)){ - let cannotMove:boolean = false; - DocListCast(this.regions).forEach(region => { - if (NumCast(region.position) !== this.regiondata.position){ - val += this.regiondata.position; - if ((val < 0 ) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration))){ - cannotMove = true; + }), + TimelineMenu.Instance.addItem("input", `position: ${this.regiondata.position}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)) { + let prevPosition = this.regiondata.position; + let cannotMove: boolean = false; + DocListCast(this.regions).forEach(region => { + if (NumCast(region.position) !== this.regiondata.position) { + if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration)))) { + cannotMove = true; + } } + }); + if (!cannotMove) { + this.regiondata.position = parseInt(val, 10); + this.updateKeyframes(this.regiondata.position - prevPosition); } - }); - if (!cannotMove) { - this.regiondata.duration = parseInt(val, 10); - this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; - this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; } - } - });}), - TimelineMenu.Instance.addMenu("Region"); - TimelineMenu.Instance.openMenu(e.clientX, e.clientY); + }); + }), + TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => { + runInAction(() => { + if (this.checkInput(val)) { + let cannotMove: boolean = false; + DocListCast(this.regions).forEach(region => { + if (NumCast(region.position) !== this.regiondata.position) { + val += this.regiondata.position; + if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration))) { + cannotMove = true; + } + } + }); + if (!cannotMove) { + this.regiondata.duration = parseInt(val, 10); + this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; + } + } + }); + }), + TimelineMenu.Instance.addMenu("Region"); + TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } @action checkInput = (val: any) => { - return typeof(val === "number"); + return typeof (val === "number"); } @action - updateKeyframes = (incr:number, filter:number[] = []) => { + updateKeyframes = (incr: number, filter: number[] = []) => { this.keyframes.forEach(kf => { - if (!filter.includes(this.keyframes.indexOf(kf))){ - kf.time = NumCast(kf.time) + incr; + if (!filter.includes(this.keyframes.indexOf(kf))) { + kf.time = NumCast(kf.time) + incr; } }); } @@ -476,7 +495,7 @@ export class Keyframe extends React.Component { e.stopPropagation(); let div = ref.current!; div.style.opacity = "1"; - Doc.BrushDoc(this.props.node); + Doc.BrushDoc(this.props.node); } @action @@ -485,36 +504,36 @@ export class Keyframe extends React.Component { e.stopPropagation(); let div = ref.current!; div.style.opacity = "0"; - Doc.UnBrushDoc(this.props.node); + Doc.UnBrushDoc(this.props.node); } private _reac: (undefined | IReactionDisposer) = undefined; private _plotList: ([string, StrokeData] | undefined) = undefined; - private _interpolationKeyframe: (Doc | undefined) = undefined; - private _type: string = ""; - + private _interpolationKeyframe: (Doc | undefined) = undefined; + private _type: string = ""; + @action onContainerDown = (kf: Doc, type: string) => { - let listenerCreated = false; - this.props.checkCallBack(true); - this._type = type; + let listenerCreated = false; + this.props.checkCallBack(true); + 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; - listenerCreated = true; + this._interpolationKeyframe = kf; + listenerCreated = true; const reac = reaction(() => { - return this.props.check; - }, () => { - if(this.props.check === "yes") this.onReactionListen(); - reac(); - this.props.checkCallBack(false); - }); + return this.props.check; + }, () => { + if (this.props.check === "yes") this.onReactionListen(); + reac(); + this.props.checkCallBack(false); + }); } }); } @@ -548,26 +567,26 @@ export class Keyframe extends React.Component { } } else { i++; - } + } } - let index = this.keyframes.indexOf(this._interpolationKeyframe!); - if (this._type === "interpolate"){ + 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._reac = undefined; + this._interpolationKeyframe = undefined; + this._plotList = undefined; } } @action - drawKeyframes = () => { - let keyframeDivs:JSX.Element[] = []; - DocListCast(this.regiondata.keyframes).forEach( kf => { + drawKeyframes = () => { + let keyframeDivs: JSX.Element[] = []; + DocListCast(this.regiondata.keyframes).forEach(kf => { if (kf.type as KeyframeFunc.KeyframeType === KeyframeFunc.KeyframeType.default) { keyframeDivs.push(
    @@ -575,8 +594,8 @@ export class Keyframe extends React.Component {
    { e.preventDefault(); e.stopPropagation(); this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); - this.makeKeyframeMenu(kf, e.nativeEvent); - }} onDoubleClick={(e) => {e.preventDefault(); e.stopPropagation(); }}>
    + this.makeKeyframeMenu(kf, e.nativeEvent); + }} onDoubleClick={(e) => { e.preventDefault(); e.stopPropagation(); }}>
    ); } @@ -588,47 +607,48 @@ export class Keyframe extends React.Component { ); } }); - return keyframeDivs; + return keyframeDivs; } - @action - drawKeyframeDividers = () => { - let keyframeDividers:JSX.Element[] = []; + @action + drawKeyframeDividers = () => { + let keyframeDividers: JSX.Element[] = []; DocListCast(this.regiondata.keyframes).forEach(kf => { - let index = this.keyframes.indexOf(kf); - if(index !== this.keyframes.length - 1 ) { - let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; - let bodyRef = React.createRef(); - let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - let leftPos = KeyframeFunc.convertPixelTime(NumCast(left!.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - keyframeDividers.push ( -
    { e.preventDefault(); e.stopPropagation(); this.onContainerOver(e, bodyRef); }} - onPointerOut={(e) => { e.preventDefault(); e.stopPropagation(); this.onContainerOut(e, bodyRef); }} - onContextMenu={(e) => { - e.preventDefault(); - e.stopPropagation(); - if (index !== 0 || index !== this.keyframes.length - 2){ - this._mouseToggled = true; - } - this.makeRegionMenu(kf, e.nativeEvent); - }}> + let index = this.keyframes.indexOf(kf); + if (index !== this.keyframes.length - 1) { + let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; + let bodyRef = React.createRef(); + let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + let leftPos = KeyframeFunc.convertPixelTime(NumCast(left!.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + keyframeDividers.push( +
    { e.preventDefault(); e.stopPropagation(); this.onContainerOver(e, bodyRef); }} + onPointerOut={(e) => { e.preventDefault(); e.stopPropagation(); this.onContainerOut(e, bodyRef); }} + onContextMenu={(e) => { + e.preventDefault(); + e.stopPropagation(); + if (index !== 0 || index !== this.keyframes.length - 2) { + this._mouseToggled = true; + } + this.makeRegionMenu(kf, e.nativeEvent); + }}>
    - ); - } - }); - return keyframeDividers; + ); + } + }); + return keyframeDividers; } render() { return (
    -
    + }>
    {this.drawKeyframes()} diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index a92b1858d..92e3e3f6f 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -76,7 +76,8 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number } else { DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } - UndoManager.AddEvent({ + UndoManager. + AddEvent({ redo: () => receiver[prop] = value, undo: () => receiver[prop] = curValue }); -- cgit v1.2.3-70-g09d2 From 5becac4b0442da73a6fe3b48e3a248f6cf2df61d Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sat, 23 Nov 2019 15:40:07 -0500 Subject: responsive ui --- src/client/views/animationtimeline/Timeline.scss | 276 ++++++++++++----------- src/client/views/animationtimeline/Timeline.tsx | 129 ++++++----- src/client/views/animationtimeline/Track.scss | 1 - src/client/views/animationtimeline/Track.tsx | 88 ++++---- src/client/views/linking/LinkFollowBox.tsx | 2 +- 5 files changed, 260 insertions(+), 236 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 88d602d76..493b084a8 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -1,191 +1,209 @@ -@import "./../globalCssVariables.scss"; +@import "./../globalCssVariables.scss"; -.timeline-toolbox{ - position:absolute; - margin: 0px; - padding: 0px; - display:flex; - align-items: flex-start; +.timeline-toolbox { + position: absolute; + margin: 0px; + padding: 0px; + display: flex; + align-items: flex-start; flex-direction: row; justify-content: space-evenly; align-items: center; top: 20px; - div{ - padding: 0px; - margin-left:10px; + + div { + padding: 0px; + margin-left: 10px; } - .animation-text{ - font-size: 20px; - height: auto; - width: auto; + + .animation-text { + font-size: 20px; + height: auto; + width: auto; white-space: nowrap; font-size: 16px; color: grey; letter-spacing: 2px; text-transform: uppercase; } + .round-toggle { height: 40px; width: 80px; background-color: white; border: 2px solid grey; border-radius: 20px; - animation-fill-mode: forwards; + animation-fill-mode: forwards; animation-duration: 500ms; - top: 30px; - input{ - position:absolute; - opacity: 0; - height: 0; - width: 0; + top: 30px; + + input { + position: absolute; + opacity: 0; + height: 0; + width: 0; } - .round-toggle-slider{ - height: 35px; - width: 35px; + + .round-toggle-slider { + height: 35px; + width: 35px; background-color: white; - border:1px solid grey; - border-radius: 20px; - transition: transform 500ms ease-in-out; - margin-left: 0px; - margin-top: 0.5px; - } + border: 1px solid grey; + border-radius: 20px; + transition: transform 500ms ease-in-out; + margin-left: 0px; + margin-top: 0.5px; + } } - + } -.time-input{ - height: 40px; - width: 120px; + +.time-input { + height: 40px; + width: 120px; white-space: nowrap; font-size: 16px; color: grey; letter-spacing: 2px; text-transform: uppercase; - padding-left: 5px; - + padding-left: 5px; + } -.tick{ - height:100%; - width: 1px; - background-color:black; -} -.timeline-container{ - width:100%; - height:300px; - position:absolute; + +.tick { + height: 100%; + width: 1px; + background-color: black; +} + +.timeline-container { + width: 100%; + height: 300px; + position: absolute; background-color: $light-color-secondary; - box-shadow: 0px 10px 20px; - transition: transform 500ms ease; - - .info-container{ - margin-top: 80px; - right:20px; - position:absolute; - height: calc(100% - 100px); - width: calc(100% - 140px); + box-shadow: 0px 10px 20px; + transition: transform 500ms ease; + + .info-container { + margin-top: 80px; + right: 20px; + position: absolute; + height: calc(100% - 100px); + width: calc(100% - 140px); overflow: hidden; - .scrubberbox{ - position:absolute; - background-color: transparent; + .scrubberbox { + position: absolute; + background-color: transparent; height: 30px; - width:100%; - + width: 100%; + } - .scrubber{ - top:30px; - height: 100%; - width: 2px; - position:absolute; + + .scrubber { + top: 30px; + height: 100%; + width: 2px; + position: absolute; z-index: 1001; - background-color:black; - .scrubberhead{ - top: -30px; - height: 30px; + background-color: black; + + .scrubberhead { + top: -30px; + height: 30px; width: 30px; - background-color:transparent; - border-radius: 50%; - border: 5px solid black; + background-color: transparent; + border-radius: 50%; + border: 5px solid black; left: -15px; - position:absolute; + 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; + .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: 110px; - margin-left: 20px; - height: calc(100% - 100px - 30px); - width: 100px; - background-color:white; - overflow: hidden; - .datapane{ - top:0px; - width: 100px; - height: 75px; + + .title-container { + margin-top: 110px; + margin-left: 20px; + height: calc(100% - 100px - 30px); + width: 100px; + background-color: white; + overflow: hidden; + + .datapane { + top: 0px; + width: 100px; + height: 30%; border: 1px solid $dark-color; - background-color: $intermediate-color; - color: white; - position:relative; - float:left; - border-style:solid; + background-color: $intermediate-color; + color: white; + position: relative; + float: left; + border-style: solid; + overflow-y: scroll; + overflow-x: hidden; } } - .resize{ - bottom: 5px; - position:absolute; - height: 30px; - width: 50px; + + .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; +.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; } } -.timeline-checker{ - height: auto; - width: auto; - overflow: hidden; - position: absolute; - display: flex; - padding: 10px 10px; - div{ - height: auto; +.timeline-checker { + height: auto; + width: auto; + overflow: hidden; + position: absolute; + display: flex; + padding: 10px 10px; + + div { + height: auto; width: auto; - overflow: hidden; - margin: 0px 10px; + overflow: hidden; + margin: 0px 10px; cursor: pointer } - .check{ - width: 50px; - height: 50px; + + .check { + width: 50px; + height: 50px; } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 43019903e..f8aa88755 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -8,7 +8,7 @@ import { Cast, NumCast, StrCast, BoolCast } 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, faEyeSlash, faTimes, faEye, faCheck, faCross, faCheckCircle, faTimesCircle} from "@fortawesome/free-solid-svg-icons"; +import { faPlayCircle, faBackward, faForward, faGripLines, faArrowUp, faArrowDown, faClock, faPauseCircle, faEyeSlash, faTimes, faEye, faCheck, faCross, faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons"; import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; @@ -19,9 +19,9 @@ import { Utils } from "../../../Utils"; @observer export class Timeline extends React.Component { - private readonly DEFAULT_CONTAINER_HEIGHT: number = 330; + private DEFAULT_CONTAINER_HEIGHT: number = 330; private readonly DEFAULT_TICK_SPACING: number = 50; - private readonly MIN_CONTAINER_HEIGHT: number = 205; + private MIN_CONTAINER_HEIGHT: number = 205; private readonly MAX_CONTAINER_HEIGHT: number = 800; private readonly DEFAULT_TICK_INCREMENT: number = 1000; @@ -31,7 +31,7 @@ export class Timeline extends React.Component { @observable private _infoContainer = React.createRef(); @observable private _roundToggleRef = React.createRef(); @observable private _roundToggleContainerRef = React.createRef(); - @observable private _timeInputRef = React.createRef(); + @observable private _timeInputRef = React.createRef(); @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @@ -46,8 +46,8 @@ export class Timeline extends React.Component { @observable private _playButton = faPlayCircle; @observable private _timelineVisible = false; @observable private _mouseToggled = false; - @observable private _doubleClickEnabled = false; - + @observable private _doubleClickEnabled = false; + private _titleHeight = 0; @computed private get children(): List { let extendedDocument = ["image", "video", "pdf"].includes(StrCast(this.props.Document.type)); @@ -60,28 +60,33 @@ export class Timeline extends React.Component { } return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; } + componentWillMount() { + this._titleHeight = window.innerHeight / 14; //arbitrary number, but for responsiveness + this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; + this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; + } componentDidMount() { runInAction(() => { - if (!this.props.Document.AnimationLength){ - this.props.Document.AnimationLength = this._time; + if (!this.props.Document.AnimationLength) { + this.props.Document.AnimationLength = this._time; } else { - this._time = NumCast(this.props.Document.AnimationLength); - console.log(this._time); + this._time = NumCast(this.props.Document.AnimationLength); + console.log(this._time); } this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; this._visibleStart = this._infoContainer.current!.scrollLeft; - this.props.Document.isATOn = !this.props.Document.isATOn; - this.toggleHandle(); + this.props.Document.isATOn = !this.props.Document.isATOn; + this.toggleHandle(); }); } - componentWillUnmount(){ + componentWillUnmount() { runInAction(() => { - console.log(this._time); - this.props.Document.AnimationLength = this._time; - }); + console.log(this._time); + this.props.Document.AnimationLength = this._time; + }); } /** @@ -90,11 +95,11 @@ export class Timeline extends React.Component { */ @action drawTicks = () => { - let ticks = []; - for (let i = 0; i < this._time / this._tickIncrement; i++){ - ticks.push(

    {this.toReadTime(i * this._tickIncrement)}

    ); + let ticks = []; + for (let i = 0; i < this._time / this._tickIncrement; i++) { + ticks.push(

    {this.toReadTime(i * this._tickIncrement)}

    ); } - return ticks; + return ticks; } @@ -210,14 +215,14 @@ export class Timeline extends React.Component { let titleContainer = this._titleContainer.current!; this.movePanX(this._visibleStart - e.movementX); trackbox.scrollTop = trackbox.scrollTop - e.movementY; - titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; - if (this._visibleStart + this._visibleLength + 20>= this._totalLength){ - this._visibleStart -= e.movementX; + titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; + if (this._visibleStart + this._visibleLength + 20 >= this._totalLength) { + this._visibleStart -= e.movementX; this._totalLength -= e.movementX; - this._time -= KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this._tickSpacing, this._tickIncrement); - this.props.Document.AnimationLength = this._time; + this._time -= KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this._tickSpacing, this._tickIncrement); + this.props.Document.AnimationLength = this._time; } - + } @action movePanX = (pixel: number) => { @@ -263,9 +268,11 @@ export class Timeline extends React.Component { } timelineContextMenu = (e: MouseEvent): void => { - ContextMenu.Instance.addItem({description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { - this._timelineVisible = !this._timelineVisible; - }), icon: this._timelineVisible ? faEyeSlash : faEye }); + ContextMenu.Instance.addItem({ + description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { + this._timelineVisible = !this._timelineVisible; + }), icon: this._timelineVisible ? faEyeSlash : faEye + }); } @@ -318,28 +325,28 @@ export class Timeline extends React.Component { let size = 40 * scale; //50 is default return (
    -
    -
    -
    +
    +
    +
    Timeline Overview
    - +
    Mode: {this.props.Document.isATOn ? "Authoring" : "Play"}
    -
    Length:
    - +
    Length:
    +
    ); } - - @action + + @action private onTimeInput = (e: React.KeyboardEvent) => { - if (e.keyCode === 13){ - let timeInput = this._timeInputRef.current!; + if (e.keyCode === 13) { + let timeInput = this._timeInputRef.current!; this._time = parseInt(timeInput.value, 10); - this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); - this.props.Document.AnimationLength = this._time; + this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); + this.props.Document.AnimationLength = this._time; } } @@ -347,7 +354,7 @@ export class Timeline extends React.Component { private toggleChecked = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - this.toggleHandle(); + this.toggleHandle(); } private toggleHandle = () => { @@ -358,36 +365,36 @@ export class Timeline extends React.Component { roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; - roundToggleContainer.style.backgroundColor = "white"; + roundToggleContainer.style.backgroundColor = "white"; timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isATOn = false; } else { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; - roundToggleContainer.style.backgroundColor = "green"; - timelineContainer.style.top = "0px"; + roundToggleContainer.style.backgroundColor = "green"; + timelineContainer.style.top = "0px"; this.props.Document.isATOn = true; } } - @observable private _check:string = ""; - @observable private _checkVisible:boolean = false; + @observable private _check: string = ""; + @observable private _checkVisible: boolean = false; @action private onCheckClicked = (type: string) => { - if (type === "yes"){ - this._check = "yes"; - } else if (type === "no"){ + if (type === "yes") { + this._check = "yes"; + } else if (type === "no") { this._check = "no"; } } - @action - private checkCallBack = (visible:boolean) => { - this._checkVisible = visible; - if (!visible){ //when user confirms + @action + private checkCallBack = (visible: boolean) => { + this._checkVisible = visible; + if (!visible) { //when user confirms this._check = ""; } @@ -404,22 +411,22 @@ export class Timeline extends React.Component {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => )}
    - {DocListCast(this.children).map(doc =>
    { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}>

    {doc.title}

    )} + {DocListCast(this.children).map(doc =>
    { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}>

    {doc.title}

    )}
    -
    -
    {this.onCheckClicked("yes");}}> - +
    +
    { this.onCheckClicked("yes"); }}> +
    -
    {this.onCheckClicked("no");}}> - +
    { this.onCheckClicked("no"); }}> +
    diff --git a/src/client/views/animationtimeline/Track.scss b/src/client/views/animationtimeline/Track.scss index c8d56edf6..61a8e0b88 100644 --- a/src/client/views/animationtimeline/Track.scss +++ b/src/client/views/animationtimeline/Track.scss @@ -5,7 +5,6 @@ .track { .inner { top:0px; - height: 75px; width: calc(100%); background-color: $light-color; border: 1px solid $dark-color; diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index e66b42690..bee9933b0 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -19,10 +19,10 @@ interface IProps { time: number; tickIncrement: number; tickSpacing: number; - timelineVisible: boolean; - check: string; + timelineVisible: boolean; + check: string; changeCurrentBarX: (x: number) => void; - checkCallBack: (visible: boolean) => void; + checkCallBack: (visible: boolean) => void; } @@ -36,35 +36,35 @@ export class Track extends React.Component { @observable private _onRegionData: (Doc | undefined) = undefined; @observable private _storedState: (Doc | undefined) = undefined; @observable private filterList = [ - "regions", - "cursors", - "hidden", - "nativeHeight", - "nativeWidth", - "schemaColumns", - "baseLayout", - "backgroundLayout", - "layout", - ]; - - @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} + "regions", + "cursors", + "hidden", + "nativeHeight", + "nativeWidth", + "schemaColumns", + "baseLayout", + "backgroundLayout", + "layout", + ]; + private _trackHeight = 0; + + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } componentWillMount() { runInAction(() => { - if (!this.props.node.regions) this.props.node.regions = new List(); - console.log("hi"); - console.log("HELLO"); - console.log("hi"); + if (!this.props.node.regions) this.props.node.regions = new List(); + this._trackHeight = window.innerHeight / 14; //for responsiveness + }); } componentDidMount() { runInAction(async () => { - this._timelineVisibleReaction = this.timelineVisibleReaction(); + this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); - this.props.node.hidden = false; - this.props.node.opacity = 1; + this.props.node.hidden = false; + this.props.node.opacity = 1; }); } @@ -72,7 +72,7 @@ export class Track extends React.Component { runInAction(() => { //disposing reactions if (this._currentBarXReaction) this._currentBarXReaction(); - if (this._timelineVisibleReaction) this._timelineVisibleReaction(); + if (this._timelineVisibleReaction) this._timelineVisibleReaction(); }); } @@ -81,7 +81,7 @@ export class Track extends React.Component { let keyframes: List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); let kfIndex: number = keyframes.indexOf(ref); let kf = keyframes[kfIndex] as Doc; - if (!kf) return; + if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); // lef keyframe, if it exists @@ -107,13 +107,13 @@ export class Track extends React.Component { this._isOnKeyframe = false; } - @action + @action revertState = () => { - let copyDoc = Doc.MakeCopy(this.props.node, true); + let copyDoc = Doc.MakeCopy(this.props.node, true); if (this._storedState) this.applyKeys(this._storedState); - let newState = new Doc(); - newState.key = copyDoc; - this._storedState = newState; + let newState = new Doc(); + newState.key = copyDoc; + this._storedState = newState; } @action @@ -129,13 +129,13 @@ export class Track extends React.Component { } }); } - @action + @action timelineVisibleReaction = () => { return reaction(() => { - return this.props.timelineVisible; + return this.props.timelineVisible; }, isVisible => { - this.revertState(); - }); + this.revertState(); + }); } @action @@ -162,28 +162,28 @@ export class Track extends React.Component { @action private applyKeys = async (kf: Doc) => { let kfNode = await Cast(kf.key, Doc) as Doc; - let docFromApply = kfNode; + 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 { let stored = kfNode[key]; - if(stored instanceof ObjectField){ - this.props.node[key] = stored[Copy](); + if (stored instanceof ObjectField) { + this.props.node[key] = stored[Copy](); } else { - this.props.node[key] = stored; + this.props.node[key] = stored; } } }); } - + @action private filterKeys = (keys: string[]): string[] => { return keys.reduce((acc: string[], key: string) => { - if (!this.filterList.includes(key)) acc.push(key); + if (!this.filterList.includes(key)) acc.push(key); return acc; }, []); } @@ -249,10 +249,10 @@ export class Track extends React.Component { } } else { let stored = leftNode[key]; - if(stored instanceof ObjectField){ - this.props.node[key] = stored[Copy](); + if (stored instanceof ObjectField) { + this.props.node[key] = stored[Copy](); } else { - this.props.node[key] = stored; + this.props.node[key] = stored; } } }); @@ -276,7 +276,7 @@ export class Track extends React.Component { let inner = this._inner.current!; let offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); - this.forceUpdate(); + this.forceUpdate(); } createRegion = (position: number) => { @@ -297,7 +297,7 @@ export class Track extends React.Component { return (
    -
    {Doc.BrushDoc(this.props.node);}}onPointerOut={() => {Doc.UnBrushDoc(this.props.node);}}> +
    { Doc.BrushDoc(this.props.node); }} onPointerOut={() => { Doc.UnBrushDoc(this.props.node); }} style={{ height: `${this._trackHeight}px` }}> {DocListCast(this.regions).map((region) => { return ; })} diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index efe2c7f2a..f3193896f 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -21,7 +21,7 @@ import { docs_v1 } from "googleapis"; import { Utils } from "../../../Utils"; import { Link } from "@react-pdf/renderer"; -enum FollowModes { +export enum FollowModes { OPENTAB = "Open in Tab", OPENRIGHT = "Open in Right Split", OPENFULL = "Open Full Screen", -- cgit v1.2.3-70-g09d2 From 8af45ed7f376981ce8f8b1c6d1b2fd3b1546a00e Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 23 Nov 2019 16:08:44 -0500 Subject: responsive update --- src/client/views/animationtimeline/Timeline.tsx | 4 +++- src/client/views/animationtimeline/Track.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index f8aa88755..64822542d 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -21,6 +21,7 @@ export class Timeline extends React.Component { private DEFAULT_CONTAINER_HEIGHT: number = 330; private readonly DEFAULT_TICK_SPACING: number = 50; + private readonly MAX_TITLE_HEIGHT = 75; private MIN_CONTAINER_HEIGHT: number = 205; private readonly MAX_CONTAINER_HEIGHT: number = 800; private readonly DEFAULT_TICK_INCREMENT: number = 1000; @@ -61,7 +62,8 @@ export class Timeline extends React.Component { return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; } componentWillMount() { - this._titleHeight = window.innerHeight / 14; //arbitrary number, but for responsiveness + let relativeHeight = window.innerHeight / 14; + this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index bee9933b0..a3aa62a31 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -46,6 +46,7 @@ export class Track extends React.Component { "backgroundLayout", "layout", ]; + private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } @@ -53,7 +54,8 @@ export class Track extends React.Component { componentWillMount() { runInAction(() => { if (!this.props.node.regions) this.props.node.regions = new List(); - this._trackHeight = window.innerHeight / 14; //for responsiveness + let relativeHeight = window.innerHeight / 14; + this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness }); } -- cgit v1.2.3-70-g09d2 From 5bf4dfa43e07a21bc5cb96b3b98f1a6ddb75b903 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 23 Nov 2019 16:31:15 -0500 Subject: another change --- src/client/views/animationtimeline/Timeline.tsx | 3 +-- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 64822542d..2b960cc88 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -8,8 +8,7 @@ import { Cast, NumCast, StrCast, BoolCast } 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, faEyeSlash, faTimes, faEye, faCheck, faCross, faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons"; -import { ContextMenuProps } from "../ContextMenuItem"; +import { faPlayCircle, faBackward, faForward, faGripLines,faPauseCircle, faEyeSlash,faEye,faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; import { FieldViewProps } from "../nodes/FieldView"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9fbfffc82..e2b4716ce 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -637,7 +637,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } private viewDefToJSX(viewDef: any): Opt { - if (viewDef.type === "text") { + if (viewDef.type === "text") { const text = Cast(viewDef.text, "string"); // don't use NumCast, StrCast, etc since we want to test for undefined below const x = Cast(viewDef.x, "number"); const y = Cast(viewDef.y, "number"); -- cgit v1.2.3-70-g09d2 From be753f6c952d8f1fc324d2c076095416b6d98a35 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 7 Dec 2019 17:04:29 -0500 Subject: bug fixes --- src/client/views/animationtimeline/Keyframe.tsx | 5 ++++- src/client/views/animationtimeline/Track.tsx | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index a2d0a644e..e8b75eef4 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -300,7 +300,10 @@ export class Keyframe extends React.Component { } else if (NumCast(this.keyframes[1].time) + offset >= NumCast(this.keyframes[2].time)) { this.regiondata.position = NumCast(this.keyframes[2].time) - this.regiondata.fadeIn; this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - NumCast(this.keyframes[2].time) + this.regiondata.fadeIn; - } else { + } else if (NumCast(this.keyframes[0].time) + offset <= 0){ + this.regiondata.position = 0; + this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time); + }else { this.regiondata.duration -= offset; this.regiondata.position += offset; } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index a3aa62a31..22b83f055 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -45,6 +45,17 @@ export class Track extends React.Component { "baseLayout", "backgroundLayout", "layout", + "title", + "AnimationLength", + "author", + "baseProto", + "creationDate", + "isATOn", + "isPrototype", + "lastOpened", + "proto", + "type", + "zIndex" ]; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; -- cgit v1.2.3-70-g09d2 From a24f9da758415c60dbcb56b1cb488bec02d5b1a0 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 10 Dec 2019 17:55:13 -0500 Subject: some changes --- src/client/views/animationtimeline/Keyframe.tsx | 5 ++- src/client/views/animationtimeline/Timeline.tsx | 45 ++++++++++++++++++++----- src/client/views/animationtimeline/Track.tsx | 25 ++++++++++++-- src/client/views/nodes/DocumentView.tsx | 4 +-- 4 files changed, 64 insertions(+), 15 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index e8b75eef4..c3e018112 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -88,6 +88,7 @@ export namespace KeyframeFunc { regiondata.fadeIn = 1000; regiondata.fadeOut = 1000; regiondata.functions = new List(); + regiondata.hasData = false; return regiondata; }; @@ -114,7 +115,8 @@ export const RegionDataSchema = createSchema({ keyframes: listSpec(Doc), fadeIn: defaultSpec("number", 0), fadeOut: defaultSpec("number", 0), - functions: listSpec(Doc) + functions: listSpec(Doc), + hasData: defaultSpec("boolean", false) }); export type RegionData = makeInterface<[typeof RegionDataSchema]>; export const RegionData = makeInterface(RegionDataSchema); @@ -340,6 +342,7 @@ export class Keyframe extends React.Component { if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends let position = this.regiondata.position; await this.makeKeyData(Math.round(position + offset)); + this.regiondata.hasData = true; this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 2b960cc88..c13a039a5 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -3,7 +3,7 @@ import "./Timeline.scss"; import { listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; -import { observable, reaction, action, IReactionDisposer, computed, runInAction, observe, toJS } from "mobx"; +import { observable, action, computed, runInAction } from "mobx"; import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Doc, DocListCast } from "../../../new_fields/Doc"; @@ -17,14 +17,18 @@ import { Utils } from "../../../Utils"; @observer export class Timeline extends React.Component { - - private DEFAULT_CONTAINER_HEIGHT: number = 330; + + //readonly constants private readonly DEFAULT_TICK_SPACING: number = 50; private readonly MAX_TITLE_HEIGHT = 75; - private MIN_CONTAINER_HEIGHT: number = 205; private readonly MAX_CONTAINER_HEIGHT: number = 800; private readonly DEFAULT_TICK_INCREMENT: number = 1000; + //height variables + private DEFAULT_CONTAINER_HEIGHT: number = 330; + private MIN_CONTAINER_HEIGHT: number = 205; + + //react refs @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); @@ -33,6 +37,7 @@ export class Timeline extends React.Component { @observable private _roundToggleContainerRef = React.createRef(); @observable private _timeInputRef = React.createRef(); + //boolean vars and instance vars @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @observable private _isPlaying: boolean = false; //scrubber playing @@ -47,7 +52,11 @@ export class Timeline extends React.Component { @observable private _timelineVisible = false; @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; - private _titleHeight = 0; + @observable private _titleHeight = 0; + + /** + * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit. + */ @computed private get children(): List { let extendedDocument = ["image", "video", "pdf"].includes(StrCast(this.props.Document.type)); @@ -60,6 +69,10 @@ export class Timeline extends React.Component { } return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; } + + /** + * + */ componentWillMount() { let relativeHeight = window.innerHeight / 14; this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; @@ -140,16 +153,21 @@ export class Timeline extends React.Component { } - + /** + * fast forward the timeline scrubbing + */ @action windForward = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); - if (this._windSpeed < 64) { //max speed is 32 + if (this._windSpeed < 64) { //max speed is 32 this._windSpeed = this._windSpeed * 2; } } + /** + * rewind the timeline scrubbing + */ @action windBackward = (e: React.MouseEvent) => { e.preventDefault(); @@ -159,7 +177,7 @@ export class Timeline extends React.Component { } } - //for scrubber action + @action onScrubberDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -392,6 +410,7 @@ export class Timeline extends React.Component { } + @action private checkCallBack = (visible: boolean) => { this._checkVisible = visible; @@ -400,10 +419,18 @@ export class Timeline extends React.Component { } } + + + + + + + + render() { return (
    -
    +
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 22b83f055..d0bcdd655 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -65,9 +65,8 @@ export class Track extends React.Component { componentWillMount() { runInAction(() => { if (!this.props.node.regions) this.props.node.regions = new List(); - let relativeHeight = window.innerHeight / 14; + let relativeHeight = window.innerHeight / 14; this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness - }); } @@ -145,12 +144,32 @@ export class Track extends React.Component { @action timelineVisibleReaction = () => { return reaction(() => { - return this.props.timelineVisible; + return this.props.timelineVisible; }, isVisible => { this.revertState(); + if (isVisible){ + DocListCast(this.regions).forEach(region => { + if (!BoolCast((Cast(region, Doc) as Doc).hasData)){ + for (let i = 0; i < 4; i++){ + DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = Doc.MakeCopy(this.props.node, true); + if (i === 0 || i === 3){ + DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key.opacity = 0.1; + } + } + console.log("saving keyframes"); + } + }); + } }); } + @action + /** + * Simple loop through all the regions to update the keyframes. Only applies to timelineVisibleReaction + */ + updateAllRegions = () => { + + } @action timeChange = async (time: number) => { if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 467fd4b32..6c3dd2051 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -669,10 +669,10 @@ export class DocumentView extends DocComponent(Docu return
    { - console.log("Brush" + this.props.Document.title); + //console.log("Brush" + this.props.Document.title); Doc.BrushDoc(this.props.Document); }} onPointerLeave={e => { - console.log("UnBrush" + this.props.Document.title); + //console.log("UnBrush" + this.props.Document.title); Doc.UnBrushDoc(this.props.Document); }} -- cgit v1.2.3-70-g09d2 From aa5b205e3d5239457fd224da37fe32421a4f3b3c Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Thu, 9 Jan 2020 21:45:00 +0900 Subject: documentation --- src/client/views/animationtimeline/Keyframe.tsx | 65 ++++++++++++ src/client/views/animationtimeline/Timeline.tsx | 127 ++++++++++++++++++------ src/client/views/animationtimeline/Track.tsx | 62 ++++++++++-- 3 files changed, 218 insertions(+), 36 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index c3e018112..c3c1f5f8c 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -15,6 +15,11 @@ import { Docs } from "../../documents/Documents"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { undoBatch, UndoManager } from "../../util/UndoManager"; + + +/** + * Useful static functions that you can use. Mostly for logic, but you can also add UI logic here also + */ export namespace KeyframeFunc { export enum KeyframeType { fade = "fade", @@ -134,6 +139,30 @@ interface IProps { checkCallBack: (visible: boolean) => void; } + +/** + * + * This class handles the green region stuff + * Key facts: + * + * Structure looks like this + * + * region as a whole + * <------------------------------REGION-------------------------------> + * + * region broken down + * + * <|---------|############ MAIN CONTENT #################|-----------|> .....followed by void......... + * (start) (Fade 2) + * (fade 1) (finish) + * + * + * As you can see, this is different from After Effect and Premiere Pro, but this is how TAG worked. + * If you want to checkout TAG, it's in the lockers, and the password is the usual lab door password. It's the blue laptop. + * If you want to know the exact location of the computer, message me. + * + * @author Andrew Kim + */ @observer export class Keyframe extends React.Component { @@ -363,6 +392,10 @@ export class Keyframe extends React.Component { this.props.node.backgroundColor = "#000000"; } + + /** + * custom keyframe context menu items (when clicking on the keyframe circle) + */ @action makeKeyframeMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Show Data", () => { @@ -396,6 +429,9 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } + /** + * context menu for region (anywhere on the green region). + */ @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Add Ease", () => { @@ -481,6 +517,10 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } + + /** + * checking if input is correct (might have to use external module) + */ @action checkInput = (val: any) => { return typeof (val === "number"); @@ -495,6 +535,9 @@ export class Keyframe extends React.Component { }); } + /** + * hovering effect when hovered (hidden div darkens) + */ @action onContainerOver = (e: React.PointerEvent, ref: React.RefObject) => { e.preventDefault(); @@ -504,6 +547,9 @@ export class Keyframe extends React.Component { Doc.BrushDoc(this.props.node); } + /** + * hovering effect when hovered out (hidden div becomes invisible) + */ @action onContainerOut = (e: React.PointerEvent, ref: React.RefObject) => { e.preventDefault(); @@ -520,6 +566,9 @@ export class Keyframe extends React.Component { private _type: string = ""; + /** + * Need to fix this. skip + */ @action onContainerDown = (kf: Doc, type: string) => { let listenerCreated = false; @@ -544,6 +593,9 @@ export class Keyframe extends React.Component { }); } + /** + * for custom draw interpolation. Need to be refactored + */ @action onReactionListen = () => { if (this._reac && this._plotList && this._interpolationKeyframe) { @@ -589,6 +641,13 @@ export class Keyframe extends React.Component { } } + ///////////////////////UI STUFF ///////////////////////// + + + /** + * drawing keyframe. Handles both keyframe with a circle (one that you create by double clicking) and one without circle (fades) + * this probably needs biggest change, since everyone expected all keyframes to have a circle (and draggable) + */ @action drawKeyframes = () => { let keyframeDivs: JSX.Element[] = []; @@ -616,6 +675,9 @@ export class Keyframe extends React.Component { return keyframeDivs; } + /** + * drawing the hidden divs that partition different intervals within a region. + */ @action drawKeyframeDividers = () => { let keyframeDividers: JSX.Element[] = []; @@ -645,6 +707,9 @@ export class Keyframe extends React.Component { return keyframeDividers; } + /** + * rendering that green region + */ render() { return (
    diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index c13a039a5..9cf600b5f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -15,9 +15,32 @@ import { FieldViewProps } from "../nodes/FieldView"; import { KeyframeFunc } from "./Keyframe"; import { Utils } from "../../../Utils"; + +/** + * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are + * zooming, panning, currentBarX (scrubber movement). Most of the UI stuff is also handled here. You shouldn't really make + * any logical changes here. Most work is needed on UI. + * + * The hierarchy works this way: + * + * Timeline.tsx --> Track.tsx --> Keyframe.tsx + | | + | TimelineMenu.tsx (timeline's custom contextmenu) + | + | + TimelineOverview.tsx (youtube like dragging thing is play mode, complex dragging thing in editing mode) + + + Most style changes are in SCSS file. + If you have any questions, email me or text me. + @author Andrew Kim + */ + + @observer export class Timeline extends React.Component { - + + //readonly constants private readonly DEFAULT_TICK_SPACING: number = 50; private readonly MAX_TITLE_HEIGHT = 75; @@ -70,38 +93,35 @@ export class Timeline extends React.Component { return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List; } - /** - * - */ + /////////lifecycle functions//////////// componentWillMount() { - let relativeHeight = window.innerHeight / 14; - this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; - this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; - this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; + let relativeHeight = window.innerHeight / 14; //sets height to arbitrary size, relative to innerHeight + this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max + this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset + this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset } componentDidMount() { runInAction(() => { - if (!this.props.Document.AnimationLength) { - this.props.Document.AnimationLength = this._time; + if (!this.props.Document.AnimationLength) { //if animation length did not exist + this.props.Document.AnimationLength = this._time; //set it to default time } else { - this._time = NumCast(this.props.Document.AnimationLength); - console.log(this._time); + this._time = NumCast(this.props.Document.AnimationLength); //else, set time to animationlength stored from before } - this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); - this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; - this._visibleStart = this._infoContainer.current!.scrollLeft; - this.props.Document.isATOn = !this.props.Document.isATOn; + this._totalLength = this._tickSpacing * (this._time / this._tickIncrement); //the entire length of the timeline div (actual div part itself) + this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) + this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts + this.props.Document.isATOn = !this.props.Document.isATOn; //turns the boolean on, saying AT (animation timeline) is on this.toggleHandle(); }); } componentWillUnmount() { runInAction(() => { - console.log(this._time); - this.props.Document.AnimationLength = this._time; + this.props.Document.AnimationLength = this._time; //save animation length }); } + ///////////////////////////////////////////////// /** * React Functional Component @@ -116,7 +136,9 @@ export class Timeline extends React.Component { return ticks; } - + /** + * changes the scrubber to actual pixel position + */ @action changeCurrentBarX = (pixel: number) => { pixel <= 0 ? this._currentBarX = 0 : pixel >= this._totalLength ? this._currentBarX = this._totalLength : this._currentBarX = pixel; @@ -130,6 +152,9 @@ export class Timeline extends React.Component { this.play(); } + /** + * when playbutton is clicked + */ @action play = () => { if (this._isPlaying) { @@ -177,7 +202,9 @@ export class Timeline extends React.Component { } } - + /** + * scrubber down + */ @action onScrubberDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -188,16 +215,22 @@ export class Timeline extends React.Component { }); } + /** + * when there is any scrubber movement + */ @action onScrubberMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); let scrubberbox = this._infoContainer.current!; let left = scrubberbox.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; - this.changeCurrentBarX(offsetX + this._visibleStart); + let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; + this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position } + /** + * when panning the timeline (in editing mode) + */ @action onPanDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -223,6 +256,9 @@ export class Timeline extends React.Component { } } + /** + * when moving the timeline (in editing mode) + */ @action onPanMove = (e: PointerEvent) => { e.preventDefault(); @@ -243,6 +279,8 @@ export class Timeline extends React.Component { } } + + @action movePanX = (pixel: number) => { let infoContainer = this._infoContainer.current!; @@ -250,6 +288,9 @@ export class Timeline extends React.Component { this._visibleStart = infoContainer.scrollLeft; } + /** + * resizing timeline (in editing mode) (the hamburger drag icon) + */ @action onResizeDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -274,6 +315,9 @@ export class Timeline extends React.Component { } } + /** + * for displaying time to standard min:sec + */ @action toReadTime = (time: number): string => { const inSeconds = time / 1000; @@ -286,6 +330,11 @@ export class Timeline extends React.Component { return `${min}:${sec}`; } + + /** + * context menu function. + * opens the timeline or closes the timeline. + */ timelineContextMenu = (e: MouseEvent): void => { ContextMenu.Instance.addItem({ description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { @@ -295,6 +344,10 @@ export class Timeline extends React.Component { } + /** + * timeline zoom function + * use mouse middle button to zoom in/out the timeline + */ @action onWheelZoom = (e: React.WheelEvent) => { e.preventDefault(); @@ -311,6 +364,9 @@ export class Timeline extends React.Component { this.changeCurrentBarX(currCurrent); } + /** + * zooming mechanism (increment and spacing changes) + */ @action zoom = (dir: boolean) => { let spacingChange = this._tickSpacing; @@ -340,6 +396,9 @@ export class Timeline extends React.Component { } } + /** + * tool box includes the toggle buttons at the top of the timeline (both editing mode and play mode) + */ private timelineToolBox = (scale: number) => { let size = 40 * scale; //50 is default return ( @@ -359,6 +418,9 @@ export class Timeline extends React.Component { ); } + /** + * manual time input (kinda broken right now) + */ @action private onTimeInput = (e: React.KeyboardEvent) => { if (e.keyCode === 13) { @@ -369,6 +431,10 @@ export class Timeline extends React.Component { } } + + /** + * when the user decides to click the toggle button (either user wants to enter editing mode or play mode) + */ @action private toggleChecked = (e: React.PointerEvent) => { e.preventDefault(); @@ -376,6 +442,9 @@ export class Timeline extends React.Component { this.toggleHandle(); } + /** + * turns on the toggle button (the purple slide button that changes from editing mode and play mode + */ private toggleHandle = () => { let roundToggle = this._roundToggleRef.current!; let roundToggleContainer = this._roundToggleContainerRef.current!; @@ -410,7 +479,9 @@ export class Timeline extends React.Component { } - + /** + * check mark thing that needs to be fixed. Do not edit this, because it most likely change. + */ @action private checkCallBack = (visible: boolean) => { this._checkVisible = visible; @@ -421,12 +492,10 @@ export class Timeline extends React.Component { } - - - - - - + /** + * if you have any question here, just shoot me an email or text. + * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) + */ render() { return (
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index d0bcdd655..d9b8026c5 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -62,9 +62,11 @@ export class Track extends React.Component { @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } + ////////// life cycle functions/////////////// componentWillMount() { runInAction(() => { - if (!this.props.node.regions) this.props.node.regions = new List(); + if (!this.props.node.regions) this.props.node.regions = new List(); //if there is no region, then create new doc to store stuff + //these two lines are exactly same from timeline.tsx let relativeHeight = window.innerHeight / 14; this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness }); @@ -80,6 +82,9 @@ export class Track extends React.Component { }); } + /** + * mainly for disposing reactions + */ componentWillUnmount() { runInAction(() => { //disposing reactions @@ -87,7 +92,11 @@ export class Track extends React.Component { if (this._timelineVisibleReaction) this._timelineVisibleReaction(); }); } + //////////////////////////////// + /** + * keyframe save logic. Needs to be changed so it's more efficient + */ @action saveKeyframe = async (ref: Doc, regiondata: Doc) => { let keyframes: List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); @@ -119,6 +128,9 @@ export class Track extends React.Component { this._isOnKeyframe = false; } + /** + * reverting back to previous state before editing on AT + */ @action revertState = () => { let copyDoc = Doc.MakeCopy(this.props.node, true); @@ -128,6 +140,10 @@ export class Track extends React.Component { this._storedState = newState; } + /** + * Reaction when scrubber bar changes + * made into function so it's easier to dispose later + */ @action currentBarXReaction = () => { return reaction(() => this.props.currentBarX, async () => { @@ -141,6 +157,10 @@ export class Track extends React.Component { } }); } + + /** + * when timeline is visible, reaction is ran so states are reverted + */ @action timelineVisibleReaction = () => { return reaction(() => { @@ -163,13 +183,9 @@ export class Track extends React.Component { }); } - @action - /** - * Simple loop through all the regions to update the keyframes. Only applies to timelineVisibleReaction + /**w + * when scrubber position changes. Need to edit the logic */ - updateAllRegions = () => { - - } @action timeChange = async (time: number) => { if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { @@ -191,6 +207,10 @@ export class Track extends React.Component { } } + /** + * applying changes (when saving the keyframe) + * need to change the logic here + */ @action private applyKeys = async (kf: Doc) => { let kfNode = await Cast(kf.key, Doc) as Doc; @@ -212,6 +232,9 @@ export class Track extends React.Component { + /** + * changing the filter here + */ @action private filterKeys = (keys: string[]): string[] => { return keys.reduce((acc: string[], key: string) => { @@ -220,6 +243,10 @@ export class Track extends React.Component { }, []); } + + /** + * calculating current keyframe, if the scrubber is right on the keyframe + */ @action calcCurrent = async (region: Doc) => { let currentkf: (Doc | undefined) = undefined; @@ -231,6 +258,10 @@ export class Track extends React.Component { } + /** + * interpolation. definetely needs to be changed. (currently involves custom linear splicing interpolations). + * Too complex right now. Also need to apply quadratic spline later on (for smoothness, instead applying "gains") + */ @action interpolate = async (left: Doc, right: Doc, regiondata: Doc) => { let leftNode = left.key as Doc; @@ -290,6 +321,10 @@ export class Track extends React.Component { }); } + /** + * finds region that corresponds to specific time (is there a region at this time?) + * linear O(n) (maybe possible to optimize this with other Data structures?) + */ @action findRegion = async (time: number) => { let foundRegion: (Doc | undefined) = undefined; @@ -303,6 +338,10 @@ export class Track extends React.Component { return foundRegion; } + + /** + * double click on track. Signalling keyframe creation. Problem with phantom regions + */ @action onInnerDoubleClick = (e: React.MouseEvent) => { let inner = this._inner.current!; @@ -311,6 +350,10 @@ export class Track extends React.Component { this.forceUpdate(); } + + /** + * creates a region (KEYFRAME.TSX stuff). + */ createRegion = (position: number) => { let regiondata = KeyframeFunc.defaultKeyframe(); regiondata.position = position; @@ -325,6 +368,11 @@ export class Track extends React.Component { } } + + + /** + * UI sstuff here. Not really much to change + */ render() { return (
    -- cgit v1.2.3-70-g09d2 From b25276d9442f5ae9196a72b4af05849b9e69ef2f Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Sat, 11 Jan 2020 15:42:58 +0900 Subject: phantom region fix, scrubber double click, fade kf --- src/client/views/animationtimeline/Keyframe.tsx | 104 +++++++---------------- src/client/views/animationtimeline/Timeline.scss | 2 + src/client/views/animationtimeline/Timeline.tsx | 4 +- src/client/views/animationtimeline/Track.tsx | 26 +++--- 4 files changed, 51 insertions(+), 85 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index c3c1f5f8c..62528483b 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -22,6 +22,7 @@ import { undoBatch, UndoManager } from "../../util/UndoManager"; */ export namespace KeyframeFunc { export enum KeyframeType { + end = "end", fade = "fade", default = "default", } @@ -194,8 +195,8 @@ export class Keyframe extends React.Component { if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); 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)!; + let start = await this.makeKeyData(this.regiondata.position, KeyframeFunc.KeyframeType.end)!; + let finish = await this.makeKeyData(this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.end)!; (fadeIn.key! as Doc).opacity = 1; (fadeOut.key! as Doc).opacity = 1; (start.key! as Doc).opacity = 0.1; @@ -206,7 +207,6 @@ export class Keyframe extends React.Component { @action makeKeyData = async (kfpos: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - const batch = UndoManager.StartBatch("makeKeyData"); let doclist = (await DocListCastAsync(this.regiondata.keyframes))!; let existingkf: (Doc | undefined) = undefined; doclist.forEach(TK => { @@ -218,19 +218,11 @@ export class Keyframe extends React.Component { TK.key = Doc.MakeCopy(this.props.node, true); TK.type = type; this.regiondata.keyframes!.push(TK); - - // myObservable++; - // UndoManager.AddEvent({ - // undo: action(() => myObservable--), - // redo: action(() => myObservable++) - // }); - let interpolationFunctions = new Doc(); interpolationFunctions.interpolationX = new List([0, 1]); interpolationFunctions.interpolationY = new List([0, 100]); interpolationFunctions.pathX = new List(); interpolationFunctions.pathY = new List(); - this.regiondata.functions!.push(interpolationFunctions); let found: boolean = false; this.regiondata.keyframes!.forEach(compkf => { @@ -244,7 +236,6 @@ export class Keyframe extends React.Component { return; } }); - batch.end(); return TK; } @@ -384,15 +375,6 @@ export class Keyframe extends React.Component { this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(NumCast(kf.time!), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); } - - @action - onKeyframeOver = (e: React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - this.props.node.backgroundColor = "#000000"; - } - - /** * custom keyframe context menu items (when clicking on the keyframe circle) */ @@ -412,7 +394,6 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("input", "Move", (val) => { runInAction(() => { - if (this.checkInput(val)) { let cannotMove: boolean = false; let kfIndex: number = this.keyframes.indexOf(kf); if (val < 0 || (val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time))) { @@ -422,7 +403,6 @@ export class Keyframe extends React.Component { this.keyframes[kfIndex].time = parseInt(val, 10); this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; } - } }); }); TimelineMenu.Instance.addMenu("Keyframe"); @@ -448,7 +428,6 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { runInAction(() => { - if (this.checkInput(val)) { let cannotMove: boolean = false; if (val < 0 || val > NumCast(this.keyframes[2].time) - this.regiondata.position) { cannotMove = true; @@ -457,59 +436,52 @@ export class Keyframe extends React.Component { this.regiondata.fadeIn = parseInt(val, 10); this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; } - } }); }), TimelineMenu.Instance.addItem("input", `fadeOut: ${this.regiondata.fadeOut}ms`, (val) => { runInAction(() => { - if (this.checkInput(val)) { - let cannotMove: boolean = false; - if (val < 0 || val > this.regiondata.position + this.regiondata.duration - NumCast(this.keyframes[this.keyframes.length - 3].time)) { - cannotMove = true; - } - if (!cannotMove) { - this.regiondata.fadeOut = parseInt(val, 10); - this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - val; - } + let cannotMove: boolean = false; + if (val < 0 || val > this.regiondata.position + this.regiondata.duration - NumCast(this.keyframes[this.keyframes.length - 3].time)) { + cannotMove = true; + } + if (!cannotMove) { + this.regiondata.fadeOut = parseInt(val, 10); + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - val; } }); }), TimelineMenu.Instance.addItem("input", `position: ${this.regiondata.position}ms`, (val) => { runInAction(() => { - if (this.checkInput(val)) { - let prevPosition = this.regiondata.position; - let cannotMove: boolean = false; - DocListCast(this.regions).forEach(region => { - if (NumCast(region.position) !== this.regiondata.position) { - if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration)))) { - cannotMove = true; - } + let prevPosition = this.regiondata.position; + let cannotMove: boolean = false; + DocListCast(this.regions).forEach(region => { + if (NumCast(region.position) !== this.regiondata.position) { + if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration)))) { + cannotMove = true; } - }); - if (!cannotMove) { - this.regiondata.position = parseInt(val, 10); - this.updateKeyframes(this.regiondata.position - prevPosition); } + }); + if (!cannotMove) { + this.regiondata.position = parseInt(val, 10); + this.updateKeyframes(this.regiondata.position - prevPosition); } }); }), TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => { runInAction(() => { - if (this.checkInput(val)) { - let cannotMove: boolean = false; - DocListCast(this.regions).forEach(region => { - if (NumCast(region.position) !== this.regiondata.position) { - val += this.regiondata.position; - if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration))) { - cannotMove = true; - } + let cannotMove: boolean = false; + DocListCast(this.regions).forEach(region => { + if (NumCast(region.position) !== this.regiondata.position) { + val += this.regiondata.position; + if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration))) { + cannotMove = true; } - }); - if (!cannotMove) { - this.regiondata.duration = parseInt(val, 10); - this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; - this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; } + }); + if (!cannotMove) { + this.regiondata.duration = parseInt(val, 10); + this.keyframes[this.keyframes.length - 1].time = this.regiondata.position + this.regiondata.duration; + this.keyframes[this.keyframes.length - 2].time = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut; } }); }), @@ -517,15 +489,6 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.openMenu(e.clientX, e.clientY); } - - /** - * checking if input is correct (might have to use external module) - */ - @action - checkInput = (val: any) => { - return typeof (val === "number"); - } - @action updateKeyframes = (incr: number, filter: number[] = []) => { this.keyframes.forEach(kf => { @@ -652,7 +615,7 @@ export class Keyframe extends React.Component { drawKeyframes = () => { let keyframeDivs: JSX.Element[] = []; DocListCast(this.regiondata.keyframes).forEach(kf => { - if (kf.type as KeyframeFunc.KeyframeType === KeyframeFunc.KeyframeType.default) { + if (kf.type as KeyframeFunc.KeyframeType !== KeyframeFunc.KeyframeType.end) { keyframeDivs.push(
    @@ -663,8 +626,7 @@ export class Keyframe extends React.Component { }} onDoubleClick={(e) => { e.preventDefault(); e.stopPropagation(); }}>
    ); - } - else { + } else { keyframeDivs.push(
    diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 493b084a8..76c8475d1 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -109,6 +109,7 @@ position: absolute; z-index: 1001; background-color: black; + pointer-events: none; .scrubberhead { top: -30px; @@ -119,6 +120,7 @@ border: 5px solid black; left: -15px; position: absolute; + pointer-events: all; } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 9cf600b5f..df25f709a 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -504,8 +504,8 @@ export class Timeline extends React.Component {
    {this.drawTicks()} -
    -
    +
    +
    {DocListCast(this.children).map(doc => )} diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index d9b8026c5..2ea3d168f 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -96,6 +96,7 @@ export class Track extends React.Component { /** * keyframe save logic. Needs to be changed so it's more efficient + * */ @action saveKeyframe = async (ref: Doc, regiondata: Doc) => { @@ -354,19 +355,20 @@ export class Track extends React.Component { /** * creates a region (KEYFRAME.TSX stuff). */ - createRegion = (position: number) => { - let regiondata = KeyframeFunc.defaultKeyframe(); - regiondata.position = position; - let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); - - if (rightRegion && rightRegion.position - regiondata.position <= 4000) { - regiondata.duration = rightRegion.position - regiondata.position; - } - if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { - this.regions.push(regiondata); - return regiondata; + createRegion = async (time: number) => { + if (await this.findRegion(time) === undefined){ //check if there is a region where double clicking (prevents phantom regions) + let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data + regiondata.position = time; //set position + let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); + + if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region + regiondata.duration = rightRegion.position - regiondata.position; + } + if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { + this.regions.push(regiondata); + return regiondata; + } } - } -- cgit v1.2.3-70-g09d2 From f7123e343024cf5760386c8e872e34e582d2d79e Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Sat, 11 Jan 2020 23:36:36 +0900 Subject: autocreate keyframe UNSTABLE --- src/client/views/animationtimeline/Keyframe.tsx | 80 +++++++++++++------------ src/client/views/animationtimeline/Track.tsx | 36 ++++++++++- 2 files changed, 77 insertions(+), 39 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 62528483b..5e4676f56 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -98,6 +98,41 @@ export namespace KeyframeFunc { return regiondata; }; + export const makeKeyData = async (regiondata:RegionData, time: number, badNode:Doc, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time + runInAction(async () => { + let doclist = (await DocListCastAsync(regiondata.keyframes))!; + let existingkf: (Doc | undefined) = undefined; + doclist.forEach(TK => { + if (TK.time === time) existingkf = TK; + }); + if (existingkf) return existingkf; + let TK: Doc = new Doc(); + TK.time = time; + TK.key = Doc.MakeCopy(badNode, true); + TK.type = type; + regiondata.keyframes!.push(TK); + let interpolationFunctions = new Doc(); + interpolationFunctions.interpolationX = new List([0, 1]); + interpolationFunctions.interpolationY = new List([0, 100]); + interpolationFunctions.pathX = new List(); + interpolationFunctions.pathY = new List(); + regiondata.functions!.push(interpolationFunctions); + let found: boolean = false; + regiondata.keyframes!.forEach(compkf => { + compkf = compkf as Doc; + if (time < NumCast(compkf.time) && !found) { + runInAction(() => { + regiondata.keyframes!.splice(doclist.indexOf(compkf as Doc), 0, TK); + regiondata.keyframes!.pop(); + found = true; + }); + return; + } + }); + return TK; + }); + }; + export const convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time", tickSpacing: number, tickIncrement: number) => { let time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement; switch (unit) { @@ -193,10 +228,10 @@ export class Keyframe extends React.Component { componentWillMount() { runInAction(async () => { if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); - 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.end)!; - let finish = await this.makeKeyData(this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.end)!; + let fadeIn = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, this.props.node, KeyframeFunc.KeyframeType.fade))! as any as Doc; + let fadeOut = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, this.props.node, KeyframeFunc.KeyframeType.fade))! as any as Doc; + let start = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position, this.props.node, KeyframeFunc.KeyframeType.end))! as any as Doc; + let finish = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, this.props.node, KeyframeFunc.KeyframeType.end))! as any as Doc; (fadeIn.key! as Doc).opacity = 1; (fadeOut.key! as Doc).opacity = 1; (start.key! as Doc).opacity = 0.1; @@ -205,39 +240,7 @@ export class Keyframe extends React.Component { }); } - @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 => { - 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([0, 1]); - interpolationFunctions.interpolationY = new List([0, 100]); - interpolationFunctions.pathX = new List(); - interpolationFunctions.pathY = new List(); - 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; - } - }); - return TK; - } + @action @@ -361,9 +364,10 @@ export class Keyframe extends React.Component { let offset = KeyframeFunc.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends let position = this.regiondata.position; - await this.makeKeyData(Math.round(position + offset)); + await KeyframeFunc.makeKeyData(this.regiondata, Math.round(position + offset), this.props.node); this.regiondata.hasData = true; this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied + } } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 2ea3d168f..c27a24ecf 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun } from "mobx"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun , toJS} from "mobx"; import "./Track.scss"; import { Doc, DocListCastAsync, DocListCast, Field } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; @@ -57,6 +57,19 @@ export class Track extends React.Component { "type", "zIndex" ]; + + private whiteList = [ + "data", + "height", + "opacity", + "width", + "x", + "y" + ] + + @observable private whitelist = [ + + ]; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; @@ -79,6 +92,7 @@ export class Track extends React.Component { if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); this.props.node.hidden = false; this.props.node.opacity = 1; + this.autoCreateKeyframe(); }); } @@ -129,6 +143,26 @@ export class Track extends React.Component { this._isOnKeyframe = false; } + /** + * autocreates keyframe + */ + @action + autoCreateKeyframe = async () => { + return reaction(() => [this.props.node.data, this.props.node.height, this.props.node.width, this.props.node.x, this.props.node.y, this.props.node.opacity], async changed => { + console.log("RAN!"); + //convert scrubber pos(pixel) to time + let time = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + //check for region + let region:(Doc | undefined) = await this.findRegion(time); + if (region !== undefined){ //if region at scrub time exist + if (DocListCast(region!.keyframes).find(kf => {return kf.time === time}) === undefined ){ + console.log("change has occured"); + } + } + + }); + } + /** * reverting back to previous state before editing on AT */ -- cgit v1.2.3-70-g09d2 From 04defabf37c5d3d9165c0ce07fefa43050e70584 Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Wed, 15 Jan 2020 01:38:06 +0900 Subject: autokeyframe still doesn't work --- src/client/views/animationtimeline/Keyframe.tsx | 103 +----------------------- src/client/views/animationtimeline/Timeline.tsx | 2 +- src/client/views/animationtimeline/Track.tsx | 50 ++++++------ 3 files changed, 28 insertions(+), 127 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 5e4676f56..2f2639c76 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -9,7 +9,6 @@ import { Cast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema"; import { Transform } from "../../util/Transform"; -import { InkField, StrokeData } from "../../../new_fields/InkField"; import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; import { CollectionDockingView } from "../collections/CollectionDockingView"; @@ -172,7 +171,6 @@ interface IProps { check: string; changeCurrentBarX: (x: number) => void; transform: Transform; - checkCallBack: (visible: boolean) => void; } @@ -214,17 +212,7 @@ export class Keyframe extends React.Component { @computed private get pixelDuration() { return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeIn() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeOut() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } - @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; - } - } - } - + componentWillMount() { runInAction(async () => { if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); @@ -418,12 +406,6 @@ export class Keyframe extends React.Component { */ @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { - TimelineMenu.Instance.addItem("button", "Add Ease", () => { - this.onContainerDown(kf, "interpolate"); - }), - TimelineMenu.Instance.addItem("button", "Add Path", () => { - this.onContainerDown(kf, "path"); - }), TimelineMenu.Instance.addItem("button", "Remove Region", () => { runInAction(() => { this.regions.splice(this.regions.indexOf(this.props.RegionData), 1); @@ -525,88 +507,7 @@ export class Keyframe extends React.Component { div.style.opacity = "0"; Doc.UnBrushDoc(this.props.node); } - - - private _reac: (undefined | IReactionDisposer) = undefined; - private _plotList: ([string, StrokeData] | undefined) = undefined; - private _interpolationKeyframe: (Doc | undefined) = undefined; - private _type: string = ""; - - - /** - * Need to fix this. skip - */ - @action - onContainerDown = (kf: Doc, type: string) => { - let listenerCreated = false; - this.props.checkCallBack(true); - 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; - listenerCreated = true; - const reac = reaction(() => { - return this.props.check; - }, () => { - if (this.props.check === "yes") this.onReactionListen(); - reac(); - this.props.checkCallBack(false); - }); - } - }); - } - - /** - * for custom draw interpolation. Need to be refactored - */ - @action - onReactionListen = () => { - if (this._reac && this._plotList && this._interpolationKeyframe) { - this.props.collection.backgroundColor = "#FFF"; - this._reac(); - let xPlots = new List(); - let yPlots = new List(); - 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; - } - } + ///////////////////////UI STUFF ///////////////////////// diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index df25f709a..fedffe8c1 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -508,7 +508,7 @@ export class Timeline extends React.Component {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => )}
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index c27a24ecf..fc8d0852f 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun , toJS} from "mobx"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun , toJS, isObservableArray, IObservableArray} from "mobx"; import "./Track.scss"; import { Doc, DocListCastAsync, DocListCast, Field } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; @@ -22,8 +22,6 @@ interface IProps { timelineVisible: boolean; check: string; changeCurrentBarX: (x: number) => void; - checkCallBack: (visible: boolean) => void; - } @observer @@ -58,18 +56,6 @@ export class Track extends React.Component { "zIndex" ]; - private whiteList = [ - "data", - "height", - "opacity", - "width", - "x", - "y" - ] - - @observable private whitelist = [ - - ]; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; @@ -143,24 +129,38 @@ export class Track extends React.Component { this._isOnKeyframe = false; } + + private whitelist = [ + "x", + "y", + "width", + "height", + "data" + ] /** * autocreates keyframe */ @action - autoCreateKeyframe = async () => { - return reaction(() => [this.props.node.data, this.props.node.height, this.props.node.width, this.props.node.x, this.props.node.y, this.props.node.opacity], async changed => { - console.log("RAN!"); + autoCreateKeyframe = async () => { + return reaction(async () => { + return this.whitelist.map(key => this.props.node[key]); + }, (changed, reaction) => { //convert scrubber pos(pixel) to time let time = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); //check for region - let region:(Doc | undefined) = await this.findRegion(time); - if (region !== undefined){ //if region at scrub time exist - if (DocListCast(region!.keyframes).find(kf => {return kf.time === time}) === undefined ){ - console.log("change has occured"); - } - } + //let region:(Doc | undefined) = await this.findRegion(time); + console.log(this.props.node.x); + console.log(this.props.node.y); + console.log(changed); + // if (region !== undefined){ //if region at scrub time exist + // if (DocListCast(region!.keyframes).find(kf => {return kf.time === time}) === undefined ){ + // console.log("change has occured"); + // } + // } + //reaction.dispose(); }); + } /** @@ -185,7 +185,7 @@ export class Track extends React.Component { let regiondata: (Doc | undefined) = await this.findRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); if (regiondata) { this.props.node.hidden = false; - await this.timeChange(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); + //await this.timeChange(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } else { this.props.node.hidden = true; this.props.node.opacity = 0; -- cgit v1.2.3-70-g09d2 From b481a3a3d95a7481c785373b9a088c442488dfde Mon Sep 17 00:00:00 2001 From: monikahedman Date: Tue, 14 Jan 2020 17:27:26 -0500 Subject: initial stylistic changes --- src/client/views/animationtimeline/Timeline.scss | 30 +++-- src/client/views/animationtimeline/Timeline.tsx | 18 +-- .../views/animationtimeline/TimelineOverview.scss | 138 +++++++++++---------- 3 files changed, 97 insertions(+), 89 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 76c8475d1..83988e324 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -1,6 +1,6 @@ @import "./../globalCssVariables.scss"; - +$timelineColor: #9acedf; .timeline-toolbox { position: absolute; @@ -13,6 +13,10 @@ align-items: center; top: 20px; + .timeline-icon { + color: $timelineColor; + } + div { padding: 0px; margin-left: 10px; @@ -24,7 +28,7 @@ width: auto; white-space: nowrap; font-size: 16px; - color: grey; + color: black; letter-spacing: 2px; text-transform: uppercase; } @@ -33,7 +37,7 @@ height: 40px; width: 80px; background-color: white; - border: 2px solid grey; + border: 2px solid black; border-radius: 20px; animation-fill-mode: forwards; animation-duration: 500ms; @@ -50,7 +54,7 @@ height: 35px; width: 35px; background-color: white; - border: 1px solid grey; + border: 1px solid black; border-radius: 20px; transition: transform 500ms ease-in-out; margin-left: 0px; @@ -65,7 +69,7 @@ width: 120px; white-space: nowrap; font-size: 16px; - color: grey; + color: black; letter-spacing: 2px; text-transform: uppercase; padding-left: 5px; @@ -112,13 +116,13 @@ pointer-events: none; .scrubberhead { - top: -30px; - height: 30px; - width: 30px; - background-color: transparent; + top: -20px; + height: 20px; + width: 20px; + background-color: white; border-radius: 50%; - border: 5px solid black; - left: -15px; + border: 3px solid black; + left: -9px; position: absolute; pointer-events: all; } @@ -132,7 +136,7 @@ overflow: hidden; background-color: white; position: absolute; - box-shadow: -10px 0px 10px 10px grey; + // box-shadow: -10px 0px 10px 10px red; } } @@ -156,7 +160,7 @@ float: left; border-style: solid; overflow-y: scroll; - overflow-x: hidden; + overflow-x: hidden; } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index fedffe8c1..589470111 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -8,7 +8,7 @@ import { Cast, NumCast, StrCast, BoolCast } 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,faPauseCircle, faEyeSlash,faEye,faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons"; +import { faPlayCircle, faBackward, faForward, faGripLines, faPauseCircle, faEyeSlash, faEye, faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons"; import { ContextMenu } from "../ContextMenu"; import { TimelineOverview } from "./TimelineOverview"; import { FieldViewProps } from "../nodes/FieldView"; @@ -43,14 +43,14 @@ export class Timeline extends React.Component { //readonly constants private readonly DEFAULT_TICK_SPACING: number = 50; - private readonly MAX_TITLE_HEIGHT = 75; + private readonly MAX_TITLE_HEIGHT = 75; private readonly MAX_CONTAINER_HEIGHT: number = 800; private readonly DEFAULT_TICK_INCREMENT: number = 1000; //height variables private DEFAULT_CONTAINER_HEIGHT: number = 330; private MIN_CONTAINER_HEIGHT: number = 205; - + //react refs @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @@ -224,7 +224,7 @@ export class Timeline extends React.Component { e.stopPropagation(); let scrubberbox = this._infoContainer.current!; let left = scrubberbox.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; + let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position } @@ -403,9 +403,9 @@ export class Timeline extends React.Component { let size = 40 * scale; //50 is default return (
    -
    -
    -
    +
    +
    +
    Timeline Overview
    Mode: {this.props.Document.isATOn ? "Authoring" : "Play"}
    @@ -460,7 +460,7 @@ export class Timeline extends React.Component { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; - roundToggleContainer.style.backgroundColor = "green"; + roundToggleContainer.style.backgroundColor = "#9acedf"; timelineContainer.style.top = "0px"; this.props.Document.isATOn = true; } @@ -499,7 +499,7 @@ export class Timeline extends React.Component { render() { return (
    -
    +
    diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index c7f9bd059..c643b978b 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -1,85 +1,89 @@ -@import "./../globalCssVariables.scss"; +@import "./../globalCssVariables.scss"; +$timelineColor: #9acedf; -.timeline-overview-container{ - padding: 0px; - margin: 0px; - width: 300px; +.timeline-overview-container { + padding: 0px; + margin: 0px; + width: 300px; height: 40px; - background: white; - position: relative; - border: 2px solid black; - - .timeline-overview-visible{ - position: absolute; - height: 100%; - background: green; + background: white; + position: relative; + border: 2px solid black; + + .timeline-overview-visible { + position: absolute; + height: 98%; + background: $timelineColor; display: inline-block; - margin: 0px; - padding: 0px; + margin: 0px; + padding: 0px; } - .timeline-overview-scrubber-container{ - margin: 0px; - padding: 0px; - position: absolute; + + .timeline-overview-scrubber-container { + margin: 0px; + padding: 0px; + position: absolute; height: 100%; - width: 2px; - top: 0px; - left: 0px; + width: 2px; + top: 0px; + left: 0px; z-index: 1001; - background-color:black; - display: inline-block; + background-color: black; + display: inline-block; - .timeline-overview-scrubber-head{ - padding: 0px; - margin: 0px; - position:absolute; - height: 30px; - width: 30px; - background-color:transparent; - border-radius: 50%; - border: 5px solid black; - left: -15px; - top: -30px; + .timeline-overview-scrubber-head { + padding: 0px; + margin: 0px; + position: absolute; + height: 10px; + width: 10px; + // background-color: black; + border-radius: 50%; + // border: 3px solid black; + left: -4px; + // top: -30px; + top: -10px; } } } -.timeline-play-bar{ - position: relative; - padding: 0px; - margin: 0px; - width: 300px; - height: 4px; - background-color: grey; +.timeline-play-bar { + position: relative; + padding: 0px; + margin: 0px; + width: 300px; + height: 4px; + background-color: $timelineColor; border-radius: 20px; - cursor: pointer; - - .timeline-play-head{ - position: absolute; - padding: 0px; - margin: 0px; - width: 20px; - height: 20px; - border-radius: 50%; - background-color: white; - border: 3px grey solid; - left: 0px; - top: -10px; - cursor: pointer; + cursor: pointer; + + .timeline-play-head { + position: absolute; + padding: 0px; + margin: 0px; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: white; + border: 3px $timelineColor; + left: 0px; + top: -10px; + cursor: pointer; } -} -.timeline-play-tail{ - position: absolute; - padding: 0px; - margin: 0px; - height: 4px; - width: 0px; - z-index: 1000; - background-color: green; +} + +.timeline-play-tail { + position: absolute; + padding: 0px; + margin: 0px; + height: 4px; + width: 0px; + z-index: 1000; + background-color: $timelineColor; border-radius: 20px; - margin-top: -4px; - cursor: pointer; + margin-top: -4px; + cursor: pointer; } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 95e321930a5e6f5a6fd86047542615dff49813f2 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Tue, 14 Jan 2020 19:35:47 -0500 Subject: color fixes --- src/client/views/animationtimeline/Timeline.scss | 9 ++++++++- src/client/views/animationtimeline/Timeline.tsx | 4 ++-- src/client/views/animationtimeline/TimelineOverview.scss | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 83988e324..060627c17 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -54,7 +54,7 @@ $timelineColor: #9acedf; height: 35px; width: 35px; background-color: white; - border: 1px solid black; + border: 3px solid black; border-radius: 20px; transition: transform 500ms ease-in-out; margin-left: 0px; @@ -80,6 +80,13 @@ $timelineColor: #9acedf; height: 100%; width: 1px; background-color: black; + color: black; +} + +.number-label { + color: black; + padding-left: 5px; + } .timeline-container { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 589470111..2332b4d5f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -131,7 +131,7 @@ export class Timeline extends React.Component { drawTicks = () => { let ticks = []; for (let i = 0; i < this._time / this._tickIncrement; i++) { - ticks.push(

    {this.toReadTime(i * this._tickIncrement)}

    ); + ticks.push(

    {this.toReadTime(i * this._tickIncrement)}

    ); } return ticks; } @@ -457,7 +457,7 @@ export class Timeline extends React.Component { timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isATOn = false; } else { - roundToggle.style.transform = "translate(45px, 0px)"; + roundToggle.style.transform = "translate(40px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; roundToggleContainer.style.backgroundColor = "#9acedf"; diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index c643b978b..3517d3f39 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -38,7 +38,7 @@ $timelineColor: #9acedf; position: absolute; height: 10px; width: 10px; - // background-color: black; + background-color: black; border-radius: 50%; // border: 3px solid black; left: -4px; @@ -68,9 +68,9 @@ $timelineColor: #9acedf; height: 20px; border-radius: 50%; background-color: white; - border: 3px $timelineColor; + border: 3px solid $timelineColor; left: 0px; - top: -10px; + top: -8px; cursor: pointer; } } -- cgit v1.2.3-70-g09d2 From da6ce86f604ad5a6e7857f073ebba0095fc5d467 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Tue, 14 Jan 2020 20:02:57 -0500 Subject: more minor changes --- src/client/views/animationtimeline/Timeline.scss | 20 ++++++++++++-------- src/client/views/animationtimeline/Timeline.tsx | 7 ++++--- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 060627c17..02c1bdb16 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -1,6 +1,7 @@ @import "./../globalCssVariables.scss"; $timelineColor: #9acedf; +$timelineDark: #77a1aa; .timeline-toolbox { position: absolute; @@ -78,15 +79,15 @@ $timelineColor: #9acedf; .tick { height: 100%; - width: 1px; + width: 2px; background-color: black; color: black; } .number-label { color: black; - padding-left: 5px; - + transform: rotate(-90deg) translate(-15px, 8px); + font-size: .85em; } .timeline-container { @@ -94,7 +95,7 @@ $timelineColor: #9acedf; height: 300px; position: absolute; background-color: $light-color-secondary; - box-shadow: 0px 10px 20px; + border-bottom: 2px solid $timelineDark; transition: transform 500ms ease; .info-container { @@ -139,7 +140,7 @@ $timelineColor: #9acedf; top: 30px; height: calc(100% - 30px); width: 100%; - border: 1px; + border: 2px solid black; overflow: hidden; background-color: white; position: absolute; @@ -155,6 +156,9 @@ $timelineColor: #9acedf; width: 100px; background-color: white; overflow: hidden; + border-left: 2px solid black; + border-top: 2px solid black; + border-bottom: 2px solid black; .datapane { top: 0px; @@ -172,10 +176,10 @@ $timelineColor: #9acedf; } .resize { - bottom: 5px; + bottom: 0px; position: absolute; - height: 30px; - width: 50px; + height: 20px; + width: 40px; left: calc(50% - 25px); } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 2332b4d5f..37d93f6c1 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -401,11 +401,12 @@ export class Timeline extends React.Component { */ private timelineToolBox = (scale: number) => { let size = 40 * scale; //50 is default + let iconSize = 25; return (
    -
    -
    -
    +
    +
    +
    Timeline Overview
    Mode: {this.props.Document.isATOn ? "Authoring" : "Play"}
    -- cgit v1.2.3-70-g09d2 From 68a8c2bdbeafc7bc799884a6c1dcea86dbfd9f27 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Fri, 24 Jan 2020 15:04:59 -0500 Subject: change branch --- src/client/views/animationtimeline/Timeline.scss | 92 ++++++++--- src/client/views/animationtimeline/Timeline.tsx | 88 ++++++++-- .../views/animationtimeline/TimelineOverview.scss | 29 ++-- .../views/animationtimeline/TimelineOverview.tsx | 180 ++++++++++++++------- 4 files changed, 284 insertions(+), 105 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 02c1bdb16..a8dd9b9e7 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -10,39 +10,79 @@ $timelineDark: #77a1aa; display: flex; align-items: flex-start; flex-direction: row; - justify-content: space-evenly; + // justify-content: space-evenly; align-items: center; - top: 20px; + top: 3px; + width: 100%; - .timeline-icon { - color: $timelineColor; + .overview-tool { + display: flex; + justify-content: center; } - div { - padding: 0px; + .playbackControls { + display: flex; + margin-left: 30px; + max-width: 84px; + width: 84px; + + .timeline-icon { + color: $timelineColor; + margin-left: 3px; + } + + } + + .grid-box { + display: grid; + grid-template-columns: [first] 50% [line2] 25% [line3] 25%; + width: calc(100% - 150px); + // width: 100%; margin-left: 10px; + + .time-box { + margin-left: 5px; + display: flex; + } + + .mode-box { + display: flex; + margin-left: 5px; + } + + .overview-box { + width: 100%; + display: flex; + } + + div { + padding: 0px; + // margin-left: 10px; + } } .animation-text { - font-size: 20px; + // font-size: 16px; height: auto; width: auto; white-space: nowrap; - font-size: 16px; + font-size: 14px; color: black; letter-spacing: 2px; text-transform: uppercase; } .round-toggle { - height: 40px; - width: 80px; + height: 20px; + width: 40px; + min-width: 40px; background-color: white; - border: 2px solid black; + border: 2px solid $timelineDark; border-radius: 20px; animation-fill-mode: forwards; animation-duration: 500ms; top: 30px; + margin-left: 5px; input { position: absolute; @@ -52,29 +92,30 @@ $timelineDark: #77a1aa; } .round-toggle-slider { - height: 35px; - width: 35px; + height: 17px; + width: 17px; background-color: white; - border: 3px solid black; - border-radius: 20px; + border: 2px solid $timelineDark; + border-radius: 50%; transition: transform 500ms ease-in-out; margin-left: 0px; - margin-top: 0.5px; + // margin-top: 0.5px; } } } .time-input { - height: 40px; - width: 120px; + height: 20px; + // width: 120px; + width: 100%; white-space: nowrap; - font-size: 16px; + font-size: 12px; color: black; letter-spacing: 2px; text-transform: uppercase; padding-left: 5px; - + margin-left: 5px; } .tick { @@ -99,7 +140,7 @@ $timelineDark: #77a1aa; transition: transform 500ms ease; .info-container { - margin-top: 80px; + margin-top: 50px; right: 20px; position: absolute; height: calc(100% - 100px); @@ -138,9 +179,12 @@ $timelineDark: #77a1aa; .trackbox { top: 30px; + // TODO: where is this 30px coming from? height: calc(100% - 30px); + // height: 100%; width: 100%; - border: 2px solid black; + border-top: 2px solid black; + border-bottom: 2px solid black; overflow: hidden; background-color: white; position: absolute; @@ -150,7 +194,7 @@ $timelineDark: #77a1aa; } .title-container { - margin-top: 110px; + margin-top: 80px; margin-left: 20px; height: calc(100% - 100px - 30px); width: 100px; @@ -159,6 +203,7 @@ $timelineDark: #77a1aa; border-left: 2px solid black; border-top: 2px solid black; border-bottom: 2px solid black; + border-right: 2px solid $timelineDark; .datapane { top: 0px; @@ -181,6 +226,7 @@ $timelineDark: #77a1aa; height: 20px; width: 40px; left: calc(50% - 25px); + color: $timelineDark; } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 37d93f6c1..e6201d431 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -3,7 +3,7 @@ import "./Timeline.scss"; import { listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; -import { observable, action, computed, runInAction } from "mobx"; +import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx"; import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Doc, DocListCast } from "../../../new_fields/Doc"; @@ -77,6 +77,11 @@ export class Timeline extends React.Component { @observable private _doubleClickEnabled = false; @observable private _titleHeight = 0; + // so a reaction can be made + @observable public _isAuthoring = this.props.Document.isATOn; + @observable private _resizeReaction?: IReactionDisposer; + @observable private _panelWidth = 0; + /** * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit. */ @@ -114,6 +119,19 @@ export class Timeline extends React.Component { this.props.Document.isATOn = !this.props.Document.isATOn; //turns the boolean on, saying AT (animation timeline) is on this.toggleHandle(); }); + + this._resizeReaction = reaction( + () => this.props.PanelWidth, + () => { + // if (!this.props.parent._isAuthoring) { + // runInAction(() => { + console.log("resizing"); + // this.setOverviewWidth(); + // }); + // } + }, + ); + } componentWillUnmount() { @@ -402,19 +420,58 @@ export class Timeline extends React.Component { private timelineToolBox = (scale: number) => { let size = 40 * scale; //50 is default let iconSize = 25; + + //decides if information should be omitted because the timeline is very small + // if its less than 950 pixels then it's going to be overlapping + let shouldCompress = false; + let width: number = this.props.PanelWidth(); + if (width < 850) { + shouldCompress = true; + } + + let modeString, overviewString, lengthString; + let modeType = this.props.Document.isATOn ? "Author" : "Play"; + + if (!shouldCompress) { + modeString = "Mode: " + modeType; + overviewString = "Overview:"; + lengthString = "Length: "; + } + else { + modeString = modeType; + overviewString = ""; + lengthString = ""; + } + + + // let modeType: string = this.props.Document.isATOn ? "Author" : "Play"; + // let modeString: string = "Mode: " + modeType; + // let overviewString: string = "Overview:"; + // let lengthString: string = "Length: "; + return (
    -
    -
    -
    -
    Timeline Overview
    - -
    Mode: {this.props.Document.isATOn ? "Authoring" : "Play"}
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +
    {overviewString}
    + +
    +
    +
    {modeString}
    +
    +
    +
    +
    +
    +
    {lengthString}
    + +
    -
    Length:
    -
    ); } @@ -457,13 +514,15 @@ export class Timeline extends React.Component { roundToggleContainer.style.backgroundColor = "white"; timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isATOn = false; + this._isAuthoring = false; } else { - roundToggle.style.transform = "translate(40px, 0px)"; + roundToggle.style.transform = "translate(20px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; roundToggleContainer.style.backgroundColor = "#9acedf"; timelineContainer.style.top = "0px"; this.props.Document.isATOn = true; + this._isAuthoring = true; } } @@ -498,6 +557,11 @@ export class Timeline extends React.Component { * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) */ render() { + console.log(this.props.PanelWidth()); + runInAction(() => { + this._panelWidth = this.props.PanelWidth(); + console.log("changing!!") + }); return (
    diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index 3517d3f39..a0b9d462b 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -1,23 +1,34 @@ @import "./../globalCssVariables.scss"; $timelineColor: #9acedf; +$timelineDark: #77a1aa; + +.timelineOverview-bounding { + width: 100%; + margin-right: 10px; +} .timeline-overview-container { - padding: 0px; - margin: 0px; - width: 300px; - height: 40px; + // padding: 0px; + margin-right: 5px; + // margin: 0px; + margin-left: 5px; + // width: 300px; + width: 100%; + height: 25px; background: white; position: relative; - border: 2px solid black; + border: 2px solid $timelineDark; + // width: 100%; .timeline-overview-visible { position: absolute; - height: 98%; + height: 21px; background: $timelineColor; display: inline-block; margin: 0px; padding: 0px; + // top: 1px; } .timeline-overview-scrubber-container { @@ -38,9 +49,9 @@ $timelineColor: #9acedf; position: absolute; height: 10px; width: 10px; - background-color: black; + background-color: white; border-radius: 50%; - // border: 3px solid black; + border: 2px solid black; left: -4px; // top: -30px; top: -10px; @@ -54,7 +65,7 @@ $timelineColor: #9acedf; position: relative; padding: 0px; margin: 0px; - width: 300px; + width: 100%; height: 4px; background-color: $timelineColor; border-radius: 20px; diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 4741969dc..4907a1ae1 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -1,101 +1,159 @@ -import * as React from "react"; -import {observable, action} from "mobx"; -import {observer} from "mobx-react"; -import "./TimelineOverview.scss"; +import * as React from "react"; +import { observable, action, computed, runInAction, reaction, IReactionDisposer } from "mobx"; +import { observer } from "mobx-react"; +import "./TimelineOverview.scss"; +import * as $ from 'jquery'; +import { Timeline } from "./Timeline"; - -interface TimelineOverviewProps{ - totalLength: number; - visibleLength:number; - visibleStart:number; - currentBarX:number; - isAuthoring: boolean; - changeCurrentBarX: (pixel:number) => void; - movePanX: (pixel:number) => any; +interface TimelineOverviewProps { + totalLength: number; + visibleLength: number; + visibleStart: number; + currentBarX: number; + isAuthoring: boolean; + parent: Timeline; + changeCurrentBarX: (pixel: number) => void; + movePanX: (pixel: number) => any; } @observer export class TimelineOverview extends React.Component{ - @observable private _visibleRef = React.createRef(); - @observable private _scrubberRef = React.createRef(); - private readonly DEFAULT_HEIGHT = 50; - private readonly DEFAULT_WIDTH = 300; + @observable private _visibleRef = React.createRef(); + @observable private _scrubberRef = React.createRef(); + @observable private overviewBarWidth: number = 0; + @observable private _authoringReaction?: IReactionDisposer; + @observable private _resizeReaction?: IReactionDisposer; + private readonly DEFAULT_HEIGHT = 50; + private readonly DEFAULT_WIDTH = 300; + + componentDidMount = () => { + this.setOverviewWidth(); + + this._authoringReaction = reaction( + () => this.props.parent._isAuthoring, + () => { + if (!this.props.parent._isAuthoring) { + runInAction(() => { + this.setOverviewWidth(); + }); + } + }, + ); + // this._resizeReaction = reaction( + // () => this.props.parent.props.PanelWidth, + // () => { + // // if (!this.props.parent._isAuthoring) { + // // runInAction(() => { + // console.log("resizing"); + // this.setOverviewWidth(); + // // }); + // // } + // }, + // ); + } + + componentWillUnmount = () => { + this._authoringReaction && this._authoringReaction(); + this._resizeReaction && this._resizeReaction(); + } @action - onPointerDown = (e:React.PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - document.removeEventListener("pointermove", this.onPanX); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointermove", this.onPanX); - document.addEventListener("pointerup", this.onPointerUp); + setOverviewWidth() { + let width = $("#timelineOverview").width(); + if (width) this.overviewBarWidth = width; + else this.overviewBarWidth = 0; + } + + @action + onPointerDown = (e: React.PointerEvent) => { + e.stopPropagation(); + e.preventDefault(); + document.removeEventListener("pointermove", this.onPanX); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPanX); + document.addEventListener("pointerup", this.onPointerUp); } @action onPanX = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - let movX = (this.props.visibleStart / this.props.totalLength)* (this.DEFAULT_WIDTH) + e.movementX; - this.props.movePanX((movX / (this.DEFAULT_WIDTH )) * this.props.totalLength); + e.stopPropagation(); + e.preventDefault(); + let movX = (this.props.visibleStart / this.props.totalLength) * (this.DEFAULT_WIDTH) + e.movementX; + // let movX = (this.props.visibleStart / this.props.totalLength) * (this.overviewWidth) + e.movementX; + this.props.movePanX((movX / (this.DEFAULT_WIDTH)) * this.props.totalLength); + // this.props.movePanX((movX / (this.overviewWidth) * this.props.totalLength); + + // console.log(this.props.totalLength); } @action onPointerUp = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - document.removeEventListener("pointermove", this.onPanX); - document.removeEventListener("pointerup", this.onPointerUp); + e.stopPropagation(); + e.preventDefault(); + document.removeEventListener("pointermove", this.onPanX); + document.removeEventListener("pointerup", this.onPointerUp); } @action - onScrubberDown = ( e:React.PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.removeEventListener("pointermove", this.onScrubberMove); - document.removeEventListener("pointerup", this.onScrubberUp); - document.addEventListener("pointermove", this.onScrubberMove); + onScrubberDown = (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.removeEventListener("pointermove", this.onScrubberMove); + document.removeEventListener("pointerup", this.onScrubberUp); + document.addEventListener("pointermove", this.onScrubberMove); document.addEventListener("pointerup", this.onScrubberUp); } @action onScrubberMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let scrubberRef = this._scrubberRef.current!; - let left = scrubberRef.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left); - this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); + e.preventDefault(); + e.stopPropagation(); + let scrubberRef = this._scrubberRef.current!; + let left = scrubberRef.getBoundingClientRect().left; + let offsetX = Math.round(e.clientX - left); + this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); } @action - onScrubberUp = (e:PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - document.removeEventListener("pointermove", this.onScrubberMove); + onScrubberUp = (e: PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + document.removeEventListener("pointermove", this.onScrubberMove); document.removeEventListener("pointerup", this.onScrubberUp); } - render(){ + render() { + // calculates where everything should fall based on its size + let percentVisible = this.props.visibleLength / this.props.totalLength; + let visibleBarWidth = percentVisible * this.overviewBarWidth; + + let percentScrubberStart = this.props.currentBarX / this.props.totalLength; + let scrubberStart = percentScrubberStart * this.overviewBarWidth; + + let percentBarStart = this.props.visibleStart / this.props.totalLength; + let barStart = percentBarStart * this.overviewBarWidth; + let timeline = this.props.isAuthoring ? [ -
    -
    , -
    + +
    +
    , +
    -
    +
    ] : [ -
    -
    -
    , -
    - ]; - return( -
    +
    +
    +
    , +
    + ]; + return ( +
    {timeline}
    - ); + ); } } -- cgit v1.2.3-70-g09d2 From e73bc263f7e0b6bc8efc62302785c5e3d82ce799 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Wed, 29 Jan 2020 16:43:14 -0500 Subject: switch --- src/client/views/animationtimeline/Track.tsx | 79 +++++++++++----------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- 2 files changed, 41 insertions(+), 40 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index fc8d0852f..1ca8022a1 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun , toJS, isObservableArray, IObservableArray} from "mobx"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun, toJS, isObservableArray, IObservableArray, trace } from "mobx"; import "./Track.scss"; import { Doc, DocListCastAsync, DocListCast, Field } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; @@ -43,20 +43,20 @@ export class Track extends React.Component { "baseLayout", "backgroundLayout", "layout", - "title", - "AnimationLength", - "author", - "baseProto", - "creationDate", - "isATOn", - "isPrototype", - "lastOpened", - "proto", - "type", + "title", + "AnimationLength", + "author", + "baseProto", + "creationDate", + "isATOn", + "isPrototype", + "lastOpened", + "proto", + "type", "zIndex" ]; - private readonly MAX_TITLE_HEIGHT = 75; + private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } @@ -78,7 +78,7 @@ export class Track extends React.Component { if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); this.props.node.hidden = false; this.props.node.opacity = 1; - this.autoCreateKeyframe(); + this.autoCreateKeyframe(); }); } @@ -131,28 +131,29 @@ export class Track extends React.Component { private whitelist = [ - "x", - "y", - "width", - "height", + "x", + "y", + "width", + "height", "data" ] /** * autocreates keyframe */ - @action - autoCreateKeyframe = async () => { - return reaction(async () => { - return this.whitelist.map(key => this.props.node[key]); - }, (changed, reaction) => { + @action + autoCreateKeyframe = () => { + const { node } = this.props; + return reaction(() => { + return this.whitelist.map(key => node[key]); + }, (changed, reaction) => { //convert scrubber pos(pixel) to time - let time = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + let time = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); //check for region - //let region:(Doc | undefined) = await this.findRegion(time); - console.log(this.props.node.x); - console.log(this.props.node.y); - console.log(changed); - + this.findRegion(time).then((region) => { + console.log(changed); + }); + + // if (region !== undefined){ //if region at scrub time exist // if (DocListCast(region!.keyframes).find(kf => {return kf.time === time}) === undefined ){ // console.log("change has occured"); @@ -160,7 +161,7 @@ export class Track extends React.Component { // } //reaction.dispose(); }); - + } /** @@ -199,21 +200,21 @@ export class Track extends React.Component { @action timelineVisibleReaction = () => { return reaction(() => { - return this.props.timelineVisible; + return this.props.timelineVisible; }, isVisible => { this.revertState(); - if (isVisible){ + if (isVisible) { DocListCast(this.regions).forEach(region => { - if (!BoolCast((Cast(region, Doc) as Doc).hasData)){ - for (let i = 0; i < 4; i++){ - DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = Doc.MakeCopy(this.props.node, true); - if (i === 0 || i === 3){ + if (!BoolCast((Cast(region, Doc) as Doc).hasData)) { + for (let i = 0; i < 4; i++) { + DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = Doc.MakeCopy(this.props.node, true); + if (i === 0 || i === 3) { DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key.opacity = 0.1; } } - console.log("saving keyframes"); + console.log("saving keyframes"); } - }); + }); } }); } @@ -390,11 +391,11 @@ export class Track extends React.Component { * creates a region (KEYFRAME.TSX stuff). */ createRegion = async (time: number) => { - if (await this.findRegion(time) === undefined){ //check if there is a region where double clicking (prevents phantom regions) + if (await this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data regiondata.position = time; //set position let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); - + if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region regiondata.duration = rightRegion.position - regiondata.position; } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index c85b59488..81d176efd 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -26,7 +26,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { export class CollectionFreeFormDocumentView extends DocComponent(PositionDocument) { _disposer: IReactionDisposer | undefined = undefined; get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive - get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; } + get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px)`; } get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); } get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); } get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.layoutDoc[WidthSym](); } -- cgit v1.2.3-70-g09d2 From 2320240229cd8e89e8f5abe917053caad7bf38c2 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sat, 1 Feb 2020 13:50:32 -0500 Subject: overveiw resize --- src/client/views/animationtimeline/Timeline.tsx | 18 ++++++++++++++++-- .../views/animationtimeline/TimelineOverview.tsx | 4 ++++ 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index e6201d431..8b943f209 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -449,6 +449,9 @@ export class Timeline extends React.Component { // let overviewString: string = "Overview:"; // let lengthString: string = "Length: "; + console.log("visible: " + this._visibleLength) + console.log("total: " + this._totalLength) + return (
    @@ -459,7 +462,7 @@ export class Timeline extends React.Component {
    {overviewString}
    - +
    {modeString}
    @@ -551,17 +554,28 @@ export class Timeline extends React.Component { } + @action.bound + changeLenths() { + if (this._infoContainer.current) { + this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) + this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts + } + } + /** * if you have any question here, just shoot me an email or text. * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) */ render() { - console.log(this.props.PanelWidth()); + // console.log(this.props.PanelWidth()); runInAction(() => { this._panelWidth = this.props.PanelWidth(); + this.changeLenths(); console.log("changing!!") }); + + // change visible and total width return (
    diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 4907a1ae1..caa97bb70 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -15,6 +15,7 @@ interface TimelineOverviewProps { parent: Timeline; changeCurrentBarX: (pixel: number) => void; movePanX: (pixel: number) => any; + panelWidth: number; } @@ -125,8 +126,11 @@ export class TimelineOverview extends React.Component{ } render() { + + console.log("helo") // calculates where everything should fall based on its size let percentVisible = this.props.visibleLength / this.props.totalLength; + console.log(this.props.visibleLength) let visibleBarWidth = percentVisible * this.overviewBarWidth; let percentScrubberStart = this.props.currentBarX / this.props.totalLength; -- cgit v1.2.3-70-g09d2 From a23aa80dcb4c0278091de6a619a195072a564a7c Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 1 Feb 2020 13:50:37 -0500 Subject: some cleanup --- src/client/views/animationtimeline/Keyframe.tsx | 1 + src/client/views/animationtimeline/Track.tsx | 162 +++++++++--------------- 2 files changed, 64 insertions(+), 99 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 2f2639c76..9f4d80d2b 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -99,6 +99,7 @@ export namespace KeyframeFunc { export const makeKeyData = async (regiondata:RegionData, time: number, badNode:Doc, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time runInAction(async () => { + console.log("ran"); let doclist = (await DocListCastAsync(regiondata.keyframes))!; let existingkf: (Doc | undefined) = undefined; doclist.forEach(TK => { diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 1ca8022a1..6d9cfe55c 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -33,33 +33,17 @@ export class Track extends React.Component { @observable private _onKeyframe: (Doc | undefined) = undefined; @observable private _onRegionData: (Doc | undefined) = undefined; @observable private _storedState: (Doc | undefined) = undefined; - @observable private filterList = [ - "regions", - "cursors", - "hidden", - "nativeHeight", - "nativeWidth", - "schemaColumns", - "baseLayout", - "backgroundLayout", - "layout", - "title", - "AnimationLength", - "author", - "baseProto", - "creationDate", - "isATOn", - "isPrototype", - "lastOpened", - "proto", - "type", - "zIndex" - ]; - private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; - + private whitelist = [ + "x", + "y", + "width", + "height", + "data" + ]; @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } + // @computed private get time() {} ////////// life cycle functions/////////////// componentWillMount() { @@ -78,7 +62,7 @@ export class Track extends React.Component { if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); this.props.node.hidden = false; this.props.node.opacity = 1; - this.autoCreateKeyframe(); + // this.autoCreateKeyframe(); }); } @@ -130,13 +114,6 @@ export class Track extends React.Component { } - private whitelist = [ - "x", - "y", - "width", - "height", - "data" - ] /** * autocreates keyframe */ @@ -146,22 +123,20 @@ export class Track extends React.Component { return reaction(() => { return this.whitelist.map(key => node[key]); }, (changed, reaction) => { + console.log(changed); //convert scrubber pos(pixel) to time let time = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); //check for region this.findRegion(time).then((region) => { - console.log(changed); + if (region !== undefined){ //if region at scrub time exist + let r = region as any as RegionData; //for some region is returning undefined... which is not the case + if (DocListCast(r.keyframes).find(kf => kf.time === time) === undefined ){ //basically when there is no additional keyframe at that timespot + KeyframeFunc.makeKeyData(r, time, this.props.node, KeyframeFunc.KeyframeType.default); + } + } + // reaction.dispose(); }); - - - // if (region !== undefined){ //if region at scrub time exist - // if (DocListCast(region!.keyframes).find(kf => {return kf.time === time}) === undefined ){ - // console.log("change has occured"); - // } - // } - //reaction.dispose(); }); - } /** @@ -186,7 +161,7 @@ export class Track extends React.Component { let regiondata: (Doc | undefined) = await this.findRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); if (regiondata) { this.props.node.hidden = false; - //await this.timeChange(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); + await this.timeChange(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } else { this.props.node.hidden = true; this.props.node.opacity = 0; @@ -267,19 +242,6 @@ export class Track extends React.Component { } - - /** - * changing the filter here - */ - @action - private filterKeys = (keys: string[]): string[] => { - return keys.reduce((acc: string[], key: string) => { - if (!this.filterList.includes(key)) acc.push(key); - return acc; - }, []); - } - - /** * calculating current keyframe, if the scrubber is right on the keyframe */ @@ -302,50 +264,52 @@ export class Track extends React.Component { interpolate = async (left: Doc, right: Doc, regiondata: Doc) => { let leftNode = left.key as Doc; let rightNode = right.key as Doc; - const dif_time = NumCast(right.time) - NumCast(left.time); - const timeratio = (KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement) - NumCast(left.time)) / dif_time; //linear - let keyframes = (await DocListCastAsync(regiondata.keyframes!))!; - let indexLeft = keyframes.indexOf(left); - let interY: List = (await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List)!; - 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 = await ((regiondata.functions as List)[indexLeft] as Doc).pathX as List; - let pathY: List = await ((regiondata.functions as List)[indexLeft] as Doc).pathY as List; - 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 => { + // const dif_time = NumCast(right.time) - NumCast(left.time); + // const timeratio = (KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement) - NumCast(left.time)) / dif_time; //linear + // let keyframes = (await DocListCastAsync(regiondata.keyframes!))!; + // let indexLeft = keyframes.indexOf(left); + // let interY: List = (await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List)!; + // 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 = await ((regiondata.functions as List)[indexLeft] as Doc).pathX as List; + // let pathY: List = await ((regiondata.functions as List)[indexLeft] as Doc).pathY as List; + // 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.whitelist.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; - } else { - const diff = NumCast(rightNode[key]) - NumCast(leftNode[key]); - const adjusted = diff * finalRatio; - this.props.node[key] = NumCast(leftNode[key]) + adjusted; - } + // if ((key === "x" || key === "y") && pathX.length !== 0) { + // if (key === "x") this.props.node[key] = proposedX; + // if (key === "y") this.props.node[key] = proposedY; + // } else { + // const diff = NumCast(rightNode[key]) - NumCast(leftNode[key]); + // const adjusted = diff * finalRatio; + // this.props.node[key] = NumCast(leftNode[key]) + adjusted; + // } + let dif = NumCast(rightNode[key]) - NumCast(leftNode[key]); + } else { let stored = leftNode[key]; if (stored instanceof ObjectField) { -- cgit v1.2.3-70-g09d2 From 648aaeda9255f944275cb7c5ecbbceb669fa6c57 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 1 Feb 2020 13:52:55 -0500 Subject: another set of changes --- src/client/views/animationtimeline/Track.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 6d9cfe55c..e5e7a364c 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -226,8 +226,7 @@ export class Track extends React.Component { 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 => { + this.whitelist.forEach(key => { if (!kfNode[key]) { this.props.node[key] = undefined; } else { -- cgit v1.2.3-70-g09d2 From 78810a40e22510eefe747c099e73fa3f2c4860a9 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sat, 1 Feb 2020 15:03:15 -0500 Subject: ui changes cl.ose to done --- src/client/views/animationtimeline/Keyframe.scss | 138 ++++++++++----------- src/client/views/animationtimeline/Keyframe.tsx | 79 ++++++------ src/client/views/animationtimeline/Timeline.scss | 24 ++-- src/client/views/animationtimeline/Timeline.tsx | 24 +--- .../views/animationtimeline/TimelineOverview.tsx | 51 ++++---- src/client/views/animationtimeline/Track.tsx | 2 +- 6 files changed, 153 insertions(+), 165 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.scss b/src/client/views/animationtimeline/Keyframe.scss index b1e8b0b65..8ff1c53f5 100644 --- a/src/client/views/animationtimeline/Keyframe.scss +++ b/src/client/views/animationtimeline/Keyframe.scss @@ -1,94 +1,94 @@ -@import "./../globalCssVariables.scss"; +@import "./../globalCssVariables.scss"; + + +$timelineColor: #9acedf; +$timelineDark: #77a1aa; .bar { height: 100%; width: 5px; position: absolute; - + // pointer-events: none; .menubox { - width: 200px; - height:200px; + width: 200px; + height: 200px; top: 50%; - position: relative; - background-color: $light-color; - .menutable{ - tr:nth-child(odd){ - background-color:$light-color-secondary; + 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; + .leftResize { + left: -10px; + border: 3px solid black; + } + + .rightResize { + right: -10px; + border: 3px solid black; } - .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; + + .keyframe-indicator { + height: 20px; + width: 20px; + top: calc(50% - 10px); + background-color: white; + -ms-transform: rotate(45deg); + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + z-index: 1000; + position: absolute; } - .fadeLeft{ - left:0px; - height:100%; - position:absolute; - pointer-events: none; - background: linear-gradient(to left, #4d9900 10%, $light-color); + + .keyframeCircle { + left: -10px; + border: 3px solid $timelineDark; } - .fadeRight{ - right:0px; - height:100%; - position:absolute; - pointer-events: none; - background: linear-gradient(to right, #4d9900 10%, $light-color); + .fadeLeft { + left: 0px; + height: 100%; + position: absolute; + pointer-events: none; + background: linear-gradient(to left, $timelineColor 10%, $light-color); } - .divider{ - height:100%; - width: 1px; - position: absolute; - background-color:black; - cursor: col-resize; - pointer-events:none; + + .fadeRight { + right: 0px; + height: 100%; + position: absolute; + pointer-events: none; + background: linear-gradient(to right, $timelineColor 10%, $light-color); } - .keyframe{ - height:100%; - position:absolute; + + .divider { + height: 100%; + width: 1px; + position: absolute; + background-color: black; + cursor: col-resize; + pointer-events: none; } - .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; + + .keyframe { + height: 100%; + position: absolute; } - .fadeIn-container, .fadeOut-container, .body-container{ - position:absolute; - height:100%; + .fadeIn-container, + .fadeOut-container, + .body-container { + position: absolute; + height: 100%; background-color: rgba(0, 0, 0, 0.5); - opacity: 0; + opacity: 0; } - - -} - +} \ No newline at end of file diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 2f2639c76..9c486a6d6 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -93,11 +93,11 @@ export namespace KeyframeFunc { regiondata.fadeIn = 1000; regiondata.fadeOut = 1000; regiondata.functions = new List(); - regiondata.hasData = false; + regiondata.hasData = false; return regiondata; }; - export const makeKeyData = async (regiondata:RegionData, time: number, badNode:Doc, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time + export const makeKeyData = async (regiondata: RegionData, time: number, badNode: Doc, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time runInAction(async () => { let doclist = (await DocListCastAsync(regiondata.keyframes))!; let existingkf: (Doc | undefined) = undefined; @@ -155,7 +155,7 @@ export const RegionDataSchema = createSchema({ keyframes: listSpec(Doc), fadeIn: defaultSpec("number", 0), fadeOut: defaultSpec("number", 0), - functions: listSpec(Doc), + functions: listSpec(Doc), hasData: defaultSpec("boolean", false) }); export type RegionData = makeInterface<[typeof RegionDataSchema]>; @@ -212,7 +212,7 @@ export class Keyframe extends React.Component { @computed private get pixelDuration() { return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeIn() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeOut() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } - + componentWillMount() { runInAction(async () => { if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); @@ -228,7 +228,7 @@ export class Keyframe extends React.Component { }); } - + @action @@ -313,10 +313,10 @@ export class Keyframe extends React.Component { } else if (NumCast(this.keyframes[1].time) + offset >= NumCast(this.keyframes[2].time)) { this.regiondata.position = NumCast(this.keyframes[2].time) - this.regiondata.fadeIn; this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - NumCast(this.keyframes[2].time) + this.regiondata.fadeIn; - } else if (NumCast(this.keyframes[0].time) + offset <= 0){ - this.regiondata.position = 0; - this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time); - }else { + } else if (NumCast(this.keyframes[0].time) + offset <= 0) { + this.regiondata.position = 0; + this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time); + } else { this.regiondata.duration -= offset; this.regiondata.position += offset; } @@ -353,9 +353,9 @@ export class Keyframe extends React.Component { if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends let position = this.regiondata.position; await KeyframeFunc.makeKeyData(this.regiondata, Math.round(position + offset), this.props.node); - this.regiondata.hasData = true; + this.regiondata.hasData = true; this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied - + } } @@ -386,15 +386,15 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("input", "Move", (val) => { runInAction(() => { - let cannotMove: boolean = false; - let kfIndex: number = this.keyframes.indexOf(kf); - if (val < 0 || (val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time))) { - cannotMove = true; - } - if (!cannotMove) { - this.keyframes[kfIndex].time = parseInt(val, 10); - this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; - } + let cannotMove: boolean = false; + let kfIndex: number = this.keyframes.indexOf(kf); + if (val < 0 || (val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time))) { + cannotMove = true; + } + if (!cannotMove) { + this.keyframes[kfIndex].time = parseInt(val, 10); + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + } }); }); TimelineMenu.Instance.addMenu("Keyframe"); @@ -406,22 +406,22 @@ export class Keyframe extends React.Component { */ @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { - TimelineMenu.Instance.addItem("button", "Remove Region", () => { - runInAction(() => { - this.regions.splice(this.regions.indexOf(this.props.RegionData), 1); - } - ); - }), + TimelineMenu.Instance.addItem("button", "Remove Region", () => { + runInAction(() => { + this.regions.splice(this.regions.indexOf(this.props.RegionData), 1); + } + ); + }), TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { runInAction(() => { - let cannotMove: boolean = false; - if (val < 0 || val > NumCast(this.keyframes[2].time) - this.regiondata.position) { - cannotMove = true; - } - if (!cannotMove) { - this.regiondata.fadeIn = parseInt(val, 10); - this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; - } + let cannotMove: boolean = false; + if (val < 0 || val > NumCast(this.keyframes[2].time) - this.regiondata.position) { + cannotMove = true; + } + if (!cannotMove) { + this.regiondata.fadeIn = parseInt(val, 10); + this.keyframes[1].time = this.regiondata.position + this.regiondata.fadeIn; + } }); }), TimelineMenu.Instance.addItem("input", `fadeOut: ${this.regiondata.fadeOut}ms`, (val) => { @@ -507,7 +507,7 @@ export class Keyframe extends React.Component { div.style.opacity = "0"; Doc.UnBrushDoc(this.props.node); } - + ///////////////////////UI STUFF ///////////////////////// @@ -524,7 +524,7 @@ export class Keyframe extends React.Component { keyframeDivs.push(
    -
    { e.preventDefault(); e.stopPropagation(); this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { +
    { e.preventDefault(); e.stopPropagation(); this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); this.makeKeyframeMenu(kf, e.nativeEvent); @@ -577,18 +577,19 @@ export class Keyframe extends React.Component { /** * rendering that green region */ + //154, 206, 223 render() { return (
    -
    -
    +
    +
    {this.drawKeyframes()} {this.drawKeyframeDividers()}
    diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index a8dd9b9e7..40479559d 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -206,18 +206,18 @@ $timelineDark: #77a1aa; border-right: 2px solid $timelineDark; .datapane { - top: 0px; - width: 100px; - height: 30%; - border: 1px solid $dark-color; - background-color: $intermediate-color; - color: white; - position: relative; - float: left; - border-style: solid; - overflow-y: scroll; - overflow-x: hidden; - } + top: 0px; + width: 100px; + height: 30%; + border: 1px solid $dark-color; + background-color: $intermediate-color; + color: white; + position: relative; + float: left; + border-style: solid; + overflow-y: scroll; + overflow-x: hidden; +} } .resize { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 8b943f209..5ff721ebb 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -120,18 +120,6 @@ export class Timeline extends React.Component { this.toggleHandle(); }); - this._resizeReaction = reaction( - () => this.props.PanelWidth, - () => { - // if (!this.props.parent._isAuthoring) { - // runInAction(() => { - console.log("resizing"); - // this.setOverviewWidth(); - // }); - // } - }, - ); - } componentWillUnmount() { @@ -443,15 +431,6 @@ export class Timeline extends React.Component { lengthString = ""; } - - // let modeType: string = this.props.Document.isATOn ? "Author" : "Play"; - // let modeString: string = "Mode: " + modeType; - // let overviewString: string = "Overview:"; - // let lengthString: string = "Length: "; - - console.log("visible: " + this._visibleLength) - console.log("total: " + this._totalLength) - return (
    @@ -462,7 +441,7 @@ export class Timeline extends React.Component {
    {overviewString}
    - +
    {modeString}
    @@ -572,7 +551,6 @@ export class Timeline extends React.Component { runInAction(() => { this._panelWidth = this.props.PanelWidth(); this.changeLenths(); - console.log("changing!!") }); // change visible and total width diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index caa97bb70..9a881264f 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -4,6 +4,7 @@ import { observer } from "mobx-react"; import "./TimelineOverview.scss"; import * as $ from 'jquery'; import { Timeline } from "./Timeline"; +import { Keyframe, KeyframeFunc } from "./Keyframe"; interface TimelineOverviewProps { @@ -15,7 +16,9 @@ interface TimelineOverviewProps { parent: Timeline; changeCurrentBarX: (pixel: number) => void; movePanX: (pixel: number) => any; - panelWidth: number; + time: number; + tickSpacing: number; + tickIncrement: number; } @@ -25,7 +28,9 @@ export class TimelineOverview extends React.Component{ @observable private _scrubberRef = React.createRef(); @observable private overviewBarWidth: number = 0; @observable private _authoringReaction?: IReactionDisposer; - @observable private _resizeReaction?: IReactionDisposer; + @observable private visibleTime: number = 0; + @observable private currentX: number = 0; + @observable private visibleStart: number = 0; private readonly DEFAULT_HEIGHT = 50; private readonly DEFAULT_WIDTH = 300; @@ -42,22 +47,10 @@ export class TimelineOverview extends React.Component{ } }, ); - // this._resizeReaction = reaction( - // () => this.props.parent.props.PanelWidth, - // () => { - // // if (!this.props.parent._isAuthoring) { - // // runInAction(() => { - // console.log("resizing"); - // this.setOverviewWidth(); - // // }); - // // } - // }, - // ); } componentWillUnmount = () => { this._authoringReaction && this._authoringReaction(); - this._resizeReaction && this._resizeReaction(); } @action @@ -85,8 +78,6 @@ export class TimelineOverview extends React.Component{ // let movX = (this.props.visibleStart / this.props.totalLength) * (this.overviewWidth) + e.movementX; this.props.movePanX((movX / (this.DEFAULT_WIDTH)) * this.props.totalLength); // this.props.movePanX((movX / (this.overviewWidth) * this.props.totalLength); - - // console.log(this.props.totalLength); } @action @@ -125,18 +116,36 @@ export class TimelineOverview extends React.Component{ document.removeEventListener("pointerup", this.onScrubberUp); } + @action + getTimes() { + let vis = KeyframeFunc.convertPixelTime(this.props.visibleLength, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + let x = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + let start = KeyframeFunc.convertPixelTime(this.props.visibleStart, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + this.visibleTime = vis; + this.currentX = x; + this.visibleStart = start; + } + render() { - console.log("helo") + this.getTimes(); // calculates where everything should fall based on its size - let percentVisible = this.props.visibleLength / this.props.totalLength; - console.log(this.props.visibleLength) + // let percentVisible = this.props.visibleLength / this.props.totalLength; + // let visibleBarWidth = percentVisible * this.overviewBarWidth; + + // let percentScrubberStart = this.props.currentBarX / this.props.totalLength; + // let scrubberStart = percentScrubberStart * this.overviewBarWidth; + + // let percentBarStart = this.props.visibleStart / this.props.totalLength; + // let barStart = percentBarStart * this.overviewBarWidth; + + let percentVisible = this.visibleTime / this.props.time; let visibleBarWidth = percentVisible * this.overviewBarWidth; - let percentScrubberStart = this.props.currentBarX / this.props.totalLength; + let percentScrubberStart = this.currentX / this.props.time; let scrubberStart = percentScrubberStart * this.overviewBarWidth; - let percentBarStart = this.props.visibleStart / this.props.totalLength; + let percentBarStart = this.visibleStart / this.props.time; let barStart = percentBarStart * this.overviewBarWidth; let timeline = this.props.isAuthoring ? [ diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 1ca8022a1..3ea34a2f3 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -66,7 +66,7 @@ export class Track extends React.Component { runInAction(() => { if (!this.props.node.regions) this.props.node.regions = new List(); //if there is no region, then create new doc to store stuff //these two lines are exactly same from timeline.tsx - let relativeHeight = window.innerHeight / 14; + let relativeHeight = window.innerHeight / 20; this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness }); } -- cgit v1.2.3-70-g09d2 From c12573ba5e45271493acf01ee7d63ce9387ac606 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 1 Feb 2020 16:23:51 -0500 Subject: lots of changes --- src/client/views/animationtimeline/Keyframe.tsx | 95 ++++++++-------- src/client/views/animationtimeline/Timeline.tsx | 4 - .../views/animationtimeline/TimelineOverview.tsx | 2 - src/client/views/animationtimeline/Track.tsx | 120 +++++++-------------- 4 files changed, 79 insertions(+), 142 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 9f4d80d2b..4f6ba728d 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -20,15 +20,18 @@ import { undoBatch, UndoManager } from "../../util/UndoManager"; * Useful static functions that you can use. Mostly for logic, but you can also add UI logic here also */ export namespace KeyframeFunc { + export enum KeyframeType { end = "end", fade = "fade", default = "default", } + export enum Direction { left = "left", right = "right" } + export const findAdjacentRegion = (dir: KeyframeFunc.Direction, currentRegion: Doc, regions: List): (RegionData | undefined) => { let leftMost: (RegionData | undefined) = undefined; let rightMost: (RegionData | undefined) = undefined; @@ -97,41 +100,6 @@ export namespace KeyframeFunc { return regiondata; }; - export const makeKeyData = async (regiondata:RegionData, time: number, badNode:Doc, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - runInAction(async () => { - console.log("ran"); - let doclist = (await DocListCastAsync(regiondata.keyframes))!; - let existingkf: (Doc | undefined) = undefined; - doclist.forEach(TK => { - if (TK.time === time) existingkf = TK; - }); - if (existingkf) return existingkf; - let TK: Doc = new Doc(); - TK.time = time; - TK.key = Doc.MakeCopy(badNode, true); - TK.type = type; - regiondata.keyframes!.push(TK); - let interpolationFunctions = new Doc(); - interpolationFunctions.interpolationX = new List([0, 1]); - interpolationFunctions.interpolationY = new List([0, 100]); - interpolationFunctions.pathX = new List(); - interpolationFunctions.pathY = new List(); - regiondata.functions!.push(interpolationFunctions); - let found: boolean = false; - regiondata.keyframes!.forEach(compkf => { - compkf = compkf as Doc; - if (time < NumCast(compkf.time) && !found) { - runInAction(() => { - regiondata.keyframes!.splice(doclist.indexOf(compkf as Doc), 0, TK); - regiondata.keyframes!.pop(); - found = true; - }); - return; - } - }); - return TK; - }); - }; export const convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time", tickSpacing: number, tickIncrement: number) => { let time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement; @@ -202,7 +170,6 @@ interface IProps { export class Keyframe extends React.Component { @observable private _bar = React.createRef(); - @observable private _gain = 20; //default @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; @@ -215,18 +182,15 @@ export class Keyframe extends React.Component { @computed private get pixelFadeOut() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } componentWillMount() { - runInAction(async () => { - if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); - let fadeIn = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, this.props.node, KeyframeFunc.KeyframeType.fade))! as any as Doc; - let fadeOut = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, this.props.node, KeyframeFunc.KeyframeType.fade))! as any as Doc; - let start = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position, this.props.node, KeyframeFunc.KeyframeType.end))! as any as Doc; - let finish = (await KeyframeFunc.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, this.props.node, KeyframeFunc.KeyframeType.end))! as any as Doc; - (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; - this.forceUpdate(); - }); + if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); + let start = this.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); + let fadeIn = this.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); + let fadeOut = this.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); + let finish = this.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); + (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; } @@ -253,7 +217,32 @@ export class Keyframe extends React.Component { }); } } - + private makeKeyData = (regiondata:RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time + let doclist = DocListCast(regiondata.keyframes)!; + let existingkf: (Doc | undefined) = undefined; + doclist.forEach(TK => { + if (TK.time === time) existingkf = TK; + }); + if (existingkf) return existingkf; + //else creates a new doc. + let TK: Doc = new Doc(); + TK.time = time; + TK.key = Doc.MakeCopy(this.props.node, true); + TK.type = type; + //assuming there are already keyframes (for keeping keyframes in order, sorted by time) + console.log("making..."); + if (doclist.length === 0) regiondata.keyframes!.push(TK); + doclist.forEach(kf => { + let index = doclist.indexOf(kf); + let kfTime = NumCast(kf.time); + console.log(kfTime); + if ((kfTime < time && index === doclist.length - 1) || (kfTime < time && time < NumCast(doclist[index + 1].time))){ + regiondata.keyframes!.splice(index + 1, 0, TK); + return; + } + }); + return TK; + } @action onBarPointerMove = (e: PointerEvent) => { @@ -353,7 +342,7 @@ export class Keyframe extends React.Component { let offset = KeyframeFunc.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends let position = this.regiondata.position; - await KeyframeFunc.makeKeyData(this.regiondata, Math.round(position + offset), this.props.node); + this.makeKeyData(this.regiondata, Math.round(position + offset)); this.regiondata.hasData = true; this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied @@ -552,12 +541,12 @@ export class Keyframe extends React.Component { DocListCast(this.regiondata.keyframes).forEach(kf => { let index = this.keyframes.indexOf(kf); if (index !== this.keyframes.length - 1) { - let left = this.keyframes[this.keyframes.indexOf(kf) + 1]; + let right = this.keyframes[index + 1]; let bodyRef = React.createRef(); let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - let leftPos = KeyframeFunc.convertPixelTime(NumCast(left!.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + let rightPos = KeyframeFunc.convertPixelTime(NumCast(right.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); keyframeDividers.push( -
    { e.preventDefault(); e.stopPropagation(); this.onContainerOver(e, bodyRef); }} onPointerOut={(e) => { e.preventDefault(); e.stopPropagation(); this.onContainerOut(e, bodyRef); }} onContextMenu={(e) => { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 8b943f209..604827213 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -449,9 +449,6 @@ export class Timeline extends React.Component { // let overviewString: string = "Overview:"; // let lengthString: string = "Length: "; - console.log("visible: " + this._visibleLength) - console.log("total: " + this._totalLength) - return (
    @@ -572,7 +569,6 @@ export class Timeline extends React.Component { runInAction(() => { this._panelWidth = this.props.PanelWidth(); this.changeLenths(); - console.log("changing!!") }); // change visible and total width diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index caa97bb70..553ba890d 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -127,10 +127,8 @@ export class TimelineOverview extends React.Component{ render() { - console.log("helo") // calculates where everything should fall based on its size let percentVisible = this.props.visibleLength / this.props.totalLength; - console.log(this.props.visibleLength) let visibleBarWidth = percentVisible * this.overviewBarWidth; let percentScrubberStart = this.props.currentBarX / this.props.totalLength; diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index e5e7a364c..17466bd1a 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -10,6 +10,7 @@ import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; import { Transform } from "../../util/Transform"; import { Copy } from "../../../new_fields/FieldSymbols"; import { ObjectField } from "../../../new_fields/ObjectField"; +import { fromCallback } from "bluebird"; interface IProps { node: Doc; @@ -40,10 +41,12 @@ export class Track extends React.Component { "y", "width", "height", - "data" + "data", + "opacity" ]; + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } - // @computed private get time() {} + @computed private get time() {return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement));} ////////// life cycle functions/////////////// componentWillMount() { @@ -59,7 +62,7 @@ export class Track extends React.Component { runInAction(async () => { this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); - if (this.regions.length === 0) this.createRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); + if (this.regions.length === 0) this.createRegion(this.time); this.props.node.hidden = false; this.props.node.opacity = 1; // this.autoCreateKeyframe(); @@ -90,17 +93,17 @@ export class Track extends React.Component { if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = Doc.MakeCopy(this.props.node, true); - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), kf); //right keyframe, if it exists + let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, this.time, kf); // lef keyframe, if it exists + let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, this.time, 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!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), leftkf!); + let edge: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, this.time, 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!, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement), rightkf!); + let edge: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, this.time, 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; @@ -124,14 +127,12 @@ export class Track extends React.Component { return this.whitelist.map(key => node[key]); }, (changed, reaction) => { console.log(changed); - //convert scrubber pos(pixel) to time - let time = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); //check for region - this.findRegion(time).then((region) => { + this.findRegion(this.time).then((region) => { if (region !== undefined){ //if region at scrub time exist let r = region as any as RegionData; //for some region is returning undefined... which is not the case - if (DocListCast(r.keyframes).find(kf => kf.time === time) === undefined ){ //basically when there is no additional keyframe at that timespot - KeyframeFunc.makeKeyData(r, time, this.props.node, KeyframeFunc.KeyframeType.default); + if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined ){ //basically when there is no additional keyframe at that timespot + KeyframeFunc.makeKeyData(r, this.time, this.props.node, KeyframeFunc.KeyframeType.default); } } // reaction.dispose(); @@ -158,10 +159,10 @@ export class Track extends React.Component { @action currentBarXReaction = () => { return reaction(() => this.props.currentBarX, async () => { - let regiondata: (Doc | undefined) = await this.findRegion(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); + let regiondata: (Doc | undefined) = await this.findRegion(this.time); if (regiondata) { this.props.node.hidden = false; - await this.timeChange(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); + await this.timeChange(); } else { this.props.node.hidden = true; this.props.node.opacity = 0; @@ -198,14 +199,14 @@ export class Track extends React.Component { * when scrubber position changes. Need to edit the logic */ @action - timeChange = async (time: number) => { + timeChange = async () => { if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { await this.saveKeyframe(this._onKeyframe, this._onRegionData); } - let regiondata = await this.findRegion(Math.round(time)); //finds a region that the scrubber is on + let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); //right keyframe, if it exists + let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists + let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //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); @@ -213,7 +214,7 @@ export class Track extends React.Component { this._onKeyframe = currentkf; this._onRegionData = regiondata; } else if (leftkf && rightkf) { - await this.interpolate(leftkf, rightkf, regiondata); + await this.interpolate(leftkf, rightkf); } } } @@ -225,17 +226,12 @@ export class Track extends React.Component { @action private applyKeys = async (kf: Doc) => { let kfNode = await Cast(kf.key, Doc) as Doc; - let docFromApply = kfNode; this.whitelist.forEach(key => { if (!kfNode[key]) { this.props.node[key] = undefined; } else { let stored = kfNode[key]; - if (stored instanceof ObjectField) { - this.props.node[key] = stored[Copy](); - } else { - this.props.node[key] = stored; - } + this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); } @@ -245,77 +241,35 @@ export class Track extends React.Component { * calculating current keyframe, if the scrubber is right on the keyframe */ @action - calcCurrent = async (region: Doc) => { + calcCurrent = (region: Doc) => { let currentkf: (Doc | undefined) = undefined; - let keyframes = await DocListCastAsync(region.keyframes!); - keyframes!.forEach((kf) => { - if (NumCast(kf.time) === Math.round(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement))) currentkf = kf; + let keyframes = DocListCast(region.keyframes!); + keyframes.forEach((kf) => { + if (NumCast(kf.time) === Math.round(this.time)) currentkf = kf; }); return currentkf; } /** - * interpolation. definetely needs to be changed. (currently involves custom linear splicing interpolations). - * Too complex right now. Also need to apply quadratic spline later on (for smoothness, instead applying "gains") + * basic linear interpolation function */ @action - interpolate = async (left: Doc, right: Doc, regiondata: Doc) => { - let leftNode = left.key as Doc; - let rightNode = right.key as Doc; - // const dif_time = NumCast(right.time) - NumCast(left.time); - // const timeratio = (KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement) - NumCast(left.time)) / dif_time; //linear - // let keyframes = (await DocListCastAsync(regiondata.keyframes!))!; - // let indexLeft = keyframes.indexOf(left); - // let interY: List = (await ((regiondata.functions as List)[indexLeft] as Doc).interpolationY as List)!; - // 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 = await ((regiondata.functions as List)[indexLeft] as Doc).pathX as List; - // let pathY: List = await ((regiondata.functions as List)[indexLeft] as Doc).pathY as List; - // 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]); - // } - - // } + interpolate = async (left: Doc, right: Doc) => { + let leftNode = await(left.key) as Doc; + let rightNode = await(right.key) as Doc; this.whitelist.forEach(key => { + console.log(key); + console.log(leftNode[key]); + console.log(rightNode[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; - // } else { - // const diff = NumCast(rightNode[key]) - NumCast(leftNode[key]); - // const adjusted = diff * finalRatio; - // this.props.node[key] = NumCast(leftNode[key]) + adjusted; - // } let dif = NumCast(rightNode[key]) - NumCast(leftNode[key]); - - } else { + let deltaLeft = this.time - NumCast(left.time); + let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); + this.props.node[key] = NumCast(leftNode[key]) + (dif * ratio); + } else { // case data let stored = leftNode[key]; - if (stored instanceof ObjectField) { - this.props.node[key] = stored[Copy](); - } else { - this.props.node[key] = stored; - } + this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); } -- cgit v1.2.3-70-g09d2 From ad15ebb5f1572702894ba96caf9a66be278e1e81 Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Sun, 2 Feb 2020 00:08:59 -0500 Subject: kinda unstable (will fix it in the morning) --- src/client/views/animationtimeline/Keyframe.tsx | 37 +----- src/client/views/animationtimeline/Track.tsx | 164 +++++++++++++++--------- 2 files changed, 106 insertions(+), 95 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 4f6ba728d..d53000460 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -140,6 +140,7 @@ interface IProps { check: string; changeCurrentBarX: (x: number) => void; transform: Transform; + makeKeyData: (region:RegionData, pos: number, kftype:KeyframeFunc.KeyframeType) => Doc; } @@ -183,10 +184,10 @@ export class Keyframe extends React.Component { componentWillMount() { if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); - let start = this.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); - let fadeIn = this.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); - let fadeOut = this.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); - let finish = this.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); + let start = this.props.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); + let fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); + let fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); + let finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); (fadeIn.key as Doc).opacity = 1; (fadeOut.key as Doc).opacity = 1; (start.key as Doc).opacity = 0.1; @@ -217,32 +218,6 @@ export class Keyframe extends React.Component { }); } } - private makeKeyData = (regiondata:RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - let doclist = DocListCast(regiondata.keyframes)!; - let existingkf: (Doc | undefined) = undefined; - doclist.forEach(TK => { - if (TK.time === time) existingkf = TK; - }); - if (existingkf) return existingkf; - //else creates a new doc. - let TK: Doc = new Doc(); - TK.time = time; - TK.key = Doc.MakeCopy(this.props.node, true); - TK.type = type; - //assuming there are already keyframes (for keeping keyframes in order, sorted by time) - console.log("making..."); - if (doclist.length === 0) regiondata.keyframes!.push(TK); - doclist.forEach(kf => { - let index = doclist.indexOf(kf); - let kfTime = NumCast(kf.time); - console.log(kfTime); - if ((kfTime < time && index === doclist.length - 1) || (kfTime < time && time < NumCast(doclist[index + 1].time))){ - regiondata.keyframes!.splice(index + 1, 0, TK); - return; - } - }); - return TK; - } @action onBarPointerMove = (e: PointerEvent) => { @@ -342,7 +317,7 @@ export class Keyframe extends React.Component { let offset = KeyframeFunc.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends let position = this.regiondata.position; - this.makeKeyData(this.regiondata, Math.round(position + offset)); + this.props.makeKeyData(this.regiondata, Math.round(position + offset), KeyframeFunc.KeyframeType.default); this.regiondata.hasData = true; this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 17466bd1a..40350bc7a 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -60,12 +60,12 @@ export class Track extends React.Component { componentDidMount() { runInAction(async () => { - this._timelineVisibleReaction = this.timelineVisibleReaction(); + // this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); if (this.regions.length === 0) this.createRegion(this.time); this.props.node.hidden = false; this.props.node.opacity = 1; - // this.autoCreateKeyframe(); + //this.autoCreateKeyframe(); }); } @@ -86,34 +86,34 @@ export class Track extends React.Component { * */ @action - saveKeyframe = async (ref: Doc, regiondata: Doc) => { - let keyframes: List = (Cast(regiondata.keyframes, listSpec(Doc)) as List); - let kfIndex: number = keyframes.indexOf(ref); - let kf = keyframes[kfIndex] as Doc; + saveKeyframe = async () => { + console.log("saving keyframe"); + let keyframes: List = (Cast(this.saveStateRegion!.keyframes, listSpec(Doc)) as List); + let kfIndex: number = keyframes.indexOf(this.saveStateKf!); + let kf = keyframes[kfIndex] as Doc; //index in the keyframe if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades - kf.key = Doc.MakeCopy(this.props.node, true); - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata!, this.time, kf); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata!, this.time, kf); //right keyframe, if it exists + kf.key = this.makeCopy(); + let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists + let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, 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.time, leftkf!); - edge!.key = Doc.MakeCopy(kf.key as Doc, true); - leftkf!.key = Doc.MakeCopy(kf.key as Doc, true); + let edge: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf!); + edge!.key = this.makeCopy(); + leftkf!.key = this.makeCopy(); (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.time, rightkf!); - edge!.key = Doc.MakeCopy(kf.key as Doc, true); - rightkf!.key = Doc.MakeCopy(kf.key as Doc, true); + let edge: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf!); + edge!.key = this.makeCopy(); + rightkf!.key = this.makeCopy(); (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; (Cast(rightkf!.key, Doc)! as Doc).opacity = 1; } } keyframes[kfIndex] = kf; - this._onKeyframe = undefined; - this._onRegionData = undefined; - this._isOnKeyframe = false; + this.saveStateKf = undefined; + this.saveStateRegion = undefined; } @@ -126,13 +126,13 @@ export class Track extends React.Component { return reaction(() => { return this.whitelist.map(key => node[key]); }, (changed, reaction) => { - console.log(changed); + console.log("autocreated"); //check for region this.findRegion(this.time).then((region) => { if (region !== undefined){ //if region at scrub time exist let r = region as any as RegionData; //for some region is returning undefined... which is not the case if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined ){ //basically when there is no additional keyframe at that timespot - KeyframeFunc.makeKeyData(r, this.time, this.props.node, KeyframeFunc.KeyframeType.default); + this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); } } // reaction.dispose(); @@ -140,17 +140,17 @@ export class Track extends React.Component { }); } - /** - * reverting back to previous state before editing on AT - */ - @action - revertState = () => { - let copyDoc = Doc.MakeCopy(this.props.node, true); - if (this._storedState) this.applyKeys(this._storedState); - let newState = new Doc(); - newState.key = copyDoc; - this._storedState = newState; - } + // /** + // * reverting back to previous state before editing on AT + // */ + // @action + // revertState = () => { + // let copyDoc = Doc.MakeCopy(this.props.node, true); + // if (this._storedState) this.applyKeys(this._storedState); + // let newState = new Doc(); + // newState.key = copyDoc; + // this._storedState = newState; + // } /** * Reaction when scrubber bar changes @@ -170,38 +170,41 @@ export class Track extends React.Component { }); } - /** - * when timeline is visible, reaction is ran so states are reverted - */ - @action - timelineVisibleReaction = () => { - return reaction(() => { - return this.props.timelineVisible; - }, isVisible => { - this.revertState(); - if (isVisible) { - DocListCast(this.regions).forEach(region => { - if (!BoolCast((Cast(region, Doc) as Doc).hasData)) { - for (let i = 0; i < 4; i++) { - DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = Doc.MakeCopy(this.props.node, true); - if (i === 0 || i === 3) { - DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key.opacity = 0.1; - } - } - console.log("saving keyframes"); - } - }); - } - }); - } + // /** + // * when timeline is visible, reaction is ran so states are reverted + // */ + // @action + // timelineVisibleReaction = () => { + // return reaction(() => { + // return this.props.timelineVisible; + // }, isVisible => { + // this.revertState(); + // if (isVisible) { + // DocListCast(this.regions).forEach(region => { + // if (!BoolCast((Cast(region, Doc) as Doc).hasData)) { + // for (let i = 0; i < 4; i++) { + // DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = Doc.MakeCopy(this.props.node, true); + // if (i === 0 || i === 3) { + // DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key.opacity = 0.1; + // } + // } + // console.log("saving keyframes"); + // } + // }); + // } + // }); + // } + + @observable private saveStateKf:(Doc | undefined) = undefined; + @observable private saveStateRegion: (Doc|undefined) = undefined; /**w * when scrubber position changes. Need to edit the logic */ @action timeChange = async () => { - if (this._isOnKeyframe && this._onKeyframe && this._onRegionData) { - await this.saveKeyframe(this._onKeyframe, this._onRegionData); + if (this.saveStateKf !== undefined) { + await this.saveKeyframe(); } let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { @@ -210,9 +213,8 @@ export class Track extends React.Component { let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { await this.applyKeys(currentkf); - this._isOnKeyframe = true; - this._onKeyframe = currentkf; - this._onRegionData = regiondata; + this.saveStateKf = currentkf; + this.saveStateRegion = regiondata; } else if (leftkf && rightkf) { await this.interpolate(leftkf, rightkf); } @@ -259,9 +261,6 @@ export class Track extends React.Component { let leftNode = await(left.key) as Doc; let rightNode = await(right.key) as Doc; this.whitelist.forEach(key => { - console.log(key); - console.log(leftNode[key]); - console.log(rightNode[key]); if (leftNode[key] && rightNode[key] && typeof (leftNode[key]) === "number" && typeof (rightNode[key]) === "number") { //if it is number, interpolate let dif = NumCast(rightNode[key]) - NumCast(leftNode[key]); let deltaLeft = this.time - NumCast(left.time); @@ -307,6 +306,7 @@ export class Track extends React.Component { /** * creates a region (KEYFRAME.TSX stuff). */ + @action createRegion = async (time: number) => { if (await this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data @@ -323,6 +323,42 @@ export class Track extends React.Component { } } + @action + makeKeyData = (regiondata:RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time + console.log("KEYDATA GENERATING"); + let doclist = DocListCast(regiondata.keyframes)!; + let existingkf: (Doc | undefined) = undefined; + doclist.forEach(TK => { + if (TK.time === time) existingkf = TK; + }); + if (existingkf) return existingkf; + //else creates a new doc. + let TK: Doc = new Doc(); + TK.time = time; + TK.key = this.makeCopy(); + TK.type = type; + //assuming there are already keyframes (for keeping keyframes in order, sorted by time) + if (doclist.length === 0) regiondata.keyframes!.push(TK); + doclist.forEach(kf => { + let index = doclist.indexOf(kf); + let kfTime = NumCast(kf.time); + if ((kfTime < time && index === doclist.length - 1) || (kfTime < time && time < NumCast(doclist[index + 1].time))){ + regiondata.keyframes!.splice(index + 1, 0, TK); + return; + } + }); + return TK; + } + + @action + makeCopy = () => { + let doc = new Doc(); + this.whitelist.forEach(key => { + let originalVal = this.props.node[key]; + doc.key = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; + }); + return doc; + } /** * UI sstuff here. Not really much to change @@ -333,7 +369,7 @@ export class Track extends React.Component {
    { Doc.BrushDoc(this.props.node); }} onPointerOut={() => { Doc.UnBrushDoc(this.props.node); }} style={{ height: `${this._trackHeight}px` }}> {DocListCast(this.regions).map((region) => { - return ; + return ; })}
    -- cgit v1.2.3-70-g09d2 From 0688541300ad0fa5cda7543d5bdc2e2c56246fbc Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Sun, 2 Feb 2020 10:40:45 -0500 Subject: cleanup (unstable) --- src/client/views/animationtimeline/Keyframe.tsx | 1 - src/client/views/animationtimeline/Timeline.tsx | 48 +------------------------ src/client/views/animationtimeline/Track.tsx | 5 --- 3 files changed, 1 insertion(+), 53 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index d53000460..482d066ba 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -137,7 +137,6 @@ interface IProps { tickSpacing: number; tickIncrement: number; time: number; - check: string; changeCurrentBarX: (x: number) => void; transform: Transform; makeKeyData: (region:RegionData, pos: number, kftype:KeyframeFunc.KeyframeType) => Doc; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 604827213..da9c361da 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -79,7 +79,6 @@ export class Timeline extends React.Component { // so a reaction can be made @observable public _isAuthoring = this.props.Document.isATOn; - @observable private _resizeReaction?: IReactionDisposer; @observable private _panelWidth = 0; /** @@ -119,19 +118,6 @@ export class Timeline extends React.Component { this.props.Document.isATOn = !this.props.Document.isATOn; //turns the boolean on, saying AT (animation timeline) is on this.toggleHandle(); }); - - this._resizeReaction = reaction( - () => this.props.PanelWidth, - () => { - // if (!this.props.parent._isAuthoring) { - // runInAction(() => { - console.log("resizing"); - // this.setOverviewWidth(); - // }); - // } - }, - ); - } componentWillUnmount() { @@ -527,30 +513,6 @@ export class Timeline extends React.Component { } - @observable private _check: string = ""; - @observable private _checkVisible: boolean = false; - @action - private onCheckClicked = (type: string) => { - if (type === "yes") { - this._check = "yes"; - } else if (type === "no") { - this._check = "no"; - } - } - - - /** - * check mark thing that needs to be fixed. Do not edit this, because it most likely change. - */ - @action - private checkCallBack = (visible: boolean) => { - this._checkVisible = visible; - if (!visible) { //when user confirms - this._check = ""; - } - - } - @action.bound changeLenths() { if (this._infoContainer.current) { @@ -583,7 +545,7 @@ export class Timeline extends React.Component {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => )}
    @@ -593,14 +555,6 @@ export class Timeline extends React.Component {
    -
    -
    { this.onCheckClicked("yes"); }}> - -
    -
    { this.onCheckClicked("no"); }}> - -
    -
    {this.timelineToolBox(1)}
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 40350bc7a..d260792e1 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -21,7 +21,6 @@ interface IProps { tickIncrement: number; tickSpacing: number; timelineVisible: boolean; - check: string; changeCurrentBarX: (x: number) => void; } @@ -30,10 +29,6 @@ export class Track extends React.Component { @observable private _inner = React.createRef(); @observable private _currentBarXReaction: any; @observable private _timelineVisibleReaction: any; - @observable private _isOnKeyframe: boolean = false; - @observable private _onKeyframe: (Doc | undefined) = undefined; - @observable private _onRegionData: (Doc | undefined) = undefined; - @observable private _storedState: (Doc | undefined) = undefined; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; private whitelist = [ -- cgit v1.2.3-70-g09d2 From 7284810ee039c8baa456d0579fefba59eef444ca Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Sun, 2 Feb 2020 13:18:39 -0500 Subject: stable --- src/client/views/animationtimeline/Keyframe.tsx | 31 ++++++++++++------------ src/client/views/animationtimeline/Track.tsx | 32 +++++++++++++------------ 2 files changed, 32 insertions(+), 31 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 482d066ba..393168ac3 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -60,9 +60,7 @@ export namespace KeyframeFunc { let keyframes = await DocListCastAsync(region.keyframes!); keyframes!.forEach((kf) => { let compTime = currentBarX; - if (ref) { - compTime = NumCast(ref.time); - } + if (ref) compTime = NumCast(ref.time); if (NumCast(kf.time) < compTime && NumCast(kf.time) >= time) { leftKf = kf; time = NumCast(kf.time); @@ -78,9 +76,7 @@ export namespace KeyframeFunc { let keyframes = await DocListCastAsync(region.keyframes!); keyframes!.forEach((kf) => { let compTime = currentBarX; - if (ref) { - compTime = NumCast(ref.time); - } + if (ref) compTime = NumCast(ref.time); if (NumCast(kf.time) > compTime && NumCast(kf.time) <= NumCast(time)) { rightKf = kf; time = NumCast(kf.time); @@ -181,16 +177,19 @@ export class Keyframe extends React.Component { @computed private get pixelFadeIn() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeOut() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } - componentWillMount() { - if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); - let start = this.props.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); - let fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); - let fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); - let finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); - (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; + componentDidMount() { + setTimeout(() => { //giving it a temporary 1sec delay... + if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); + let start = this.props.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); + let fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); + let fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); + let finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); + (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; + this.forceUpdate(); //not needed, if setTimeout is gone... + }, 1000); } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index d260792e1..a20769142 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -10,7 +10,6 @@ import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; import { Transform } from "../../util/Transform"; import { Copy } from "../../../new_fields/FieldSymbols"; import { ObjectField } from "../../../new_fields/ObjectField"; -import { fromCallback } from "bluebird"; interface IProps { node: Doc; @@ -153,15 +152,16 @@ export class Track extends React.Component { */ @action currentBarXReaction = () => { - return reaction(() => this.props.currentBarX, async () => { - let regiondata: (Doc | undefined) = await this.findRegion(this.time); - if (regiondata) { - this.props.node.hidden = false; - await this.timeChange(); - } else { - this.props.node.hidden = true; - this.props.node.opacity = 0; - } + return reaction(() => this.props.currentBarX, () => { + this.findRegion(this.time).then((regiondata: (Doc | undefined)) => { + if (regiondata) { + this.props.node.hidden = false; + this.timeChange(); + } else { + this.props.node.hidden = true; + this.props.node.opacity = 0; + } + }); }); } @@ -207,10 +207,12 @@ export class Track extends React.Component { let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { + console.log("on a keyframe"); await this.applyKeys(currentkf); this.saveStateKf = currentkf; this.saveStateRegion = regiondata; } else if (leftkf && rightkf) { + console.log("interpolating!"); await this.interpolate(leftkf, rightkf); } } @@ -228,6 +230,8 @@ export class Track extends React.Component { this.props.node[key] = undefined; } else { let stored = kfNode[key]; + console.log(key); + console.log(stored); this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); @@ -287,14 +291,13 @@ export class Track extends React.Component { /** - * double click on track. Signalling keyframe creation. Problem with phantom regions + * double click on track. Signalling keyframe creation. */ @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(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); - this.forceUpdate(); } @@ -320,7 +323,6 @@ export class Track extends React.Component { @action makeKeyData = (regiondata:RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - console.log("KEYDATA GENERATING"); let doclist = DocListCast(regiondata.keyframes)!; let existingkf: (Doc | undefined) = undefined; doclist.forEach(TK => { @@ -349,8 +351,8 @@ export class Track extends React.Component { makeCopy = () => { let doc = new Doc(); this.whitelist.forEach(key => { - let originalVal = this.props.node[key]; - doc.key = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; + let originalVal = this.props.node[key]; + doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; }); return doc; } -- cgit v1.2.3-70-g09d2 From d5cda7cf9a67d0e47908dd510fbc8d43a3920f15 Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Sun, 2 Feb 2020 13:45:01 -0500 Subject: autokeyframing problem --- src/client/views/animationtimeline/Track.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index a20769142..24a5d2b6c 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -28,6 +28,7 @@ export class Track extends React.Component { @observable private _inner = React.createRef(); @observable private _currentBarXReaction: any; @observable private _timelineVisibleReaction: any; + @observable private _autoKfReaction: any; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; private whitelist = [ @@ -59,7 +60,7 @@ export class Track extends React.Component { if (this.regions.length === 0) this.createRegion(this.time); this.props.node.hidden = false; this.props.node.opacity = 1; - //this.autoCreateKeyframe(); + this.autoCreateKeyframe(); }); } @@ -71,6 +72,7 @@ export class Track extends React.Component { //disposing reactions if (this._currentBarXReaction) this._currentBarXReaction(); if (this._timelineVisibleReaction) this._timelineVisibleReaction(); + //if (this._autoKfReaction) this._autoKfReaction(); }); } //////////////////////////////// @@ -121,6 +123,7 @@ export class Track extends React.Component { return this.whitelist.map(key => node[key]); }, (changed, reaction) => { console.log("autocreated"); + console.log(changed); //check for region this.findRegion(this.time).then((region) => { if (region !== undefined){ //if region at scrub time exist @@ -129,9 +132,8 @@ export class Track extends React.Component { this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); } } - // reaction.dispose(); }); - }); + }, {fireImmediately: false}); } // /** @@ -156,10 +158,15 @@ export class Track extends React.Component { this.findRegion(this.time).then((regiondata: (Doc | undefined)) => { if (regiondata) { this.props.node.hidden = false; + if (!this._autoKfReaction){ + // console.log("creating another reaction"); + // this._autoKfReaction = this.autoCreateKeyframe(); + } this.timeChange(); } else { this.props.node.hidden = true; this.props.node.opacity = 0; + //if (this._autoKfReaction) this._autoKfReaction(); } }); }); @@ -207,12 +214,10 @@ export class Track extends React.Component { let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { - console.log("on a keyframe"); await this.applyKeys(currentkf); this.saveStateKf = currentkf; this.saveStateRegion = regiondata; } else if (leftkf && rightkf) { - console.log("interpolating!"); await this.interpolate(leftkf, rightkf); } } @@ -229,9 +234,7 @@ export class Track extends React.Component { if (!kfNode[key]) { this.props.node[key] = undefined; } else { - let stored = kfNode[key]; - console.log(key); - console.log(stored); + let stored = kfNode[key]; this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); -- cgit v1.2.3-70-g09d2 From 4fd68c0ec41f2871ae24a87d8e5316d093bdb505 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sun, 2 Feb 2020 15:10:34 -0500 Subject: changes --- src/client/views/animationtimeline/Keyframe.scss | 15 +++++++++++ src/client/views/animationtimeline/Keyframe.tsx | 8 +++++- src/client/views/animationtimeline/Timeline.scss | 32 +++++++++++++++--------- src/client/views/animationtimeline/Timeline.tsx | 2 +- src/client/views/animationtimeline/Track.scss | 17 +++++++------ 5 files changed, 52 insertions(+), 22 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.scss b/src/client/views/animationtimeline/Keyframe.scss index 8ff1c53f5..f0685943b 100644 --- a/src/client/views/animationtimeline/Keyframe.scss +++ b/src/client/views/animationtimeline/Keyframe.scss @@ -46,6 +46,21 @@ $timelineDark: #77a1aa; position: absolute; } + .keyframe:hover~.keyframe-information { + display: flex; + } + + .keyframe-information { + display: none; + position: relative; + // z-index: 100000; + // background: $timelineDark; + width: 100px; + // left: -50px; + height: 100px; + // top: 40px; + } + .keyframeCircle { left: -10px; border: 3px solid $timelineDark; diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 9c486a6d6..2348da813 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -522,14 +522,18 @@ export class Keyframe extends React.Component { DocListCast(this.regiondata.keyframes).forEach(kf => { if (kf.type as KeyframeFunc.KeyframeType !== KeyframeFunc.KeyframeType.end) { keyframeDivs.push( -
    + <>
    { e.preventDefault(); e.stopPropagation(); this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); this.makeKeyframeMenu(kf, e.nativeEvent); }} onDoubleClick={(e) => { e.preventDefault(); e.stopPropagation(); }}>
    +
    +
    + + ); } else { keyframeDivs.push( @@ -589,7 +593,9 @@ export class Keyframe extends React.Component { onPointerDown={this.onBarPointerDown }>
    + {/*
    */}
    + {/*
    */} {this.drawKeyframes()} {this.drawKeyframeDividers()}
    diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 40479559d..dbdade03f 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -206,18 +206,26 @@ $timelineDark: #77a1aa; border-right: 2px solid $timelineDark; .datapane { - top: 0px; - width: 100px; - height: 30%; - border: 1px solid $dark-color; - background-color: $intermediate-color; - color: white; - position: relative; - float: left; - border-style: solid; - overflow-y: scroll; - overflow-x: hidden; -} + top: 0px; + width: 100px; + height: 30%; + border: 1px solid $dark-color; + font-size: 12px; + line-height: 11px; + background-color: $timelineDark; + color: white; + position: relative; + float: left; + padding: 3px; + border-style: solid; + overflow-y: scroll; + overflow-x: hidden; + + p { + hyphens: auto; + } + + } } .resize { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 5ff721ebb..f0ca1c0ab 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -100,7 +100,7 @@ export class Timeline extends React.Component { /////////lifecycle functions//////////// componentWillMount() { - let relativeHeight = window.innerHeight / 14; //sets height to arbitrary size, relative to innerHeight + let relativeHeight = window.innerHeight / 20; //sets height to arbitrary size, relative to innerHeight this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset diff --git a/src/client/views/animationtimeline/Track.scss b/src/client/views/animationtimeline/Track.scss index 61a8e0b88..aec587a79 100644 --- a/src/client/views/animationtimeline/Track.scss +++ b/src/client/views/animationtimeline/Track.scss @@ -1,14 +1,15 @@ -@import "./../globalCssVariables.scss"; +@import "./../globalCssVariables.scss"; -.track-container{ +.track-container { .track { - .inner { - top:0px; - width: calc(100%); - background-color: $light-color; - border: 1px solid $dark-color; - position:relative; + .inner { + top: 0px; + width: calc(100%); + background-color: $light-color; + border: 1px solid $dark-color; + position: relative; + z-index: 100; } } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 84d5d8808bc0a51ae7a45f6415d9bd926c78bf6c Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 2 Feb 2020 15:56:16 -0500 Subject: changes --- src/client/views/animationtimeline/Track.tsx | 105 ++++++++++++++------------- 1 file changed, 56 insertions(+), 49 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 24a5d2b6c..e352cdbb5 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun, toJS, isObservableArray, IObservableArray, trace } from "mobx"; +import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun, toJS, isObservableArray, IObservableArray, trace, observe, intercept } from "mobx"; import "./Track.scss"; import { Doc, DocListCastAsync, DocListCast, Field } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; @@ -28,20 +28,22 @@ export class Track extends React.Component { @observable private _inner = React.createRef(); @observable private _currentBarXReaction: any; @observable private _timelineVisibleReaction: any; - @observable private _autoKfReaction: any; + @observable private _autoKfReaction: any; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; - private whitelist = [ + private primitiveWhitelist = [ "x", "y", "width", "height", - "data", "opacity" ]; + private objectWhitelist = [ + "data" + ] @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } - @computed private get time() {return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement));} + @computed private get time() { return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } ////////// life cycle functions/////////////// componentWillMount() { @@ -55,7 +57,7 @@ export class Track extends React.Component { componentDidMount() { runInAction(async () => { - // this._timelineVisibleReaction = this.timelineVisibleReaction(); + // this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); if (this.regions.length === 0) this.createRegion(this.time); this.props.node.hidden = false; @@ -72,7 +74,7 @@ export class Track extends React.Component { //disposing reactions if (this._currentBarXReaction) this._currentBarXReaction(); if (this._timelineVisibleReaction) this._timelineVisibleReaction(); - //if (this._autoKfReaction) this._autoKfReaction(); + if (this._autoKfReaction) this._autoKfReaction(); }); } //////////////////////////////// @@ -89,12 +91,12 @@ export class Track extends React.Component { let kf = keyframes[kfIndex] as Doc; //index in the keyframe if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades - kf.key = this.makeCopy(); + kf.key = this.makeCopy(); let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades let edge: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf!); - edge!.key = this.makeCopy(); + edge!.key = this.makeCopy(); leftkf!.key = this.makeCopy(); (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; (Cast(leftkf!.key, Doc)! as Doc).opacity = 1; @@ -102,13 +104,13 @@ export class Track extends React.Component { if (rightkf!.type === KeyframeFunc.KeyframeType.fade) { let edge: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf!); edge!.key = this.makeCopy(); - rightkf!.key = this.makeCopy(); + rightkf!.key = this.makeCopy(); (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; (Cast(rightkf!.key, Doc)! as Doc).opacity = 1; } } keyframes[kfIndex] = kf; - this.saveStateKf = undefined; + this.saveStateKf = undefined; this.saveStateRegion = undefined; } @@ -119,21 +121,26 @@ export class Track extends React.Component { @action autoCreateKeyframe = () => { const { node } = this.props; + const objects = this.objectWhitelist.map(key => node[key]); + intercept(this.props.node, change => { + console.log(change); + return change; + }); return reaction(() => { - return this.whitelist.map(key => node[key]); + return [...this.primitiveWhitelist.map(key => node[key]), ...objects]; }, (changed, reaction) => { - console.log("autocreated"); + console.log("autocreated"); console.log(changed); //check for region this.findRegion(this.time).then((region) => { - if (region !== undefined){ //if region at scrub time exist + if (region !== undefined) { //if region at scrub time exist let r = region as any as RegionData; //for some region is returning undefined... which is not the case - if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined ){ //basically when there is no additional keyframe at that timespot - this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); - } + if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot + this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); + } } }); - }, {fireImmediately: false}); + }, { fireImmediately: false }); } // /** @@ -158,10 +165,10 @@ export class Track extends React.Component { this.findRegion(this.time).then((regiondata: (Doc | undefined)) => { if (regiondata) { this.props.node.hidden = false; - if (!this._autoKfReaction){ - // console.log("creating another reaction"); - // this._autoKfReaction = this.autoCreateKeyframe(); - } + if (!this._autoKfReaction) { + // console.log("creating another reaction"); + // this._autoKfReaction = this.autoCreateKeyframe(); + } this.timeChange(); } else { this.props.node.hidden = true; @@ -197,8 +204,8 @@ export class Track extends React.Component { // }); // } - @observable private saveStateKf:(Doc | undefined) = undefined; - @observable private saveStateRegion: (Doc|undefined) = undefined; + @observable private saveStateKf: (Doc | undefined) = undefined; + @observable private saveStateRegion: (Doc | undefined) = undefined; /**w * when scrubber position changes. Need to edit the logic @@ -215,8 +222,8 @@ export class Track extends React.Component { let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { await this.applyKeys(currentkf); - this.saveStateKf = currentkf; - this.saveStateRegion = regiondata; + this.saveStateKf = currentkf; + this.saveStateRegion = regiondata; } else if (leftkf && rightkf) { await this.interpolate(leftkf, rightkf); } @@ -230,12 +237,12 @@ export class Track extends React.Component { @action private applyKeys = async (kf: Doc) => { let kfNode = await Cast(kf.key, Doc) as Doc; - this.whitelist.forEach(key => { + this.primitiveWhitelist.forEach(key => { if (!kfNode[key]) { this.props.node[key] = undefined; } else { - let stored = kfNode[key]; - this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; + let stored = kfNode[key]; + this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); } @@ -260,17 +267,17 @@ export class Track extends React.Component { */ @action interpolate = async (left: Doc, right: Doc) => { - let leftNode = await(left.key) as Doc; - let rightNode = await(right.key) as Doc; - this.whitelist.forEach(key => { + let leftNode = await (left.key) as Doc; + let rightNode = await (right.key) as Doc; + this.primitiveWhitelist.forEach(key => { if (leftNode[key] && rightNode[key] && typeof (leftNode[key]) === "number" && typeof (rightNode[key]) === "number") { //if it is number, interpolate - let dif = NumCast(rightNode[key]) - NumCast(leftNode[key]); + let dif = NumCast(rightNode[key]) - NumCast(leftNode[key]); let deltaLeft = this.time - NumCast(left.time); - let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); - this.props.node[key] = NumCast(leftNode[key]) + (dif * ratio); + let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); + this.props.node[key] = NumCast(leftNode[key]) + (dif * ratio); } else { // case data let stored = leftNode[key]; - this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; + this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); } @@ -325,8 +332,8 @@ export class Track extends React.Component { } @action - makeKeyData = (regiondata:RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - let doclist = DocListCast(regiondata.keyframes)!; + makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time + let doclist = DocListCast(regiondata.keyframes)!; let existingkf: (Doc | undefined) = undefined; doclist.forEach(TK => { if (TK.time === time) existingkf = TK; @@ -338,26 +345,26 @@ export class Track extends React.Component { TK.key = this.makeCopy(); TK.type = type; //assuming there are already keyframes (for keeping keyframes in order, sorted by time) - if (doclist.length === 0) regiondata.keyframes!.push(TK); + if (doclist.length === 0) regiondata.keyframes!.push(TK); doclist.forEach(kf => { - let index = doclist.indexOf(kf); + let index = doclist.indexOf(kf); let kfTime = NumCast(kf.time); - if ((kfTime < time && index === doclist.length - 1) || (kfTime < time && time < NumCast(doclist[index + 1].time))){ - regiondata.keyframes!.splice(index + 1, 0, TK); - return; + if ((kfTime < time && index === doclist.length - 1) || (kfTime < time && time < NumCast(doclist[index + 1].time))) { + regiondata.keyframes!.splice(index + 1, 0, TK); + return; } }); - return TK; + return TK; } - @action + @action makeCopy = () => { - let doc = new Doc(); - this.whitelist.forEach(key => { + let doc = new Doc(); + this.primitiveWhitelist.forEach(key => { let originalVal = this.props.node[key]; - doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; + doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; }); - return doc; + return doc; } /** -- cgit v1.2.3-70-g09d2 From 6b8060dd969b314728a86fcd2fefc0e667ba079b Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Wed, 5 Feb 2020 00:22:54 -0500 Subject: everything works except data tracking --- src/client/views/animationtimeline/Track.tsx | 76 ++++++++++++++++------------ 1 file changed, 45 insertions(+), 31 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index e352cdbb5..32fa1d9ca 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -29,6 +29,7 @@ export class Track extends React.Component { @observable private _currentBarXReaction: any; @observable private _timelineVisibleReaction: any; @observable private _autoKfReaction: any; + @observable private _newKeyframe: boolean = false; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; private primitiveWhitelist = [ @@ -36,11 +37,12 @@ export class Track extends React.Component { "y", "width", "height", - "opacity" + "opacity", + "data", ]; private objectWhitelist = [ "data" - ] + ]; @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } @computed private get time() { return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } @@ -57,12 +59,12 @@ export class Track extends React.Component { componentDidMount() { runInAction(async () => { - // this._timelineVisibleReaction = this.timelineVisibleReaction(); + this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); if (this.regions.length === 0) this.createRegion(this.time); this.props.node.hidden = false; this.props.node.opacity = 1; - this.autoCreateKeyframe(); + // this.autoCreateKeyframe(); }); } @@ -108,10 +110,21 @@ export class Track extends React.Component { (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; (Cast(rightkf!.key, Doc)! as Doc).opacity = 1; } + } else if (this._newKeyframe) { + // console.log("new keyframe registering"); + // let kfList = DocListCast(this.saveStateRegion!.keyframes); + // kfList.forEach(kf => { + // kf.key = this.makeCopy(); + // if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3){ + // (kf.key as Doc).opacity = 0.1; + // } + // }); + } keyframes[kfIndex] = kf; this.saveStateKf = undefined; this.saveStateRegion = undefined; + this._newKeyframe = false; } @@ -129,8 +142,6 @@ export class Track extends React.Component { return reaction(() => { return [...this.primitiveWhitelist.map(key => node[key]), ...objects]; }, (changed, reaction) => { - console.log("autocreated"); - console.log(changed); //check for region this.findRegion(this.time).then((region) => { if (region !== undefined) { //if region at scrub time exist @@ -179,30 +190,28 @@ export class Track extends React.Component { }); } - // /** - // * when timeline is visible, reaction is ran so states are reverted - // */ - // @action - // timelineVisibleReaction = () => { - // return reaction(() => { - // return this.props.timelineVisible; - // }, isVisible => { - // this.revertState(); - // if (isVisible) { - // DocListCast(this.regions).forEach(region => { - // if (!BoolCast((Cast(region, Doc) as Doc).hasData)) { - // for (let i = 0; i < 4; i++) { - // DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = Doc.MakeCopy(this.props.node, true); - // if (i === 0 || i === 3) { - // DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key.opacity = 0.1; - // } - // } - // console.log("saving keyframes"); - // } - // }); - // } - // }); - // } + /** + * when timeline is visible, reaction is ran so states are reverted + */ + @action + timelineVisibleReaction = () => { + return reaction(() => { + return this.props.timelineVisible; + }, isVisible => { + if (isVisible) { + DocListCast(this.regions).forEach(region => { + if (!BoolCast((Cast(region, Doc) as Doc).hasData)) { + for (let i = 0; i < 4; i++) { + DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = this.makeCopy(); + if (i === 0 || i === 3) { //manually inputing fades + (DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key! as Doc).opacity = 0.1; + } + } + } + }); + } + }); + } @observable private saveStateKf: (Doc | undefined) = undefined; @observable private saveStateRegion: (Doc | undefined) = undefined; @@ -214,7 +223,7 @@ export class Track extends React.Component { timeChange = async () => { if (this.saveStateKf !== undefined) { await this.saveKeyframe(); - } + } let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists @@ -318,6 +327,8 @@ export class Track extends React.Component { createRegion = async (time: number) => { if (await this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data + this._newKeyframe = true; + this.saveStateRegion = regiondata; regiondata.position = time; //set position let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); @@ -362,6 +373,9 @@ export class Track extends React.Component { let doc = new Doc(); this.primitiveWhitelist.forEach(key => { let originalVal = this.props.node[key]; + if (key === "data"){ + console.log(originalVal); + } doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; }); return doc; -- cgit v1.2.3-70-g09d2 From c1846375f87b82ea2391f0e56bd1606e34a8f69b Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Wed, 5 Feb 2020 01:01:54 -0500 Subject: major changes --- src/client/views/animationtimeline/Track.tsx | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 32fa1d9ca..09ebbab14 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -91,6 +91,17 @@ export class Track extends React.Component { let keyframes: List = (Cast(this.saveStateRegion!.keyframes, listSpec(Doc)) as List); let kfIndex: number = keyframes.indexOf(this.saveStateKf!); let kf = keyframes[kfIndex] as Doc; //index in the keyframe + if (this._newKeyframe) { + console.log("new keyframe registering"); + let kfList = DocListCast(this.saveStateRegion!.keyframes); + kfList.forEach(kf => { + kf.key = this.makeCopy(); + if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3){ + (kf.key as Doc).opacity = 0.1; + } + }); + this._newKeyframe = false; + } if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades kf.key = this.makeCopy(); @@ -110,21 +121,10 @@ export class Track extends React.Component { (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; (Cast(rightkf!.key, Doc)! as Doc).opacity = 1; } - } else if (this._newKeyframe) { - // console.log("new keyframe registering"); - // let kfList = DocListCast(this.saveStateRegion!.keyframes); - // kfList.forEach(kf => { - // kf.key = this.makeCopy(); - // if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3){ - // (kf.key as Doc).opacity = 0.1; - // } - // }); - } keyframes[kfIndex] = kf; this.saveStateKf = undefined; this.saveStateRegion = undefined; - this._newKeyframe = false; } @@ -223,7 +223,10 @@ export class Track extends React.Component { timeChange = async () => { if (this.saveStateKf !== undefined) { await this.saveKeyframe(); - } + } else if (this._newKeyframe){ + console.log("CALLED"); + await this.saveKeyframe(); + } let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists -- cgit v1.2.3-70-g09d2 From 10432453db7890696eb5c9b1c4572d4cff63b0b1 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Wed, 5 Feb 2020 08:46:21 -0500 Subject: about to merge --- src/client/views/animationtimeline/Keyframe.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.scss b/src/client/views/animationtimeline/Keyframe.scss index f0685943b..8dcf71994 100644 --- a/src/client/views/animationtimeline/Keyframe.scss +++ b/src/client/views/animationtimeline/Keyframe.scss @@ -46,9 +46,9 @@ $timelineDark: #77a1aa; position: absolute; } - .keyframe:hover~.keyframe-information { - display: flex; - } + // .keyframe:hover~.keyframe-information { + // display: flex; + // } .keyframe-information { display: none; -- cgit v1.2.3-70-g09d2 From 9e4d644c5977c781b4822ba065cec6e92977de10 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Wed, 5 Feb 2020 10:16:42 -0500 Subject: about to pull --- src/client/views/animationtimeline/Timeline.scss | 28 ++++++++++++++++++++-- src/client/views/animationtimeline/Timeline.tsx | 21 +++++++++++++--- .../views/animationtimeline/TimelineOverview.scss | 9 ++++++- .../views/animationtimeline/TimelineOverview.tsx | 19 +++++++-------- 4 files changed, 61 insertions(+), 16 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index dbdade03f..ce8c845df 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -43,6 +43,8 @@ $timelineDark: #77a1aa; .time-box { margin-left: 5px; display: flex; + justify-content: center; + align-items: center; } .mode-box { @@ -61,6 +63,12 @@ $timelineDark: #77a1aa; } } + .overview-tool { + display: flex; + justify-content: center; + align-items: center; + } + .animation-text { // font-size: 16px; height: auto; @@ -179,13 +187,14 @@ $timelineDark: #77a1aa; .trackbox { top: 30px; - // TODO: where is this 30px coming from? height: calc(100% - 30px); // height: 100%; + // height: 100%; width: 100%; border-top: 2px solid black; border-bottom: 2px solid black; overflow: hidden; + // overflow-y: scroll; background-color: white; position: absolute; // box-shadow: -10px 0px 10px 10px red; @@ -193,10 +202,25 @@ $timelineDark: #77a1aa; } + .currentTime { + // background: red; + font-size: 12px; + display: flex; + justify-content: center; + align-items: center; + height: 40px; + top: 40px; + position: relative; + width: 100px; + margin-left: 20px; + } + .title-container { - margin-top: 80px; + // margin-top: 80px; + margin-top: 40px; margin-left: 20px; height: calc(100% - 100px - 30px); + // height: 100%; width: 100px; background-color: white; overflow: hidden; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index f0ca1c0ab..ae2dfafc9 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -312,6 +312,7 @@ export class Timeline extends React.Component { e.preventDefault(); e.stopPropagation(); let offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom; + // let offset = 0; if (this._containerHeight + offset <= this.MIN_CONTAINER_HEIGHT) { this._containerHeight = this.MIN_CONTAINER_HEIGHT; } else if (this._containerHeight + offset >= this.MAX_CONTAINER_HEIGHT) { @@ -326,7 +327,10 @@ export class Timeline extends React.Component { */ @action toReadTime = (time: number): string => { - const inSeconds = time / 1000; + time = time / 1000; + var inSeconds = Math.round((time * 100)) / 100; + // var inSeconds = parseFloat(time.toFixed(2)); + // const inSeconds = (Math.floor(time) / 1000); let min: (string | number) = Math.floor(inSeconds / 60); let sec: (string | number) = inSeconds % 60; @@ -451,7 +455,8 @@ export class Timeline extends React.Component {
    {lengthString}
    - + +
    Hello there
    @@ -541,13 +546,22 @@ export class Timeline extends React.Component { } } + // @computed + getCurrentTime = () => { + let current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); + // console.log(this._currentBarX) + return this.toReadTime(current); `` + // return (Math.floor(current) / 1000) + // return current / 1000.0; + } + /** * if you have any question here, just shoot me an email or text. * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) */ render() { - // console.log(this.props.PanelWidth()); + console.log(this.props.Document.isATOn); runInAction(() => { this._panelWidth = this.props.PanelWidth(); this.changeLenths(); @@ -568,6 +582,7 @@ export class Timeline extends React.Component { {DocListCast(this.children).map(doc => )}
    +
    Current: {this.getCurrentTime()}
    {DocListCast(this.children).map(doc =>
    { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}>

    {doc.title}

    )}
    diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index a0b9d462b..283163ea7 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -8,6 +8,13 @@ $timelineDark: #77a1aa; margin-right: 10px; } +.timeline-flex { + display: flex; + justify-content: center; + align-items: center; + width: 100%; +} + .timeline-overview-container { // padding: 0px; margin-right: 5px; @@ -93,7 +100,7 @@ $timelineDark: #77a1aa; height: 4px; width: 0px; z-index: 1000; - background-color: $timelineColor; + background-color: $timelineDark; border-radius: 20px; margin-top: -4px; cursor: pointer; diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 9a881264f..65fd8ff62 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -56,8 +56,11 @@ export class TimelineOverview extends React.Component{ @action setOverviewWidth() { let width = $("#timelineOverview").width(); + // console.log($("timelineOverview")) if (width) this.overviewBarWidth = width; else this.overviewBarWidth = 0; + + // console.log(this.overviewBarWidth) } @action @@ -124,20 +127,14 @@ export class TimelineOverview extends React.Component{ this.visibleTime = vis; this.currentX = x; this.visibleStart = start; + // console.log("getting times") + // console.log(x) + // console.log(start) } render() { - + this.setOverviewWidth(); this.getTimes(); - // calculates where everything should fall based on its size - // let percentVisible = this.props.visibleLength / this.props.totalLength; - // let visibleBarWidth = percentVisible * this.overviewBarWidth; - - // let percentScrubberStart = this.props.currentBarX / this.props.totalLength; - // let scrubberStart = percentScrubberStart * this.overviewBarWidth; - - // let percentBarStart = this.props.visibleStart / this.props.totalLength; - // let barStart = percentBarStart * this.overviewBarWidth; let percentVisible = this.visibleTime / this.props.time; let visibleBarWidth = percentVisible * this.overviewBarWidth; @@ -163,9 +160,11 @@ export class TimelineOverview extends React.Component{
    ]; return ( +
    {timeline}
    +
    ); } -- cgit v1.2.3-70-g09d2 From 4cc2472c5e2d16ac5bc60a919150d1df5d6f4129 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Wed, 5 Feb 2020 10:25:23 -0500 Subject: ready --- src/client/views/animationtimeline/Timeline.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 68b0ce80c..f74d4c71e 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -454,7 +454,7 @@ export class Timeline extends React.Component {
    {lengthString}
    -
    {this.getCurrentTime()}
    +
    Current: {this.getCurrentTime()}
    -- cgit v1.2.3-70-g09d2 From 0c8f7a70dfa954e74c886d89fadf79ee75c26029 Mon Sep 17 00:00:00 2001 From: Andrew Kim Date: Wed, 5 Feb 2020 11:45:03 -0500 Subject: fixed --- src/client/views/animationtimeline/Track.tsx | 31 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 09ebbab14..287e4a04f 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -38,7 +38,6 @@ export class Track extends React.Component { "width", "height", "opacity", - "data", ]; private objectWhitelist = [ "data" @@ -98,6 +97,8 @@ export class Track extends React.Component { kf.key = this.makeCopy(); if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3){ (kf.key as Doc).opacity = 0.1; + } else{ + (kf.key as Doc).opacity = 1; } }); this._newKeyframe = false; @@ -154,18 +155,18 @@ export class Track extends React.Component { }, { fireImmediately: false }); } + + + // @observable private _storedState:(Doc | undefined) = undefined; // /** // * reverting back to previous state before editing on AT // */ // @action // revertState = () => { - // let copyDoc = Doc.MakeCopy(this.props.node, true); // if (this._storedState) this.applyKeys(this._storedState); - // let newState = new Doc(); - // newState.key = copyDoc; - // this._storedState = newState; // } + /** * Reaction when scrubber bar changes * made into function so it's easier to dispose later @@ -176,10 +177,10 @@ export class Track extends React.Component { this.findRegion(this.time).then((regiondata: (Doc | undefined)) => { if (regiondata) { this.props.node.hidden = false; - if (!this._autoKfReaction) { - // console.log("creating another reaction"); - // this._autoKfReaction = this.autoCreateKeyframe(); - } + // if (!this._autoKfReaction) { + // // console.log("creating another reaction"); + // // this._autoKfReaction = this.autoCreateKeyframe(); + // } this.timeChange(); } else { this.props.node.hidden = true; @@ -209,6 +210,9 @@ export class Track extends React.Component { } } }); + } else { + console.log("reverting state"); + //this.revertState(); } }); } @@ -224,7 +228,6 @@ export class Track extends React.Component { if (this.saveStateKf !== undefined) { await this.saveKeyframe(); } else if (this._newKeyframe){ - console.log("CALLED"); await this.saveKeyframe(); } let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on @@ -233,6 +236,7 @@ export class Track extends React.Component { let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { + console.log("is current"); await this.applyKeys(currentkf); this.saveStateKf = currentkf; this.saveStateRegion = regiondata; @@ -330,8 +334,7 @@ export class Track extends React.Component { createRegion = async (time: number) => { if (await this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data - this._newKeyframe = true; - this.saveStateRegion = regiondata; + regiondata.position = time; //set position let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); @@ -339,7 +342,9 @@ export class Track extends React.Component { regiondata.duration = rightRegion.position - regiondata.position; } if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { - this.regions.push(regiondata); + this.regions.push(regiondata); + this._newKeyframe = true; + this.saveStateRegion = regiondata; return regiondata; } } -- cgit v1.2.3-70-g09d2 From e310c0fdcef6ac71ee492470d4ac689cbb094167 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sat, 8 Feb 2020 12:54:07 -0500 Subject: small changes --- src/client/views/animationtimeline/Timeline.tsx | 15 +++++++-------- src/client/views/animationtimeline/TimelineOverview.scss | 2 +- src/client/views/animationtimeline/TimelineOverview.tsx | 8 ++++++-- 3 files changed, 14 insertions(+), 11 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index f74d4c71e..5d5e78652 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -453,7 +453,7 @@ export class Timeline extends React.Component {
    {lengthString}
    - +
    Current: {this.getCurrentTime()}
    @@ -466,12 +466,12 @@ export class Timeline extends React.Component { */ @action private onTimeInput = (e: React.KeyboardEvent) => { - if (e.keyCode === 13) { - let timeInput = this._timeInputRef.current!; - this._time = parseInt(timeInput.value, 10); - this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); - this.props.Document.AnimationLength = this._time; - } + // if (e.keyCode === 13) { + // let timeInput = this._timeInputRef.current!; + // this._time = parseInt(timeInput.value, 10); + // this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); + // this.props.Document.AnimationLength = this._time; + // } } @@ -535,7 +535,6 @@ export class Timeline extends React.Component { * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) */ render() { - console.log(this.props.Document.isATOn); runInAction(() => { this._panelWidth = this.props.PanelWidth(); this.changeLenths(); diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index 283163ea7..b54399307 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -72,7 +72,7 @@ $timelineDark: #77a1aa; position: relative; padding: 0px; margin: 0px; - width: 100%; + // width: 100%; height: 4px; background-color: $timelineColor; border-radius: 20px; diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 7ff78955e..5d6378068 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -58,7 +58,7 @@ export class TimelineOverview extends React.Component{ let width = $("#timelineOverview").width(); // console.log($("timelineOverview")) if (width) this.overviewBarWidth = width; - else this.overviewBarWidth = 0; + // else this.overviewBarWidth = 0; // console.log(this.overviewBarWidth) } @@ -95,10 +95,12 @@ export class TimelineOverview extends React.Component{ onScrubberDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); + if (!this.props.isAuthoring) { document.removeEventListener("pointermove", this.onScrubberMove); document.removeEventListener("pointerup", this.onScrubberUp); document.addEventListener("pointermove", this.onScrubberMove); document.addEventListener("pointerup", this.onScrubberUp); + } } @action @@ -107,8 +109,10 @@ export class TimelineOverview extends React.Component{ e.stopPropagation(); let scrubberRef = this._scrubberRef.current!; let left = scrubberRef.getBoundingClientRect().left; + // left = e.screenX; let offsetX = Math.round(e.clientX - left); this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); + // this.props.changeCurrentBarX(e.screenX) } @action @@ -154,7 +158,7 @@ export class TimelineOverview extends React.Component{
    ] : [ -
    +
    ,
    -- cgit v1.2.3-70-g09d2 From 92068acedc3d6f5bed25dc1a0dacbe19d0338829 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 9 Feb 2020 14:06:54 -0500 Subject: reformatted --- package-lock.json | 8 ++++- package.json | 2 +- src/client/views/animationtimeline/Timeline.tsx | 2 +- .../views/animationtimeline/TimelineOverview.scss | 2 +- .../views/animationtimeline/TimelineOverview.tsx | 35 +++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 33 ++++++++++---------- 6 files changed, 46 insertions(+), 36 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/package-lock.json b/package-lock.json index 379cd3337..1f87e1493 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14244,7 +14244,8 @@ "fsevents": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==" + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "optional": true }, "get-caller-file": { "version": "2.0.5", @@ -16134,6 +16135,11 @@ "setimmediate": "^1.0.4" } }, + "tiny-timer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tiny-timer/-/tiny-timer-1.4.0.tgz", + "integrity": "sha512-NZCayn+Y2OaDaBtVQmx8Lx/SggG9qGGINYoPNmXpqvj8ue7E24H9ZgI+02I9BZSMWCW8keOXsw2R3hSzk7aALQ==" + }, "tinycolor2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", diff --git a/package.json b/package.json index b6beb5a3a..155026d2c 100644 --- a/package.json +++ b/package.json @@ -237,4 +237,4 @@ "xoauth2": "^1.2.0", "youtube": "^0.1.0" } -} \ No newline at end of file +} diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 5d5e78652..2ac7e6c92 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -343,7 +343,7 @@ export class Timeline extends React.Component { * context menu function. * opens the timeline or closes the timeline. */ - timelineContextMenu = (e: MouseEvent): void => { + timelineContextMenu = (e: React.MouseEvent): void => { ContextMenu.Instance.addItem({ description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { this._timelineVisible = !this._timelineVisible; diff --git a/src/client/views/animationtimeline/TimelineOverview.scss b/src/client/views/animationtimeline/TimelineOverview.scss index b54399307..283163ea7 100644 --- a/src/client/views/animationtimeline/TimelineOverview.scss +++ b/src/client/views/animationtimeline/TimelineOverview.scss @@ -72,7 +72,7 @@ $timelineDark: #77a1aa; position: relative; padding: 0px; margin: 0px; - // width: 100%; + width: 100%; height: 4px; background-color: $timelineColor; border-radius: 20px; diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 5d6378068..6358982c4 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -55,7 +55,7 @@ export class TimelineOverview extends React.Component{ @action setOverviewWidth() { - let width = $("#timelineOverview").width(); + const width = $("#timelineOverview").width(); // console.log($("timelineOverview")) if (width) this.overviewBarWidth = width; // else this.overviewBarWidth = 0; @@ -77,7 +77,7 @@ export class TimelineOverview extends React.Component{ onPanX = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); - let movX = (this.props.visibleStart / this.props.totalLength) * (this.DEFAULT_WIDTH) + e.movementX; + const movX = (this.props.visibleStart / this.props.totalLength) * (this.DEFAULT_WIDTH) + e.movementX; // let movX = (this.props.visibleStart / this.props.totalLength) * (this.overviewWidth) + e.movementX; this.props.movePanX((movX / (this.DEFAULT_WIDTH)) * this.props.totalLength); // this.props.movePanX((movX / (this.overviewWidth) * this.props.totalLength); @@ -95,22 +95,22 @@ export class TimelineOverview extends React.Component{ onScrubberDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - if (!this.props.isAuthoring) { + // if (!this.props.isAuthoring) { document.removeEventListener("pointermove", this.onScrubberMove); document.removeEventListener("pointerup", this.onScrubberUp); document.addEventListener("pointermove", this.onScrubberMove); document.addEventListener("pointerup", this.onScrubberUp); - } + // } } @action onScrubberMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let scrubberRef = this._scrubberRef.current!; - let left = scrubberRef.getBoundingClientRect().left; + const scrubberRef = this._scrubberRef.current!; + const left = scrubberRef.getBoundingClientRect().left; // left = e.screenX; - let offsetX = Math.round(e.clientX - left); + const offsetX = Math.round(e.clientX - left); this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); // this.props.changeCurrentBarX(e.screenX) } @@ -125,9 +125,9 @@ export class TimelineOverview extends React.Component{ @action getTimes() { - let vis = KeyframeFunc.convertPixelTime(this.props.visibleLength, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); - let x = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); - let start = KeyframeFunc.convertPixelTime(this.props.visibleStart, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const vis = KeyframeFunc.convertPixelTime(this.props.visibleLength, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const x = KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const start = KeyframeFunc.convertPixelTime(this.props.visibleStart, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); this.visibleTime = vis; this.currentX = x; this.visibleStart = start; @@ -137,19 +137,20 @@ export class TimelineOverview extends React.Component{ } render() { + console.log("RERENDERED!"); this.setOverviewWidth(); this.getTimes(); - let percentVisible = this.visibleTime / this.props.time; - let visibleBarWidth = percentVisible * this.overviewBarWidth; + const percentVisible = this.visibleTime / this.props.time; + const visibleBarWidth = percentVisible * this.overviewBarWidth; - let percentScrubberStart = this.currentX / this.props.time; - let scrubberStart = percentScrubberStart * this.overviewBarWidth; + const percentScrubberStart = this.currentX / this.props.time; + const scrubberStart = percentScrubberStart * this.overviewBarWidth; - let percentBarStart = this.visibleStart / this.props.time; - let barStart = percentBarStart * this.overviewBarWidth; + const percentBarStart = this.visibleStart / this.props.time; + const barStart = percentBarStart * this.overviewBarWidth; - let timeline = this.props.isAuthoring ? [ + const timeline = this.props.isAuthoring ? [
    , diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c61dcd21f..56b3b3bc0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -966,7 +966,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onContextMenu = (e: React.MouseEvent) => { const layoutItems: ContextMenuProps[] = []; - layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); layoutItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); @@ -1019,6 +1018,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { icon: "eye" }); ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" }); + this._timelineRef.current!.timelineContextMenu(e); } @@ -1070,21 +1070,24 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey. // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document // let lodarea = this.Document[WidthSym]() * this.Document[HeightSym]() / this.props.ScreenToLocalTransform().Scale / this.props.ScreenToLocalTransform().Scale; - return
    - {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ? - this.placeholder : this.marqueeView} + return
    +
    + {/* */} + {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ? + this.placeholder : this.marqueeView} + +
    -
    ; } } -- cgit v1.2.3-70-g09d2 From 4eb94980caad8fb98863e140189a9f77b67cf354 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 9 Feb 2020 14:46:05 -0500 Subject: fixed scrubber --- src/client/views/animationtimeline/Keyframe.tsx | 2 +- src/client/views/animationtimeline/TimelineOverview.tsx | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index a2add3ae8..fd4a3f281 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -337,7 +337,7 @@ export class Keyframe extends React.Component { makeKeyframeMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Show Data", () => { runInAction(() => { - let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { width: 300, height: 300 }); + let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { _width: 300, _height: 300 }); CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); }), diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 6358982c4..4d1d1cf88 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -109,10 +109,8 @@ export class TimelineOverview extends React.Component{ e.stopPropagation(); const scrubberRef = this._scrubberRef.current!; const left = scrubberRef.getBoundingClientRect().left; - // left = e.screenX; const offsetX = Math.round(e.clientX - left); - this.props.changeCurrentBarX((offsetX / (this.DEFAULT_WIDTH) * this.props.totalLength) + this.props.currentBarX); - // this.props.changeCurrentBarX(e.screenX) + this.props.changeCurrentBarX((((offsetX) / this.overviewBarWidth) * this.props.totalLength) + this.props.currentBarX); } @action @@ -154,7 +152,7 @@ export class TimelineOverview extends React.Component{
    , -
    +
    -- cgit v1.2.3-70-g09d2 From 7d2e05ca34d28d49d9272668091bfce7e6b47f22 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 9 Feb 2020 14:51:26 -0500 Subject: fixed --- src/client/views/animationtimeline/TimelineOverview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 4d1d1cf88..66f6a9482 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -158,9 +158,9 @@ export class TimelineOverview extends React.Component{
    ] : [
    -
    +
    , -
    +
    ]; return (
    -- cgit v1.2.3-70-g09d2 From fb9cbca164800c26baee2d0bde171e9e052b7866 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sun, 9 Feb 2020 14:55:25 -0500 Subject: taking out right information --- package-lock.json | 142 +++++++++++++++--------- package.json | 4 +- src/client/views/animationtimeline/Timeline.tsx | 23 +++- 3 files changed, 111 insertions(+), 58 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/package-lock.json b/package-lock.json index 1f87e1493..5684789c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6533,9 +6533,9 @@ "dev": true }, "https-proxy-agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", - "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "requires": { "agent-base": "^4.3.0", "debug": "^3.1.0" @@ -8844,9 +8844,9 @@ "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" }, "npm": { - "version": "6.13.6", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.6.tgz", - "integrity": "sha512-NomC08kv7HIl1FOyLOe9Hp89kYsOsvx52huVIJ7i8hFW8Xp65lDwe/8wTIrh9q9SaQhA8hTrfXPh3BEL3TmMpw==", + "version": "6.13.7", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.7.tgz", + "integrity": "sha512-X967EKTT407CvgrWFjXusnPh0VLERcmR9hZFSVgkEquOomZkvpwLJ5zrQ3qrG9SpPLKJE4bPLUu76exKQ4a3Cg==", "requires": { "JSONStream": "^1.3.5", "abbrev": "~1.1.1", @@ -8854,7 +8854,7 @@ "ansistyles": "~0.1.3", "aproba": "^2.0.0", "archy": "~1.0.0", - "bin-links": "^1.1.6", + "bin-links": "^1.1.7", "bluebird": "^3.5.5", "byte-size": "^5.0.1", "cacache": "^12.0.3", @@ -8897,7 +8897,7 @@ "libnpmorg": "^1.0.1", "libnpmsearch": "^2.0.2", "libnpmteam": "^1.0.2", - "libnpx": "^10.2.0", + "libnpx": "^10.2.2", "lock-verify": "^2.1.0", "lockfile": "^1.0.4", "lodash._baseindexof": "*", @@ -8916,7 +8916,7 @@ "mississippi": "^3.0.0", "mkdirp": "~0.5.1", "move-concurrently": "^1.0.1", - "node-gyp": "^5.0.5", + "node-gyp": "^5.0.7", "nopt": "~4.0.1", "normalize-package-data": "^2.5.0", "npm-audit-report": "^1.3.2", @@ -9113,7 +9113,7 @@ } }, "bin-links": { - "version": "1.1.6", + "version": "1.1.7", "bundled": true, "requires": { "bluebird": "^3.5.3", @@ -9593,7 +9593,7 @@ } }, "env-paths": { - "version": "1.0.0", + "version": "2.2.0", "bundled": true }, "err-code": { @@ -9896,7 +9896,7 @@ } }, "get-caller-file": { - "version": "1.0.2", + "version": "1.0.3", "bundled": true }, "get-stream": { @@ -10091,7 +10091,7 @@ } }, "invert-kv": { - "version": "1.0.0", + "version": "2.0.0", "bundled": true }, "ip": { @@ -10249,10 +10249,10 @@ "bundled": true }, "lcid": { - "version": "1.0.0", + "version": "2.0.0", "bundled": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "^2.0.0" } }, "libcipm": { @@ -10411,7 +10411,7 @@ } }, "libnpx": { - "version": "10.2.0", + "version": "10.2.2", "bundled": true, "requires": { "dotenv": "^5.0.1", @@ -10541,15 +10541,30 @@ "ssri": "^6.0.0" } }, + "map-age-cleaner": { + "version": "0.1.3", + "bundled": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "meant": { "version": "1.0.1", "bundled": true }, "mem": { - "version": "1.1.0", + "version": "4.3.0", "bundled": true, "requires": { - "mimic-fn": "^1.0.0" + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "bundled": true + } } }, "mime-db": { @@ -10563,10 +10578,6 @@ "mime-db": "~1.35.0" } }, - "mimic-fn": { - "version": "1.2.0", - "bundled": true - }, "minimatch": { "version": "3.0.4", "bundled": true, @@ -10644,6 +10655,10 @@ "version": "0.0.7", "bundled": true }, + "nice-try": { + "version": "1.0.5", + "bundled": true + }, "node-fetch-npm": { "version": "2.0.2", "bundled": true, @@ -10654,33 +10669,20 @@ } }, "node-gyp": { - "version": "5.0.5", + "version": "5.0.7", "bundled": true, "requires": { - "env-paths": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.2", + "mkdirp": "^0.5.1", + "nopt": "^4.0.1", + "npmlog": "^4.1.2", + "request": "^2.88.0", + "rimraf": "^2.6.3", + "semver": "^5.7.1", "tar": "^4.4.12", - "which": "1" - }, - "dependencies": { - "nopt": { - "version": "3.0.6", - "bundled": true, - "requires": { - "abbrev": "1" - } - }, - "semver": { - "version": "5.3.0", - "bundled": true - } + "which": "^1.3.1" } }, "nopt": { @@ -10874,12 +10876,38 @@ "bundled": true }, "os-locale": { - "version": "2.1.0", + "version": "3.1.0", "bundled": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "bundled": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "bundled": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "os-tmpdir": { @@ -10894,10 +10922,18 @@ "os-tmpdir": "^1.0.0" } }, + "p-defer": { + "version": "1.0.0", + "bundled": true + }, "p-finally": { "version": "1.0.0", "bundled": true }, + "p-is-promise": { + "version": "2.1.0", + "bundled": true + }, "p-limit": { "version": "1.2.0", "bundled": true, @@ -11872,14 +11908,14 @@ "bundled": true }, "yargs": { - "version": "11.0.0", + "version": "11.1.1", "bundled": true, "requires": { "cliui": "^4.0.0", "decamelize": "^1.1.1", "find-up": "^2.1.0", "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", + "os-locale": "^3.1.0", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", @@ -17955,7 +17991,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", diff --git a/package.json b/package.json index 155026d2c..5b2818c33 100644 --- a/package.json +++ b/package.json @@ -176,7 +176,7 @@ "nodemailer": "^5.1.1", "nodemon": "^1.19.4", "normalize.css": "^8.0.1", - "npm": "^6.13.6", + "npm": "^6.13.7", "p-limit": "^2.2.0", "passport": "^0.4.0", "passport-google-oauth20": "^2.0.0", @@ -226,8 +226,8 @@ "socket.io": "^2.3.0", "socket.io-client": "^2.3.0", "solr-node": "^1.2.1", - "tiny-timer": "^1.3.0", "standard-http-error": "^2.0.1", + "tiny-timer": "^1.3.0", "typescript-collections": "^1.3.3", "url-loader": "^1.1.2", "uuid": "^3.4.0", diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 2ac7e6c92..e4af43e0a 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -433,6 +433,8 @@ export class Timeline extends React.Component { lengthString = ""; } + // let rightInfo = this.timeIndicator; + return (
    @@ -452,15 +454,30 @@ export class Timeline extends React.Component {
    -
    {lengthString}
    - -
    Current: {this.getCurrentTime()}
    + {this.timeIndicator(lengthString)} + {/* {rightInfo} */}
    ); } + timeIndicator(lengthString: string) { + if (this.props.Document.isATOn) { + return ( + <> +
    {lengthString}
    + + + ); + } + else { + return ( +
    Current: {this.getCurrentTime()}
    + ); + } + } + /** * manual time input (kinda broken right now) */ -- cgit v1.2.3-70-g09d2 From eea67eb24b944f13e307915d386f7745078e7d23 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 9 Feb 2020 15:06:45 -0500 Subject: const to let --- src/client/views/animationtimeline/Keyframe.tsx | 74 +++++++++++----------- src/client/views/animationtimeline/Timeline.tsx | 56 ++++++++-------- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- 3 files changed, 67 insertions(+), 67 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index fd4a3f281..bb557289e 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -36,7 +36,7 @@ export namespace KeyframeFunc { let leftMost: (RegionData | undefined) = undefined; let rightMost: (RegionData | undefined) = undefined; DocListCast(regions).forEach(region => { - let neighbor = RegionData(region); + const neighbor = RegionData(region); if (currentRegion.position! > neighbor.position) { if (!leftMost || neighbor.position > leftMost.position) { leftMost = neighbor; @@ -57,7 +57,7 @@ export namespace KeyframeFunc { 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!); + const keyframes = await DocListCastAsync(region.keyframes!); keyframes!.forEach((kf) => { let compTime = currentBarX; if (ref) compTime = NumCast(ref.time); @@ -73,7 +73,7 @@ export namespace KeyframeFunc { 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!); + const keyframes = await DocListCastAsync(region.keyframes!); keyframes!.forEach((kf) => { let compTime = currentBarX; if (ref) compTime = NumCast(ref.time); @@ -86,7 +86,7 @@ export namespace KeyframeFunc { }; export const defaultKeyframe = () => { - let regiondata = new Doc(); //creating regiondata in MILI + const regiondata = new Doc(); //creating regiondata in MILI regiondata.duration = 4000; regiondata.position = 0; regiondata.fadeIn = 1000; @@ -98,7 +98,7 @@ export namespace KeyframeFunc { export const convertPixelTime = (pos: number, unit: "mili" | "sec" | "min" | "hr", dir: "pixel" | "time", tickSpacing: number, tickIncrement: number) => { - let time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement; + const time = dir === "pixel" ? (pos * tickSpacing) / tickIncrement : (pos / tickSpacing) * tickIncrement; switch (unit) { case "mili": return time; @@ -180,10 +180,10 @@ export class Keyframe extends React.Component { componentDidMount() { setTimeout(() => { //giving it a temporary 1sec delay... if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); - let start = this.props.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); - let fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); - let fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); - let finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); + const start = this.props.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); + const fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); + const fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); + const finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); (fadeIn.key as Doc).opacity = 1; (fadeOut.key as Doc).opacity = 1; (start.key as Doc).opacity = 0.1; @@ -199,7 +199,7 @@ export class Keyframe extends React.Component { onBarPointerDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let clientX = e.clientX; + const clientX = e.clientX; if (this._doubleClickEnabled) { this.createKeyframe(clientX); this._doubleClickEnabled = false; @@ -224,10 +224,10 @@ export class Keyframe extends React.Component { if (e.movementX !== 0) { this._mouseToggled = true; } - 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 + KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const left = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions)!; + const right = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions!); + const prevX = this.regiondata.position; + const futureX = this.regiondata.position + KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); if (futureX <= 0) { this.regiondata.position = 0; } else if ((left && left.position + left.duration >= futureX)) { @@ -237,7 +237,7 @@ export class Keyframe extends React.Component { } else { this.regiondata.position = futureX; } - let movement = this.regiondata.position - prevX; + const movement = this.regiondata.position - prevX; this.keyframes.forEach(kf => { kf.time = NumCast(kf.time) + movement; }); @@ -267,9 +267,9 @@ export class Keyframe extends React.Component { onDragResizeLeft = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let bar = this._bar.current!; - let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); - let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions); + const bar = this._bar.current!; + const offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions); if (leftRegion && this.regiondata.position + offset <= leftRegion.position + leftRegion.duration) { this.regiondata.position = leftRegion.position + leftRegion.duration; this.regiondata.duration = NumCast(this.keyframes[this.keyframes.length - 1].time) - (leftRegion.position + leftRegion.duration); @@ -292,10 +292,10 @@ export class Keyframe extends React.Component { onDragResizeRight = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let bar = this._bar.current!; - let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); - let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions); - let fadeOutKeyframeTime = NumCast(this.keyframes[this.keyframes.length - 3].time); + const bar = this._bar.current!; + const offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions); + const fadeOutKeyframeTime = NumCast(this.keyframes[this.keyframes.length - 3].time); if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= fadeOutKeyframeTime) { //case 1: when third to last keyframe is in the way this.regiondata.duration = fadeOutKeyframeTime - this.regiondata.position + this.regiondata.fadeOut; } else if (rightRegion && (this.regiondata.position + this.regiondata.duration + offset >= rightRegion.position)) { @@ -311,10 +311,10 @@ export class Keyframe extends React.Component { @action createKeyframe = async (clientX: number) => { this._mouseToggled = true; - let bar = this._bar.current!; - let offset = KeyframeFunc.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); + const bar = this._bar.current!; + const offset = KeyframeFunc.convertPixelTime(Math.round((clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends - let position = this.regiondata.position; + const position = this.regiondata.position; this.props.makeKeyData(this.regiondata, Math.round(position + offset), KeyframeFunc.KeyframeType.default); this.regiondata.hasData = true; this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied @@ -337,7 +337,7 @@ export class Keyframe extends React.Component { makeKeyframeMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Show Data", () => { runInAction(() => { - let kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { _width: 300, _height: 300 }); + const kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { _width: 300, _height: 300 }); CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); }); }), @@ -350,7 +350,7 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.addItem("input", "Move", (val) => { runInAction(() => { let cannotMove: boolean = false; - let kfIndex: number = this.keyframes.indexOf(kf); + const kfIndex: number = this.keyframes.indexOf(kf); if (val < 0 || (val < NumCast(this.keyframes[kfIndex - 1].time) || val > NumCast(this.keyframes[kfIndex + 1].time))) { cannotMove = true; } @@ -401,7 +401,7 @@ export class Keyframe extends React.Component { }), TimelineMenu.Instance.addItem("input", `position: ${this.regiondata.position}ms`, (val) => { runInAction(() => { - let prevPosition = this.regiondata.position; + const prevPosition = this.regiondata.position; let cannotMove: boolean = false; DocListCast(this.regions).forEach(region => { if (NumCast(region.position) !== this.regiondata.position) { @@ -454,7 +454,7 @@ export class Keyframe extends React.Component { onContainerOver = (e: React.PointerEvent, ref: React.RefObject) => { e.preventDefault(); e.stopPropagation(); - let div = ref.current!; + const div = ref.current!; div.style.opacity = "1"; Doc.BrushDoc(this.props.node); } @@ -466,7 +466,7 @@ export class Keyframe extends React.Component { onContainerOut = (e: React.PointerEvent, ref: React.RefObject) => { e.preventDefault(); e.stopPropagation(); - let div = ref.current!; + const div = ref.current!; div.style.opacity = "0"; Doc.UnBrushDoc(this.props.node); } @@ -481,7 +481,7 @@ export class Keyframe extends React.Component { */ @action drawKeyframes = () => { - let keyframeDivs: JSX.Element[] = []; + const keyframeDivs: JSX.Element[] = []; DocListCast(this.regiondata.keyframes).forEach(kf => { if (kf.type as KeyframeFunc.KeyframeType !== KeyframeFunc.KeyframeType.end) { keyframeDivs.push( @@ -514,14 +514,14 @@ export class Keyframe extends React.Component { */ @action drawKeyframeDividers = () => { - let keyframeDividers: JSX.Element[] = []; + const keyframeDividers: JSX.Element[] = []; DocListCast(this.regiondata.keyframes).forEach(kf => { - let index = this.keyframes.indexOf(kf); + const index = this.keyframes.indexOf(kf); if (index !== this.keyframes.length - 1) { - let right = this.keyframes[index + 1]; - let bodyRef = React.createRef(); - let kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - let rightPos = KeyframeFunc.convertPixelTime(NumCast(right.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + const right = this.keyframes[index + 1]; + const bodyRef = React.createRef(); + const kfPos = KeyframeFunc.convertPixelTime(NumCast(kf.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); + const rightPos = KeyframeFunc.convertPixelTime(NumCast(right.time), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); keyframeDividers.push(
    { e.preventDefault(); e.stopPropagation(); this.onContainerOver(e, bodyRef); }} diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 2ac7e6c92..dc381609e 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -86,7 +86,7 @@ export class Timeline extends React.Component { */ @computed private get children(): List { - let extendedDocument = ["image", "video", "pdf"].includes(StrCast(this.props.Document.type)); + const 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; @@ -99,7 +99,7 @@ export class Timeline extends React.Component { /////////lifecycle functions//////////// componentWillMount() { - let relativeHeight = window.innerHeight / 20; //sets height to arbitrary size, relative to innerHeight + const relativeHeight = window.innerHeight / 20; //sets height to arbitrary size, relative to innerHeight this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset @@ -133,7 +133,7 @@ export class Timeline extends React.Component { */ @action drawTicks = () => { - let ticks = []; + const ticks = []; for (let i = 0; i < this._time / this._tickIncrement; i++) { ticks.push(

    {this.toReadTime(i * this._tickIncrement)}

    ); } @@ -226,9 +226,9 @@ export class Timeline extends React.Component { onScrubberMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let scrubberbox = this._infoContainer.current!; - let left = scrubberbox.getBoundingClientRect().left; - let offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; + const scrubberbox = this._infoContainer.current!; + const left = scrubberbox.getBoundingClientRect().left; + const offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale; this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position } @@ -239,7 +239,7 @@ export class Timeline extends React.Component { onPanDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let clientX = e.clientX; + const clientX = e.clientX; if (this._doubleClickEnabled) { this._doubleClickEnabled = false; } else { @@ -270,8 +270,8 @@ export class Timeline extends React.Component { if (e.movementX !== 0 || e.movementY !== 0) { this._mouseToggled = true; } - let trackbox = this._trackbox.current!; - let titleContainer = this._titleContainer.current!; + const trackbox = this._trackbox.current!; + const titleContainer = this._titleContainer.current!; this.movePanX(this._visibleStart - e.movementX); trackbox.scrollTop = trackbox.scrollTop - e.movementY; titleContainer.scrollTop = titleContainer.scrollTop - e.movementY; @@ -287,7 +287,7 @@ export class Timeline extends React.Component { @action movePanX = (pixel: number) => { - let infoContainer = this._infoContainer.current!; + const infoContainer = this._infoContainer.current!; infoContainer.scrollLeft = pixel; this._visibleStart = infoContainer.scrollLeft; } @@ -309,7 +309,7 @@ export class Timeline extends React.Component { onResizeMove = (e: PointerEvent) => { e.preventDefault(); e.stopPropagation(); - let offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom; + const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom; // let offset = 0; if (this._containerHeight + offset <= this.MIN_CONTAINER_HEIGHT) { this._containerHeight = this.MIN_CONTAINER_HEIGHT; @@ -326,10 +326,10 @@ export class Timeline extends React.Component { @action toReadTime = (time: number): string => { time = time / 1000; - var inSeconds = Math.round((time * 100)) / 100; + const inSeconds = Math.round((time * 100)) / 100; // var inSeconds = parseFloat(time.toFixed(2)); // const inSeconds = (Math.floor(time) / 1000); - let min: (string | number) = Math.floor(inSeconds / 60); + const min: (string | number) = Math.floor(inSeconds / 60); let sec: (string | number) = inSeconds % 60; if (Math.floor(sec / 10) === 0) { @@ -360,12 +360,12 @@ export class Timeline extends React.Component { onWheelZoom = (e: React.WheelEvent) => { e.preventDefault(); e.stopPropagation(); - let offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left; - let prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement); - let prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); + const offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left; + const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement); + const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); e.deltaY < 0 ? this.zoom(true) : this.zoom(false); - let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); - let currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement); + const currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); + const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement); this._infoContainer.current!.scrollLeft = currPixel - offset; this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0; this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) : 0; @@ -396,7 +396,7 @@ export class Timeline extends React.Component { spacingChange -= 5; } } - let finalLength = spacingChange * (this._time / incrementChange); + const finalLength = spacingChange * (this._time / incrementChange); if (finalLength >= this._infoContainer.current!.getBoundingClientRect().width) { this._totalLength = finalLength; this._tickSpacing = spacingChange; @@ -408,19 +408,19 @@ export class Timeline extends React.Component { * tool box includes the toggle buttons at the top of the timeline (both editing mode and play mode) */ private timelineToolBox = (scale: number) => { - let size = 40 * scale; //50 is default - let iconSize = 25; + const size = 40 * scale; //50 is default + const iconSize = 25; //decides if information should be omitted because the timeline is very small // if its less than 950 pixels then it's going to be overlapping let shouldCompress = false; - let width: number = this.props.PanelWidth(); + const width: number = this.props.PanelWidth(); if (width < 850) { shouldCompress = true; } let modeString, overviewString, lengthString; - let modeType = this.props.Document.isATOn ? "Author" : "Play"; + const modeType = this.props.Document.isATOn ? "Author" : "Play"; if (!shouldCompress) { modeString = "Mode: " + modeType; @@ -489,9 +489,9 @@ export class Timeline extends React.Component { * turns on the toggle button (the purple slide button that changes from editing mode and play mode */ private toggleHandle = () => { - let roundToggle = this._roundToggleRef.current!; - let roundToggleContainer = this._roundToggleContainerRef.current!; - let timelineContainer = this._timelineContainer.current!; + const roundToggle = this._roundToggleRef.current!; + const roundToggleContainer = this._roundToggleContainerRef.current!; + const timelineContainer = this._timelineContainer.current!; if (BoolCast(this.props.Document.isATOn)) { roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; @@ -522,9 +522,9 @@ export class Timeline extends React.Component { // @computed getCurrentTime = () => { - let current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); + const current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); // console.log(this._currentBarX) - return this.toReadTime(current); `` + return this.toReadTime(current); // return (Math.floor(current) / 1000) // return current / 1000.0; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 56b3b3bc0..ef1ec5e78 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -809,7 +809,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { doFreeformLayout(poolData: Map) { const layoutDocs = this.childLayoutPairs.map(pair => pair.layout); const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log); - let state = initResult && initResult.success ? initResult.result.scriptState : undefined; + const state = initResult && initResult.success ? initResult.result.scriptState : undefined; const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : []; this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => { @@ -1001,7 +1001,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } }); //@ts-ignore - let subitems: ContextMenuProps[] = + const subitems: ContextMenuProps[] = DocListCast((CurrentUserUtils.UserDocument.noteTypes as Doc).data).map((note, i) => ({ description: (i + 1) + ": " + StrCast(note.title), event: () => console.log("Hi"), -- cgit v1.2.3-70-g09d2 From 61d4938de06a09687eca4a1549d48b1009aa4134 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sun, 9 Feb 2020 15:07:55 -0500 Subject: rounding fix --- src/client/views/animationtimeline/Timeline.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index e4af43e0a..cbdbd07b149 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -326,15 +326,18 @@ export class Timeline extends React.Component { @action toReadTime = (time: number): string => { time = time / 1000; - var inSeconds = Math.round((time * 100)) / 100; + const inSeconds = Math.round(time * 100) / 100; + + // console.log(inSeconds) // var inSeconds = parseFloat(time.toFixed(2)); // const inSeconds = (Math.floor(time) / 1000); - let min: (string | number) = Math.floor(inSeconds / 60); - let sec: (string | number) = inSeconds % 60; + const min: (string | number) = Math.floor(inSeconds / 60); + let sec: (string | number) = (Math.round((inSeconds % 60) * 100) / 100); if (Math.floor(sec / 10) === 0) { sec = "0" + sec; } + // sec = Number.parseFloat(sec).toFixed(2); return `${min}:${sec}`; } -- cgit v1.2.3-70-g09d2 From ba835bbe5b31e2ad0badc9f960b75f639e0b02e0 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sun, 9 Feb 2020 15:50:43 -0500 Subject: moving... --- src/client/views/animationtimeline/Timeline.tsx | 23 ++++++++++++++++++++-- .../views/animationtimeline/TimelineOverview.tsx | 1 - src/client/views/animationtimeline/Track.tsx | 7 +++++++ 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index dc381609e..42bb02e85 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -342,6 +342,7 @@ export class Timeline extends React.Component { /** * context menu function. * opens the timeline or closes the timeline. + * Used in: Freeform */ timelineContextMenu = (e: React.MouseEvent): void => { ContextMenu.Instance.addItem({ @@ -493,6 +494,7 @@ export class Timeline extends React.Component { const roundToggleContainer = this._roundToggleContainerRef.current!; const timelineContainer = this._timelineContainer.current!; if (BoolCast(this.props.Document.isATOn)) { + //turning on playmode... roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; @@ -500,7 +502,9 @@ export class Timeline extends React.Component { timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isATOn = false; this._isAuthoring = false; + this.tracks(); } else { + //turning on authoring mode... roundToggle.style.transform = "translate(20px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; @@ -524,12 +528,24 @@ export class Timeline extends React.Component { getCurrentTime = () => { const current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); // console.log(this._currentBarX) - return this.toReadTime(current); + return this.toReadTime(current); // return (Math.floor(current) / 1000) // return current / 1000.0; } + @observable private mapOfTracks: (Track | null)[] = []; + @action + tracks = () => { + console.log(this.mapOfTracks.length); + this.mapOfTracks.forEach(track => { + if (track !== null) { + track.getLastRegion(); + } else { + + } + }); + } /** * if you have any question here, just shoot me an email or text. * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) @@ -552,7 +568,10 @@ export class Timeline extends React.Component {
    - {DocListCast(this.children).map(doc => )} + {DocListCast(this.children).map(doc => { + const track = { this.mapOfTracks.push(ref); }} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={this._timelineVisible} /> + return track; + })}
    Current: {this.getCurrentTime()}
    diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 66f6a9482..b8c639825 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -135,7 +135,6 @@ export class TimelineOverview extends React.Component{ } render() { - console.log("RERENDERED!"); this.setOverviewWidth(); this.getTimes(); diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 84edc6e0a..620668db7 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -80,6 +80,13 @@ export class Track extends React.Component { } //////////////////////////////// + + getLastRegion = () => { + + console.log((this.regions[this.regions.length - 1] as Doc).time); + return this.regions[this.regions.length - 1] as Doc; + } + /** * keyframe save logic. Needs to be changed so it's more efficient * -- cgit v1.2.3-70-g09d2 From 79f70eebe0a29f8f3d759eff82dd3446ac3d5ed1 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sun, 9 Feb 2020 16:14:04 -0500 Subject: done with ui bugs --- src/client/views/animationtimeline/Timeline.scss | 3 ++ src/client/views/animationtimeline/Timeline.tsx | 11 +++--- .../views/animationtimeline/TimelineOverview.tsx | 45 +++++++++++----------- 3 files changed, 30 insertions(+), 29 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index ce8c845df..a99d017f4 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -42,14 +42,17 @@ $timelineDark: #77a1aa; .time-box { margin-left: 5px; + min-width: 140px; display: flex; justify-content: center; align-items: center; + // grid-column-start: line2; } .mode-box { display: flex; margin-left: 5px; + // grid-column-start: line3; } .overview-box { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index e0f70dd43..3c4c5c93f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -458,7 +458,6 @@ export class Timeline extends React.Component {
    {this.timeIndicator(lengthString)} - {/* {rightInfo} */}
    @@ -470,13 +469,16 @@ export class Timeline extends React.Component { return ( <>
    {lengthString}
    - +
    1:40.07
    ); } else { return ( -
    Current: {this.getCurrentTime()}
    +
    +
    {`Current: ${this.getCurrentTime()}`}
    +
    {`Total: 1:40.07`}
    +
    ); } } @@ -543,10 +545,7 @@ export class Timeline extends React.Component { // @computed getCurrentTime = () => { const current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); - // console.log(this._currentBarX) return this.toReadTime(current); - // return (Math.floor(current) / 1000) - // return current / 1000.0; } diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 66f6a9482..94bbe7074 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -27,6 +27,8 @@ export class TimelineOverview extends React.Component{ @observable private _visibleRef = React.createRef(); @observable private _scrubberRef = React.createRef(); @observable private overviewBarWidth: number = 0; + @observable private playbarWidth: number = 0; + @observable private activeOverviewWidth: number = 0; @observable private _authoringReaction?: IReactionDisposer; @observable private visibleTime: number = 0; @observable private currentX: number = 0; @@ -55,12 +57,17 @@ export class TimelineOverview extends React.Component{ @action setOverviewWidth() { - const width = $("#timelineOverview").width(); - // console.log($("timelineOverview")) - if (width) this.overviewBarWidth = width; - // else this.overviewBarWidth = 0; - - // console.log(this.overviewBarWidth) + const width1 = $("#timelineOverview").width(); + const width2 = $("#timelinePlay").width(); + if (width1 && width1 !== 0) this.overviewBarWidth = width1; + if (width2 && width2 !== 0) this.playbarWidth = width2; + + if (this.props.isAuthoring) { + this.activeOverviewWidth = this.overviewBarWidth; + } + else { + this.activeOverviewWidth = this.playbarWidth; + } } @action @@ -78,9 +85,7 @@ export class TimelineOverview extends React.Component{ e.stopPropagation(); e.preventDefault(); const movX = (this.props.visibleStart / this.props.totalLength) * (this.DEFAULT_WIDTH) + e.movementX; - // let movX = (this.props.visibleStart / this.props.totalLength) * (this.overviewWidth) + e.movementX; this.props.movePanX((movX / (this.DEFAULT_WIDTH)) * this.props.totalLength); - // this.props.movePanX((movX / (this.overviewWidth) * this.props.totalLength); } @action @@ -95,12 +100,10 @@ export class TimelineOverview extends React.Component{ onScrubberDown = (e: React.PointerEvent) => { e.preventDefault(); e.stopPropagation(); - // if (!this.props.isAuthoring) { document.removeEventListener("pointermove", this.onScrubberMove); document.removeEventListener("pointerup", this.onScrubberUp); document.addEventListener("pointermove", this.onScrubberMove); document.addEventListener("pointerup", this.onScrubberUp); - // } } @action @@ -110,7 +113,7 @@ export class TimelineOverview extends React.Component{ const scrubberRef = this._scrubberRef.current!; const left = scrubberRef.getBoundingClientRect().left; const offsetX = Math.round(e.clientX - left); - this.props.changeCurrentBarX((((offsetX) / this.overviewBarWidth) * this.props.totalLength) + this.props.currentBarX); + this.props.changeCurrentBarX((((offsetX) / this.activeOverviewWidth) * this.props.totalLength) + this.props.currentBarX); } @action @@ -129,38 +132,34 @@ export class TimelineOverview extends React.Component{ this.visibleTime = vis; this.currentX = x; this.visibleStart = start; - // console.log("getting times") - // console.log(x) - // console.log(start) } render() { - console.log("RERENDERED!"); this.setOverviewWidth(); this.getTimes(); const percentVisible = this.visibleTime / this.props.time; - const visibleBarWidth = percentVisible * this.overviewBarWidth; + const visibleBarWidth = percentVisible * this.activeOverviewWidth; const percentScrubberStart = this.currentX / this.props.time; - const scrubberStart = percentScrubberStart * this.overviewBarWidth; + const scrubberStart = this.props.currentBarX / this.props.totalLength * this.activeOverviewWidth; const percentBarStart = this.visibleStart / this.props.time; - const barStart = percentBarStart * this.overviewBarWidth; + const barStart = percentBarStart * this.activeOverviewWidth; const timeline = this.props.isAuthoring ? [ -
    +
    , -
    +
    ] : [ -
    -
    +
    +
    , -
    +
    ]; return (
    -- cgit v1.2.3-70-g09d2 From 8d28c35bb9cf6ec190dbfed2e2cda8b5d1454efb Mon Sep 17 00:00:00 2001 From: ab Date: Sun, 9 Feb 2020 16:15:28 -0500 Subject: some changes --- src/client/views/animationtimeline/Timeline.tsx | 6 ++++-- src/client/views/animationtimeline/Track.tsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 42bb02e85..9e90cc764 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -539,8 +539,10 @@ export class Timeline extends React.Component { tracks = () => { console.log(this.mapOfTracks.length); this.mapOfTracks.forEach(track => { - if (track !== null) { - track.getLastRegion(); + console.log(track); + if (track) { + const region = track.getLastRegion(); + console.log(region.time); } else { } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 620668db7..9f63b3562 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -82,7 +82,7 @@ export class Track extends React.Component { getLastRegion = () => { - + console.log(this.regions.length); console.log((this.regions[this.regions.length - 1] as Doc).time); return this.regions[this.regions.length - 1] as Doc; } -- cgit v1.2.3-70-g09d2 From d3f9765c98595788eaeb8b5c0ef52af1cd3adab3 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Sun, 9 Feb 2020 17:01:37 -0500 Subject: reset view done --- src/client/views/animationtimeline/Keyframe.scss | 4 ---- src/client/views/animationtimeline/Timeline.scss | 27 +++++++++++++++++----- src/client/views/animationtimeline/Timeline.tsx | 11 +++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 25 ++++++++++++++++---- src/new_fields/Doc.ts | 19 +++++++++++++++ 5 files changed, 68 insertions(+), 18 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.scss b/src/client/views/animationtimeline/Keyframe.scss index 8dcf71994..84c8de287 100644 --- a/src/client/views/animationtimeline/Keyframe.scss +++ b/src/client/views/animationtimeline/Keyframe.scss @@ -46,10 +46,6 @@ $timelineDark: #77a1aa; position: absolute; } - // .keyframe:hover~.keyframe-information { - // display: flex; - // } - .keyframe-information { display: none; position: relative; diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index a99d017f4..f90249771 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -34,29 +34,44 @@ $timelineDark: #77a1aa; } .grid-box { - display: grid; - grid-template-columns: [first] 50% [line2] 25% [line3] 25%; + display: flex; + // grid-template-columns: [first] 20% [line2] 20% [line3] 60%; width: calc(100% - 150px); // width: 100%; margin-left: 10px; .time-box { - margin-left: 5px; + margin-left: 10px; min-width: 140px; display: flex; justify-content: center; align-items: center; - // grid-column-start: line2; + + .resetView-tool { + width: 30px; + height: 30px; + display: flex; + justify-content: center; + align-items: center; + margin: 3px; + color: $timelineDark; + } + + .resetView-tool:hover { + -webkit-transform: scale(1.1); + -ms-transform: scale(1.1); + transform: scale(1.1); + transition: .2s ease; + } } .mode-box { display: flex; margin-left: 5px; - // grid-column-start: line3; } .overview-box { - width: 100%; + width: 80%; display: flex; } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 3942499c9..256b98ffa 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -459,6 +459,9 @@ export class Timeline extends React.Component {
    {this.timeIndicator(lengthString)} +
    Doc.resetView(this.props.Document)}>
    +
    Doc.setView(this.props.Document)}>
    +
    @@ -477,8 +480,8 @@ export class Timeline extends React.Component { else { return (
    -
    {`Current: ${this.getCurrentTime()}`}
    -
    {`Total: 1:40.07`}
    +
    {`Current: ${this.getCurrentTime()}`}
    +
    {`Total: 1:40.07`}
    ); } @@ -539,7 +542,7 @@ export class Timeline extends React.Component { @action.bound - changeLenths() { + changeLengths() { if (this._infoContainer.current) { this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts @@ -572,7 +575,7 @@ export class Timeline extends React.Component { render() { runInAction(() => { this._panelWidth = this.props.PanelWidth(); - this.changeLenths(); + this.changeLengths(); }); // change visible and total width diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ef1ec5e78..81fca3b54 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -966,7 +966,24 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onContextMenu = (e: React.MouseEvent) => { const layoutItems: ContextMenuProps[] = []; - layoutItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); + const { Document } = this.props; + + layoutItems.push({ + description: "reset view", event: () => { + Doc.resetView(Document); + }, icon: "compress-arrows-alt" + }); + layoutItems.push({ + description: "set view origin", event: () => { + Doc.setView(Document); + }, icon: "expand-arrows-alt" + }); + layoutItems.push({ + description: "reset view to origin", event: () => { + Doc.resetViewToOrigin(Document); + }, icon: "expand-arrows-alt" + }); + layoutItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); layoutItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); layoutItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); @@ -1018,7 +1035,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { icon: "eye" }); ContextMenu.Instance.addItem({ description: "Freeform Options ...", subitems: layoutItems, icon: "eye" }); - this._timelineRef.current!.timelineContextMenu(e); + this._timelineRef.current!.timelineContextMenu(e); } @@ -1081,12 +1098,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { transformOrigin: this.contentScaling ? "left top" : "", width: this.contentScaling ? `${100 / this.contentScaling}%` : "", height: this.contentScaling ? `${100 / this.contentScaling}%` : this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() - }}> + }}> {/* */} {!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? // && this.props.CollectionView && lodarea < NumCast(this.Document.LODarea, 100000) ? this.placeholder : this.marqueeView} -
    +
    ; } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index f230abaf4..216005697 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -255,6 +255,25 @@ export namespace Doc { // return Cast(field, ctor); // }); // } + + export function resetView(doc: Doc) { + doc._panX = doc._customOriginX ?? 0; + doc._panY = doc._customOriginY ?? 0; + doc.scale = doc._customOriginScale ?? 1; + } + + export function resetViewToOrigin(doc: Doc) { + doc._panX = 0; + doc._panY = 0; + doc.scale = 1; + } + + export function setView(doc: Doc) { + doc._customOriginX = doc._panX; + doc._customOriginY = doc._panY; + doc._customOriginScale = doc.scale; + } + export function RunCachedUpdate(doc: Doc, field: string) { const update = doc[CachedUpdates][field]; if (update) { -- cgit v1.2.3-70-g09d2 From cb140cb5387836de1e2287ed9519a132f0f9d28f Mon Sep 17 00:00:00 2001 From: ab Date: Sun, 9 Feb 2020 18:10:26 -0500 Subject: kinda works --- src/client/views/animationtimeline/Timeline.tsx | 42 +++++++++++++++++----- .../views/animationtimeline/TimelineOverview.tsx | 8 +++-- src/client/views/animationtimeline/Track.tsx | 15 +++++--- 3 files changed, 50 insertions(+), 15 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index fc1a0ec3b..c52669879 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -14,6 +14,7 @@ import { TimelineOverview } from "./TimelineOverview"; import { FieldViewProps } from "../nodes/FieldView"; import { KeyframeFunc } from "./Keyframe"; import { Utils } from "../../../Utils"; +import { createPromiseCapability } from "../../../../deploy/assets/pdf.worker"; /** @@ -470,7 +471,7 @@ export class Timeline extends React.Component { return ( <>
    {lengthString}
    -
    1:40.07
    +
    {this.toReadTime(this._time)}
    ); } @@ -478,7 +479,7 @@ export class Timeline extends React.Component { return (
    {`Current: ${this.getCurrentTime()}`}
    -
    {`Total: 1:40.07`}
    +
    {`Total: ${this.toReadTime(this._time)}`}
    ); } @@ -524,7 +525,7 @@ export class Timeline extends React.Component { timelineContainer.style.top = `${-this._containerHeight}px`; this.props.Document.isATOn = false; this._isAuthoring = false; - this.tracks(); + this.toPlay(); } else { //turning on authoring mode... roundToggle.style.transform = "translate(20px, 0px)"; @@ -534,6 +535,7 @@ export class Timeline extends React.Component { timelineContainer.style.top = "0px"; this.props.Document.isATOn = true; this._isAuthoring = true; + this.toAuthoring(); } } @@ -555,18 +557,40 @@ export class Timeline extends React.Component { @observable private mapOfTracks: (Track | null)[] = []; @action - tracks = () => { - console.log(this.mapOfTracks.length); + findLongestTime = () => { + let longestTime:number = 0; this.mapOfTracks.forEach(track => { - console.log(track); if (track) { - const region = track.getLastRegion(); - console.log(region.time); + const lastTime = track.getLastRegionTime(); + if (this.children.length !== 0 ){ + if (longestTime <= lastTime){ + longestTime = lastTime; + } + } } else { - + //TODO: remove undefineds and duplicates } }); + return longestTime; + } + + @action + toAuthoring = () => { + let longestTime = this.findLongestTime(); + if (longestTime === 0) longestTime = 1; + const adjustedTime = Math.ceil(longestTime / 100000) * 100000; + console.log(adjustedTime); + this._totalLength = KeyframeFunc.convertPixelTime(adjustedTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); + this._time = adjustedTime; + } + + @action + toPlay = () => { + const longestTime = this.findLongestTime(); + this._time = longestTime; + this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); } + /** * if you have any question here, just shoot me an email or text. * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 94bbe7074..a587e1138 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -142,11 +142,15 @@ export class TimelineOverview extends React.Component{ const visibleBarWidth = percentVisible * this.activeOverviewWidth; const percentScrubberStart = this.currentX / this.props.time; - const scrubberStart = this.props.currentBarX / this.props.totalLength * this.activeOverviewWidth; + let scrubberStart = this.props.currentBarX / this.props.totalLength * this.activeOverviewWidth; + if (scrubberStart > this.activeOverviewWidth) scrubberStart = this.activeOverviewWidth; const percentBarStart = this.visibleStart / this.props.time; const barStart = percentBarStart * this.activeOverviewWidth; + let playWidth = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth; + if (playWidth > this.activeOverviewWidth) playWidth = this.activeOverviewWidth; + const timeline = this.props.isAuthoring ? [
    @@ -159,7 +163,7 @@ export class TimelineOverview extends React.Component{
    , -
    +
    ]; return (
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 9f63b3562..69e65fbcf 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -81,10 +81,17 @@ export class Track extends React.Component { //////////////////////////////// - getLastRegion = () => { - console.log(this.regions.length); - console.log((this.regions[this.regions.length - 1] as Doc).time); - return this.regions[this.regions.length - 1] as Doc; + getLastRegionTime = () => { + let lastTime:number = 0; + let lastRegion:(Doc | undefined); + DocListCast(this.regions).forEach(region => { + const time = NumCast(region.time); + if (lastTime <= time) { + lastTime = time; + lastRegion = region; + } + }); + return lastRegion ? lastTime + NumCast(lastRegion.duration) : 0; } /** -- cgit v1.2.3-70-g09d2 From d9990be832185b31375aaeb458c21addb389c32e Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Mon, 10 Feb 2020 20:30:59 -0500 Subject: fixed time --- package-lock.json | 2 +- src/client/views/animationtimeline/Timeline.tsx | 1 + src/client/views/animationtimeline/Track.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/package-lock.json b/package-lock.json index 5684789c1..0ae430be9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17991,7 +17991,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { "string-width": "^1.0.1", diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 914c19b53..d27990bd6 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -574,6 +574,7 @@ export class Timeline extends React.Component { //TODO: remove undefineds and duplicates } }); + console.log(longestTime); return longestTime; } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 69e65fbcf..705cc33a2 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -85,7 +85,7 @@ export class Track extends React.Component { let lastTime:number = 0; let lastRegion:(Doc | undefined); DocListCast(this.regions).forEach(region => { - const time = NumCast(region.time); + const time = NumCast(region.position); if (lastTime <= time) { lastTime = time; lastRegion = region; -- cgit v1.2.3-70-g09d2 From 77ddee9b0d5697522894badc9df1d54a0a7f08c9 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Mon, 10 Feb 2020 20:38:37 -0500 Subject: fixed width --- src/client/views/animationtimeline/Timeline.tsx | 32 +++++++++++----------- .../views/animationtimeline/TimelineOverview.tsx | 16 ++++++----- 2 files changed, 25 insertions(+), 23 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index d27990bd6..b1777bfa0 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -538,7 +538,7 @@ export class Timeline extends React.Component { timelineContainer.style.top = "0px"; this.props.Document.isATOn = true; this._isAuthoring = true; - this.toAuthoring(); + this.toAuthoring(); } } @@ -561,37 +561,37 @@ export class Timeline extends React.Component { @action findLongestTime = () => { - let longestTime:number = 0; + let longestTime: number = 0; this.mapOfTracks.forEach(track => { if (track) { const lastTime = track.getLastRegionTime(); - if (this.children.length !== 0 ){ - if (longestTime <= lastTime){ - longestTime = lastTime; + if (this.children.length !== 0) { + if (longestTime <= lastTime) { + longestTime = lastTime; } } } else { //TODO: remove undefineds and duplicates } }); - console.log(longestTime); - return longestTime; + // console.log(longestTime); + return longestTime; } - @action + @action toAuthoring = () => { - let longestTime = this.findLongestTime(); + let longestTime = this.findLongestTime(); if (longestTime === 0) longestTime = 1; - const adjustedTime = Math.ceil(longestTime / 100000) * 100000; - console.log(adjustedTime); - this._totalLength = KeyframeFunc.convertPixelTime(adjustedTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); - this._time = adjustedTime; + const adjustedTime = Math.ceil(longestTime / 100000) * 100000; + // console.log(adjustedTime); + this._totalLength = KeyframeFunc.convertPixelTime(adjustedTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); + this._time = adjustedTime; } - @action + @action toPlay = () => { - const longestTime = this.findLongestTime(); - this._time = longestTime; + const longestTime = this.findLongestTime(); + this._time = longestTime; this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); } diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index a587e1138..e3a276737 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -26,6 +26,8 @@ interface TimelineOverviewProps { export class TimelineOverview extends React.Component{ @observable private _visibleRef = React.createRef(); @observable private _scrubberRef = React.createRef(); + @observable private authoringContainer = React.createRef(); + @observable private playbackContainer = React.createRef(); @observable private overviewBarWidth: number = 0; @observable private playbarWidth: number = 0; @observable private activeOverviewWidth: number = 0; @@ -57,8 +59,8 @@ export class TimelineOverview extends React.Component{ @action setOverviewWidth() { - const width1 = $("#timelineOverview").width(); - const width2 = $("#timelinePlay").width(); + const width1 = this.authoringContainer.current?.clientWidth; + const width2 = this.playbackContainer.current?.clientWidth; if (width1 && width1 !== 0) this.overviewBarWidth = width1; if (width2 && width2 !== 0) this.playbarWidth = width2; @@ -143,24 +145,24 @@ export class TimelineOverview extends React.Component{ const percentScrubberStart = this.currentX / this.props.time; let scrubberStart = this.props.currentBarX / this.props.totalLength * this.activeOverviewWidth; - if (scrubberStart > this.activeOverviewWidth) scrubberStart = this.activeOverviewWidth; + if (scrubberStart > this.activeOverviewWidth) scrubberStart = this.activeOverviewWidth; const percentBarStart = this.visibleStart / this.props.time; const barStart = percentBarStart * this.activeOverviewWidth; - let playWidth = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth; - if (playWidth > this.activeOverviewWidth) playWidth = this.activeOverviewWidth; + let playWidth = (this.props.currentBarX / this.props.totalLength) * this.activeOverviewWidth; + if (playWidth > this.activeOverviewWidth) playWidth = this.activeOverviewWidth; const timeline = this.props.isAuthoring ? [ -
    +
    ,
    ] : [ -
    +
    ,
    -- cgit v1.2.3-70-g09d2 From 02c8bdb41bc7daf8268fde3fe85a9e2f237c772d Mon Sep 17 00:00:00 2001 From: monikahedman Date: Mon, 10 Feb 2020 20:53:45 -0500 Subject: time fixed --- src/client/views/animationtimeline/Timeline.tsx | 38 +++++++++++-------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index b1777bfa0..2b0525c6e 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -412,7 +412,7 @@ export class Timeline extends React.Component { /** * tool box includes the toggle buttons at the top of the timeline (both editing mode and play mode) */ - private timelineToolBox = (scale: number) => { + private timelineToolBox = (scale: number, totalTime: number) => { const size = 40 * scale; //50 is default const iconSize = 25; @@ -459,7 +459,7 @@ export class Timeline extends React.Component {
    - {this.timeIndicator(lengthString)} + {this.timeIndicator(lengthString, totalTime)}
    Doc.resetView(this.props.Document)}>
    Doc.setView(this.props.Document)}>
    @@ -469,12 +469,11 @@ export class Timeline extends React.Component { ); } - timeIndicator(lengthString: string) { + timeIndicator(lengthString: string, totalTime: number) { if (this.props.Document.isATOn) { return ( <> -
    {lengthString}
    -
    {this.toReadTime(this._time)}
    +
    {`Total: ${this.toReadTime(totalTime)}`}
    ); } @@ -482,26 +481,12 @@ export class Timeline extends React.Component { return (
    {`Current: ${this.getCurrentTime()}`}
    -
    {`Total:${this.toReadTime(this._time)}`}
    +
    {`Total: ${this.toReadTime(this._time)}`}
    ); } } - /** - * manual time input (kinda broken right now) - */ - @action - private onTimeInput = (e: React.KeyboardEvent) => { - // if (e.keyCode === 13) { - // let timeInput = this._timeInputRef.current!; - // this._time = parseInt(timeInput.value, 10); - // this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement); - // this.props.Document.AnimationLength = this._time; - // } - } - - /** * when the user decides to click the toggle button (either user wants to enter editing mode or play mode) */ @@ -553,7 +538,10 @@ export class Timeline extends React.Component { // @computed getCurrentTime = () => { - const current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); + let current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement); + if (current > this._time) { + current = this._time; + } return this.toReadTime(current); } @@ -603,8 +591,14 @@ export class Timeline extends React.Component { runInAction(() => { this._panelWidth = this.props.PanelWidth(); this.changeLengths(); + // this.toPlay(); + + + // this._time = longestTime; }); + const longestTime = this.findLongestTime(); + // change visible and total width return (
    @@ -632,7 +626,7 @@ export class Timeline extends React.Component {
    - {this.timelineToolBox(1)} + {this.timelineToolBox(1, longestTime)}
    ); -- cgit v1.2.3-70-g09d2 From 4ecf08b5c5cdc4ddb3a997e2f3a2188e921ff430 Mon Sep 17 00:00:00 2001 From: monikahedman Date: Tue, 11 Feb 2020 19:29:19 -0500 Subject: number jitter fixed --- src/client/views/animationtimeline/Timeline.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 2b0525c6e..e9caa2b2a 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -329,17 +329,15 @@ export class Timeline extends React.Component { time = time / 1000; const inSeconds = Math.round(time * 100) / 100; - // console.log(inSeconds) - // var inSeconds = parseFloat(time.toFixed(2)); - // const inSeconds = (Math.floor(time) / 1000); const min: (string | number) = Math.floor(inSeconds / 60); - let sec: (string | number) = (Math.round((inSeconds % 60) * 100) / 100); + const sec: (string | number) = (Math.round((inSeconds % 60) * 100) / 100); + let secString = sec.toFixed(2); if (Math.floor(sec / 10) === 0) { - sec = "0" + sec; + secString = "0" + secString; } - // sec = Number.parseFloat(sec).toFixed(2); - return `${min}:${sec}`; + + return `${min}:${secString}`; } -- cgit v1.2.3-70-g09d2 From 0fb0341287cfed4c5b846f6bc7126623599b34f5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 28 Apr 2020 20:16:45 -0400 Subject: small timeline fixes --- src/client/views/animationtimeline/Keyframe.tsx | 103 ++++++++++----------- src/client/views/animationtimeline/Timeline.tsx | 16 +--- .../views/animationtimeline/TimelineOverview.tsx | 8 +- src/client/views/animationtimeline/Track.tsx | 60 ++++++------ 4 files changed, 92 insertions(+), 95 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index bb557289e..76c3e63be 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -3,7 +3,7 @@ import "./Keyframe.scss"; import "./Timeline.scss"; import "../globalCssVariables.scss"; import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction } from "mobx"; +import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx"; import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { Cast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; @@ -13,6 +13,7 @@ import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { emptyPath } from "../../../Utils"; @@ -32,10 +33,10 @@ export namespace KeyframeFunc { right = "right" } - export const findAdjacentRegion = (dir: KeyframeFunc.Direction, currentRegion: Doc, regions: List): (RegionData | undefined) => { + export const findAdjacentRegion = (dir: KeyframeFunc.Direction, currentRegion: Doc, regions: Doc[]): (RegionData | undefined) => { let leftMost: (RegionData | undefined) = undefined; let rightMost: (RegionData | undefined) = undefined; - DocListCast(regions).forEach(region => { + regions.forEach(region => { const neighbor = RegionData(region); if (currentRegion.position! > neighbor.position) { if (!leftMost || neighbor.position > leftMost.position) { @@ -135,7 +136,7 @@ interface IProps { time: number; changeCurrentBarX: (x: number) => void; transform: Transform; - makeKeyData: (region:RegionData, pos: number, kftype:KeyframeFunc.KeyframeType) => Doc; + makeKeyData: (region: RegionData, pos: number, kftype: KeyframeFunc.KeyframeType) => Doc; } @@ -169,21 +170,24 @@ export class Keyframe extends React.Component { @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; - @computed private get regiondata() { return RegionData(this.regions[this.regions.indexOf(this.props.RegionData)] as Doc); } - @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } + @computed private get regiondata() { return RegionData(this.props.RegionData); } + @computed private get regions() { return DocListCast(this.props.node.regions); } @computed private get keyframes() { return DocListCast(this.regiondata.keyframes); } @computed private get pixelPosition() { return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelDuration() { return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeIn() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get pixelFadeOut() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } - + + constructor(props: any) { + super(props); + } componentDidMount() { setTimeout(() => { //giving it a temporary 1sec delay... - if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); - const start = this.props.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); + if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); + const start = this.props.makeKeyData(this.regiondata, this.regiondata.position, KeyframeFunc.KeyframeType.end); const fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade); const fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade); - const finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration,KeyframeFunc.KeyframeType.end); + const finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.end); (fadeIn.key as Doc).opacity = 1; (fadeOut.key as Doc).opacity = 1; (start.key as Doc).opacity = 0.1; @@ -192,9 +196,6 @@ export class Keyframe extends React.Component { }, 1000); } - - - @action onBarPointerDown = (e: React.PointerEvent) => { e.preventDefault(); @@ -316,7 +317,7 @@ export class Keyframe extends React.Component { if (offset > this.regiondata.fadeIn && offset < this.regiondata.duration - this.regiondata.fadeOut) { //make sure keyframe is not created inbetween fades and ends const position = this.regiondata.position; this.props.makeKeyData(this.regiondata, Math.round(position + offset), KeyframeFunc.KeyframeType.default); - this.regiondata.hasData = true; + this.regiondata.hasData = true; this.props.changeCurrentBarX(KeyframeFunc.convertPixelTime(Math.round(position + offset), "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement)); //first move the keyframe to the correct location and make a copy so the correct file gets coppied } @@ -338,7 +339,7 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.addItem("button", "Show Data", () => { runInAction(() => { const kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { _width: 300, _height: 300 }); - CollectionDockingView.AddRightSplit(kvp, (kf.key as Doc).data as Doc); + CollectionDockingView.AddRightSplit(kvp, emptyPath); }); }), TimelineMenu.Instance.addItem("button", "Delete", () => { @@ -403,7 +404,7 @@ export class Keyframe extends React.Component { runInAction(() => { const prevPosition = this.regiondata.position; let cannotMove: boolean = false; - DocListCast(this.regions).forEach(region => { + this.regions.forEach(region => { if (NumCast(region.position) !== this.regiondata.position) { if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration)))) { cannotMove = true; @@ -419,7 +420,7 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => { runInAction(() => { let cannotMove: boolean = false; - DocListCast(this.regions).forEach(region => { + this.regions.forEach(region => { if (NumCast(region.position) !== this.regiondata.position) { val += this.regiondata.position; if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration))) { @@ -479,34 +480,31 @@ export class Keyframe extends React.Component { * drawing keyframe. Handles both keyframe with a circle (one that you create by double clicking) and one without circle (fades) * this probably needs biggest change, since everyone expected all keyframes to have a circle (and draggable) */ - @action drawKeyframes = () => { const keyframeDivs: JSX.Element[] = []; - DocListCast(this.regiondata.keyframes).forEach(kf => { + return DocListCast(this.regiondata.keyframes).map(kf => { if (kf.type as KeyframeFunc.KeyframeType !== KeyframeFunc.KeyframeType.end) { - keyframeDivs.push( - <>
    -
    -
    { e.preventDefault(); e.stopPropagation(); this.moveKeyframe(e, kf); }} onContextMenu={(e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - this.makeKeyframeMenu(kf, e.nativeEvent); - }} onDoubleClick={(e) => { e.preventDefault(); e.stopPropagation(); }}>
    - -
    -
    - - - ); - } else { - keyframeDivs.push( + return <>
    +
    { e.preventDefault(); e.stopPropagation(); this.moveKeyframe(e, kf); }} + onContextMenu={(e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + this.makeKeyframeMenu(kf, e.nativeEvent); + }} + onDoubleClick={(e) => { e.preventDefault(); e.stopPropagation(); }}> +
    - ); +
    + ; + } else { + return
    +
    +
    } }); - return keyframeDivs; } /** @@ -546,22 +544,23 @@ export class Keyframe extends React.Component { */ //154, 206, 223 render() { + trace(); + console.log(this.props.RegionData.position); + console.log(this.regiondata.position); + console.log(this.pixelPosition); return ( -
    -
    -
    - {/*
    */} -
    - {/*
    */} - {this.drawKeyframes()} - {this.drawKeyframeDividers()} -
    +
    +
    + {/*
    */} +
    + {/*
    */} + {this.drawKeyframes()} + {this.drawKeyframeDividers()}
    ); } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index e9caa2b2a..7d245ab6f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -3,7 +3,7 @@ import "./Timeline.scss"; import { listSpec } from "../../../new_fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; -import { observable, action, computed, runInAction, IReactionDisposer, reaction } from "mobx"; +import { observable, action, computed, runInAction, IReactionDisposer, reaction, trace } from "mobx"; import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { Doc, DocListCast } from "../../../new_fields/Doc"; @@ -14,8 +14,6 @@ import { TimelineOverview } from "./TimelineOverview"; import { FieldViewProps } from "../nodes/FieldView"; import { KeyframeFunc } from "./Keyframe"; import { Utils } from "../../../Utils"; -import { createPromiseCapability } from "../../../../deploy/assets/pdf.worker"; - /** * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are @@ -80,7 +78,6 @@ export class Timeline extends React.Component { // so a reaction can be made @observable public _isAuthoring = this.props.Document.isATOn; - @observable private _panelWidth = 0; /** * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit. @@ -90,7 +87,7 @@ export class Timeline extends React.Component { const 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; + return Cast((Cast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + "-annotations"], Doc) as Doc).annotations, listSpec(Doc)) as List; } else { return new List(); } @@ -586,17 +583,14 @@ export class Timeline extends React.Component { * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region) */ render() { - runInAction(() => { - this._panelWidth = this.props.PanelWidth(); + setTimeout(() => { this.changeLengths(); // this.toPlay(); - - // this._time = longestTime; - }); + }, 0); const longestTime = this.findLongestTime(); - + trace(); // change visible and total width return (
    diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index e3a276737..31e248823 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -156,16 +156,16 @@ export class TimelineOverview extends React.Component{ const timeline = this.props.isAuthoring ? [
    -
    , -
    +
    , +
    ] : [ -
    +
    , -
    +
    ]; return (
    diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 705cc33a2..0e3c209dc 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -29,7 +29,7 @@ export class Track extends React.Component { @observable private _currentBarXReaction: any; @observable private _timelineVisibleReaction: any; @observable private _autoKfReaction: any; - @observable private _newKeyframe: boolean = false; + @observable private _newKeyframe: boolean = false; private readonly MAX_TITLE_HEIGHT = 75; private _trackHeight = 0; private primitiveWhitelist = [ @@ -37,7 +37,7 @@ export class Track extends React.Component { "y", "width", "height", - "opacity", + "opacity", ]; private objectWhitelist = [ "data" @@ -81,17 +81,17 @@ export class Track extends React.Component { //////////////////////////////// - getLastRegionTime = () => { - let lastTime:number = 0; - let lastRegion:(Doc | undefined); + getLastRegionTime = () => { + let lastTime: number = 0; + let lastRegion: (Doc | undefined); DocListCast(this.regions).forEach(region => { - const time = NumCast(region.position); + const time = NumCast(region.position); if (lastTime <= time) { - lastTime = time; - lastRegion = region; + lastTime = time; + lastRegion = region; } - }); - return lastRegion ? lastTime + NumCast(lastRegion.duration) : 0; + }); + return lastRegion ? lastTime + NumCast(lastRegion.duration) : 0; } /** @@ -106,16 +106,16 @@ export class Track extends React.Component { let kf = keyframes[kfIndex] as Doc; //index in the keyframe if (this._newKeyframe) { console.log("new keyframe registering"); - let kfList = DocListCast(this.saveStateRegion!.keyframes); + let kfList = DocListCast(this.saveStateRegion!.keyframes); kfList.forEach(kf => { - kf.key = this.makeCopy(); - if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3){ - (kf.key as Doc).opacity = 0.1; - } else{ - (kf.key as Doc).opacity = 1; + kf.key = this.makeCopy(); + if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3) { + (kf.key as Doc).opacity = 0.1; + } else { + (kf.key as Doc).opacity = 1; } }); - this._newKeyframe = false; + this._newKeyframe = false; } if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades @@ -225,7 +225,7 @@ export class Track extends React.Component { } }); } else { - console.log("reverting state"); + console.log("reverting state"); //this.revertState(); } }); @@ -241,7 +241,7 @@ export class Track extends React.Component { timeChange = async () => { if (this.saveStateKf !== undefined) { await this.saveKeyframe(); - } else if (this._newKeyframe){ + } else if (this._newKeyframe) { await this.saveKeyframe(); } let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on @@ -250,7 +250,7 @@ export class Track extends React.Component { let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { - console.log("is current"); + console.log("is current"); await this.applyKeys(currentkf); this.saveStateKf = currentkf; this.saveStateRegion = regiondata; @@ -348,7 +348,7 @@ export class Track extends React.Component { createRegion = async (time: number) => { if (await this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data - + regiondata.position = time; //set position let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); @@ -356,9 +356,9 @@ export class Track extends React.Component { regiondata.duration = rightRegion.position - regiondata.position; } if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { - this.regions.push(regiondata); - this._newKeyframe = true; - this.saveStateRegion = regiondata; + this.regions.push(regiondata); + this._newKeyframe = true; + this.saveStateRegion = regiondata; return regiondata; } } @@ -395,7 +395,7 @@ export class Track extends React.Component { let doc = new Doc(); this.primitiveWhitelist.forEach(key => { let originalVal = this.props.node[key]; - if (key === "data"){ + if (key === "data") { console.log(originalVal); } doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; @@ -407,12 +407,16 @@ export class Track extends React.Component { * UI sstuff here. Not really much to change */ render() { + trace(); return (
    -
    { Doc.BrushDoc(this.props.node); }} onPointerOut={() => { Doc.UnBrushDoc(this.props.node); }} style={{ height: `${this._trackHeight}px` }}> - {DocListCast(this.regions).map((region) => { - return ; +
    Doc.BrushDoc(this.props.node)} + onPointerOut={() => Doc.UnBrushDoc(this.props.node)} > + {DocListCast(this.regions).map((region, i) => { + return ; })}
    -- cgit v1.2.3-70-g09d2 From ab4297e59e142f70b420831a73c3f3e4506eee15 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 28 Apr 2020 23:00:15 -0400 Subject: more animation timeline cleanup --- src/client/views/animationtimeline/Keyframe.tsx | 43 ++--- src/client/views/animationtimeline/Timeline.tsx | 41 ++-- src/client/views/animationtimeline/Track.tsx | 239 ++++++++++-------------- 3 files changed, 135 insertions(+), 188 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 76c3e63be..a3407f653 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -4,7 +4,7 @@ import "./Timeline.scss"; import "../globalCssVariables.scss"; import { observer } from "mobx-react"; import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx"; -import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc"; import { Cast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema"; @@ -55,11 +55,11 @@ export namespace KeyframeFunc { } }; - 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; + export const calcMinLeft = (region: Doc, currentBarX: number, ref?: Doc) => { //returns the time of the closet keyframe to the left + let leftKf: Opt; let time: number = 0; - const keyframes = await DocListCastAsync(region.keyframes!); - keyframes!.forEach((kf) => { + const keyframes = DocListCast(region.keyframes!); + keyframes.map((kf) => { let compTime = currentBarX; if (ref) compTime = NumCast(ref.time); if (NumCast(kf.time) < compTime && NumCast(kf.time) >= time) { @@ -71,11 +71,10 @@ export namespace KeyframeFunc { }; - 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; + export const calcMinRight = (region: Doc, currentBarX: number, ref?: Doc) => { //returns the time of the closest keyframe to the right + let rightKf: Opt; let time: number = Infinity; - const keyframes = await DocListCastAsync(region.keyframes!); - keyframes!.forEach((kf) => { + DocListCast(region.keyframes!).forEach((kf) => { let compTime = currentBarX; if (ref) compTime = NumCast(ref.time); if (NumCast(kf.time) > compTime && NumCast(kf.time) <= NumCast(time)) { @@ -239,9 +238,7 @@ export class Keyframe extends React.Component { this.regiondata.position = futureX; } const movement = this.regiondata.position - prevX; - this.keyframes.forEach(kf => { - kf.time = NumCast(kf.time) + movement; - }); + this.keyframes.forEach(kf => kf.time = NumCast(kf.time) + movement); } @action @@ -338,7 +335,7 @@ export class Keyframe extends React.Component { makeKeyframeMenu = (kf: Doc, e: MouseEvent) => { TimelineMenu.Instance.addItem("button", "Show Data", () => { runInAction(() => { - const kvp = Docs.Create.KVPDocument(Cast(kf.key, Doc) as Doc, { _width: 300, _height: 300 }); + const kvp = Docs.Create.KVPDocument(kf, { _width: 300, _height: 300 }); CollectionDockingView.AddRightSplit(kvp, emptyPath); }); }), @@ -370,12 +367,8 @@ export class Keyframe extends React.Component { */ @action makeRegionMenu = (kf: Doc, e: MouseEvent) => { - TimelineMenu.Instance.addItem("button", "Remove Region", () => { - runInAction(() => { - this.regions.splice(this.regions.indexOf(this.props.RegionData), 1); - } - ); - }), + TimelineMenu.Instance.addItem("button", "Remove Region", () => + Cast(this.props.node.regions, listSpec(Doc))?.splice(this.regions.indexOf(this.props.RegionData), 1)), TimelineMenu.Instance.addItem("input", `fadeIn: ${this.regiondata.fadeIn}ms`, (val) => { runInAction(() => { let cannotMove: boolean = false; @@ -404,9 +397,9 @@ export class Keyframe extends React.Component { runInAction(() => { const prevPosition = this.regiondata.position; let cannotMove: boolean = false; - this.regions.forEach(region => { - if (NumCast(region.position) !== this.regiondata.position) { - if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration) || (this.regiondata.duration + val > NumCast(region.position) && this.regiondata.duration + val < NumCast(region.position) + NumCast(region.duration)))) { + this.regions.map(region => ({ pos: NumCast(region.position), dur: NumCast(region.duration) })).forEach(({ pos, dur }) => { + if (pos !== this.regiondata.position) { + if ((val < 0) || (val > pos && val < pos + dur || (this.regiondata.duration + val > pos && this.regiondata.duration + val < pos + dur))) { cannotMove = true; } } @@ -420,10 +413,10 @@ export class Keyframe extends React.Component { TimelineMenu.Instance.addItem("input", `duration: ${this.regiondata.duration}ms`, (val) => { runInAction(() => { let cannotMove: boolean = false; - this.regions.forEach(region => { - if (NumCast(region.position) !== this.regiondata.position) { + this.regions.map(region => ({ pos: NumCast(region.position), dur: NumCast(region.duration) })).forEach(({ pos, dur }) => { + if (pos !== this.regiondata.position) { val += this.regiondata.position; - if ((val < 0) || (val > NumCast(region.position) && val < NumCast(region.position) + NumCast(region.duration))) { + if ((val < 0) || (val > pos && val < pos + dur)) { cannotMove = true; } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 7d245ab6f..234d560f1 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -593,33 +593,30 @@ export class Timeline extends React.Component { trace(); // change visible and total width return ( -
    -
    -
    -
    -
    - {this.drawTicks()} -
    -
    -
    -
    - {DocListCast(this.children).map(doc => { - const track = { this.mapOfTracks.push(ref); }} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={this._timelineVisible} /> - return track; - })} -
    +
    +
    +
    +
    + {this.drawTicks()} +
    +
    -
    Current: {this.getCurrentTime()}
    -
    - {DocListCast(this.children).map(doc =>
    { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}>

    {doc.title}

    )} -
    -
    - +
    + {DocListCast(this.children).map(doc => + this.mapOfTracks.push(ref)} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={this._timelineVisible} /> + )}
    +
    Current: {this.getCurrentTime()}
    +
    + {DocListCast(this.children).map(doc =>
    { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}>

    {doc.title}

    )} +
    +
    + +
    - {this.timelineToolBox(1, longestTime)}
    + {this.timelineToolBox(1, longestTime)}
    ); } diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 0e3c209dc..9e2ec8886 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,15 +1,15 @@ -import * as React from "react"; +import { action, computed, intercept, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { observable, reaction, action, IReactionDisposer, computed, runInAction, autorun, toJS, isObservableArray, IObservableArray, trace, observe, intercept } 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 { Transform } from "../../util/Transform"; +import * as React from "react"; +import { Doc, DocListCast, Opt } from "../../../new_fields/Doc"; import { Copy } from "../../../new_fields/FieldSymbols"; +import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; +import { listSpec } from "../../../new_fields/Schema"; +import { Cast, NumCast } from "../../../new_fields/Types"; +import { Transform } from "../../util/Transform"; +import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; +import "./Track.scss"; interface IProps { node: Doc; @@ -43,48 +43,41 @@ export class Track extends React.Component { "data" ]; - @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List; } + @computed private get regions() { return DocListCast(this.props.node.regions); } @computed private get time() { return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } ////////// life cycle functions/////////////// componentWillMount() { - runInAction(() => { - if (!this.props.node.regions) this.props.node.regions = new List(); //if there is no region, then create new doc to store stuff - //these two lines are exactly same from timeline.tsx - let relativeHeight = window.innerHeight / 20; - this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness - }); + if (!this.props.node.regions) this.props.node.regions = new List(); //if there is no region, then create new doc to store stuff + //these two lines are exactly same from timeline.tsx + const relativeHeight = window.innerHeight / 20; + this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness } componentDidMount() { - runInAction(async () => { - this._timelineVisibleReaction = this.timelineVisibleReaction(); - this._currentBarXReaction = this.currentBarXReaction(); - if (this.regions.length === 0) this.createRegion(this.time); - this.props.node.hidden = false; - this.props.node.opacity = 1; - // this.autoCreateKeyframe(); - }); + this._timelineVisibleReaction = this.timelineVisibleReaction(); + this._currentBarXReaction = this.currentBarXReaction(); + if (this.regions.length === 0) this.createRegion(this.time); + this.props.node.hidden = false; + this.props.node.opacity = 1; + // this.autoCreateKeyframe(); } /** * mainly for disposing reactions */ componentWillUnmount() { - runInAction(() => { - //disposing reactions - if (this._currentBarXReaction) this._currentBarXReaction(); - if (this._timelineVisibleReaction) this._timelineVisibleReaction(); - if (this._autoKfReaction) this._autoKfReaction(); - }); + this._currentBarXReaction?.(); + this._timelineVisibleReaction?.(); + this._autoKfReaction?.(); } //////////////////////////////// getLastRegionTime = () => { let lastTime: number = 0; - let lastRegion: (Doc | undefined); - DocListCast(this.regions).forEach(region => { + let lastRegion: Opt; + this.regions.forEach(region => { const time = NumCast(region.position); if (lastTime <= time) { lastTime = time; @@ -100,41 +93,34 @@ export class Track extends React.Component { */ @action saveKeyframe = async () => { - console.log("saving keyframe"); - let keyframes: List = (Cast(this.saveStateRegion!.keyframes, listSpec(Doc)) as List); - let kfIndex: number = keyframes.indexOf(this.saveStateKf!); + let keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List; + let kfIndex = keyframes.indexOf(this.saveStateKf!); let kf = keyframes[kfIndex] as Doc; //index in the keyframe if (this._newKeyframe) { - console.log("new keyframe registering"); - let kfList = DocListCast(this.saveStateRegion!.keyframes); - kfList.forEach(kf => { - kf.key = this.makeCopy(); - if (kfList.indexOf(kf) === 0 || kfList.indexOf(kf) === 3) { - (kf.key as Doc).opacity = 0.1; - } else { - (kf.key as Doc).opacity = 1; - } + DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => { + this.copyDocDataToKeyFrame(kf); + kf.opacity = (index === 0 || index === 3) ? 0.1 : 1; }); this._newKeyframe = false; } if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades - kf.key = this.makeCopy(); - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists - if (leftkf!.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades - let edge: (Doc | undefined) = await KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf!); - edge!.key = this.makeCopy(); - leftkf!.key = this.makeCopy(); - (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; - (Cast(leftkf!.key, Doc)! as Doc).opacity = 1; + this.copyDocDataToKeyFrame(kf); + let leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists + let rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists + if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades + let edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf); + edge && this.copyDocDataToKeyFrame(edge); + leftkf && this.copyDocDataToKeyFrame(leftkf); + edge && (edge!.opacity = 0.1); + leftkf && (leftkf!.opacity = 1); } - if (rightkf!.type === KeyframeFunc.KeyframeType.fade) { - let edge: (Doc | undefined) = await KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf!); - edge!.key = this.makeCopy(); - rightkf!.key = this.makeCopy(); - (Cast(edge!.key, Doc)! as Doc).opacity = 0.1; - (Cast(rightkf!.key, Doc)! as Doc).opacity = 1; + if (rightkf?.type === KeyframeFunc.KeyframeType.fade) { + let edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf); + edge && this.copyDocDataToKeyFrame(edge); + rightkf && this.copyDocDataToKeyFrame(rightkf); + edge && (edge.opacity = 0.1); + rightkf && (rightkf.opacity = 1); } } keyframes[kfIndex] = kf; @@ -148,24 +134,22 @@ export class Track extends React.Component { */ @action autoCreateKeyframe = () => { - const { node } = this.props; - const objects = this.objectWhitelist.map(key => node[key]); + const objects = this.objectWhitelist.map(key => this.props.node[key]); intercept(this.props.node, change => { console.log(change); return change; }); return reaction(() => { - return [...this.primitiveWhitelist.map(key => node[key]), ...objects]; + return [...this.primitiveWhitelist.map(key => this.props.node[key]), ...objects]; }, (changed, reaction) => { //check for region - this.findRegion(this.time).then((region) => { - if (region !== undefined) { //if region at scrub time exist - let r = region as any as RegionData; //for some region is returning undefined... which is not the case - if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot - this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); - } + const region = this.findRegion(this.time); + if (region !== undefined) { //if region at scrub time exist + let r = region as RegionData; //for some region is returning undefined... which is not the case + if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot + this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); } - }); + } }, { fireImmediately: false }); } @@ -188,20 +172,19 @@ export class Track extends React.Component { @action currentBarXReaction = () => { return reaction(() => this.props.currentBarX, () => { - this.findRegion(this.time).then((regiondata: (Doc | undefined)) => { - if (regiondata) { - this.props.node.hidden = false; - // if (!this._autoKfReaction) { - // // console.log("creating another reaction"); - // // this._autoKfReaction = this.autoCreateKeyframe(); - // } - this.timeChange(); - } else { - this.props.node.hidden = true; - this.props.node.opacity = 0; - //if (this._autoKfReaction) this._autoKfReaction(); - } - }); + const regiondata = this.findRegion(this.time); + if (regiondata) { + this.props.node.hidden = false; + // if (!this._autoKfReaction) { + // // console.log("creating another reaction"); + // // this._autoKfReaction = this.autoCreateKeyframe(); + // } + this.timeChange(); + } else { + this.props.node.hidden = true; + this.props.node.opacity = 0; + //if (this._autoKfReaction) this._autoKfReaction(); + } }); } @@ -214,13 +197,11 @@ export class Track extends React.Component { return this.props.timelineVisible; }, isVisible => { if (isVisible) { - DocListCast(this.regions).forEach(region => { - if (!BoolCast((Cast(region, Doc) as Doc).hasData)) { - for (let i = 0; i < 4; i++) { - DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key = this.makeCopy(); - if (i === 0 || i === 3) { //manually inputing fades - (DocListCast(((Cast(region, Doc) as Doc).keyframes as List))[i].key! as Doc).opacity = 0.1; - } + this.regions.filter(region => !region.hasData).forEach(region => { + for (let i = 0; i < 4; i++) { + this.copyDocDataToKeyFrame(DocListCast(region.keyframes)[i]); + if (i === 0 || i === 3) { //manually inputing fades + DocListCast(region.keyframes)[i].opacity = 0.1; } } }); @@ -266,12 +247,11 @@ export class Track extends React.Component { */ @action private applyKeys = async (kf: Doc) => { - let kfNode = await Cast(kf.key, Doc) as Doc; this.primitiveWhitelist.forEach(key => { - if (!kfNode[key]) { + if (!kf[key]) { this.props.node[key] = undefined; } else { - let stored = kfNode[key]; + let stored = kf[key]; this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); @@ -297,16 +277,14 @@ export class Track extends React.Component { */ @action interpolate = async (left: Doc, right: Doc) => { - let leftNode = await (left.key) as Doc; - let rightNode = await (right.key) as Doc; this.primitiveWhitelist.forEach(key => { - if (leftNode[key] && rightNode[key] && typeof (leftNode[key]) === "number" && typeof (rightNode[key]) === "number") { //if it is number, interpolate - let dif = NumCast(rightNode[key]) - NumCast(leftNode[key]); + if (left[key] && right[key] && typeof (left[key]) === "number" && typeof (right[key]) === "number") { //if it is number, interpolate + let dif = NumCast(right[key]) - NumCast(left[key]); let deltaLeft = this.time - NumCast(left.time); let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); - this.props.node[key] = NumCast(leftNode[key]) + (dif * ratio); + this.props.node[key] = NumCast(left[key]) + (dif * ratio); } else { // case data - let stored = leftNode[key]; + let stored = left[key]; this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); @@ -316,17 +294,8 @@ export class Track extends React.Component { * finds region that corresponds to specific time (is there a region at this time?) * linear O(n) (maybe possible to optimize this with other Data structures?) */ - @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; + findRegion = (time: number) => { + return this.regions?.find(rd => (time >= NumCast(rd.position) && time <= (NumCast(rd.position) + NumCast(rd.duration)))); } @@ -345,8 +314,8 @@ export class Track extends React.Component { * creates a region (KEYFRAME.TSX stuff). */ @action - createRegion = async (time: number) => { - if (await this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) + createRegion = (time: number) => { + if (this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data regiondata.position = time; //set position @@ -356,7 +325,7 @@ export class Track extends React.Component { regiondata.duration = rightRegion.position - regiondata.position; } if (this.regions.length === 0 || !rightRegion || (rightRegion && rightRegion.position - regiondata.position >= NumCast(regiondata.fadeIn) + NumCast(regiondata.fadeOut))) { - this.regions.push(regiondata); + Cast(this.props.node.regions, listSpec(Doc))?.push(regiondata); this._newKeyframe = true; this.saveStateRegion = regiondata; return regiondata; @@ -366,48 +335,36 @@ export class Track extends React.Component { @action makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - let doclist = DocListCast(regiondata.keyframes)!; - let existingkf: (Doc | undefined) = undefined; - doclist.forEach(TK => { - if (TK.time === time) existingkf = TK; - }); + const trackKeyFrames = DocListCast(regiondata.keyframes)!; + const existingkf = trackKeyFrames.find(TK => TK.time === time); if (existingkf) return existingkf; //else creates a new doc. - let TK: Doc = new Doc(); - TK.time = time; - TK.key = this.makeCopy(); - TK.type = type; + const newKeyFrame: Doc = new Doc(); + newKeyFrame.time = time; + newKeyFrame.type = type; + this.copyDocDataToKeyFrame(newKeyFrame); //assuming there are already keyframes (for keeping keyframes in order, sorted by time) - if (doclist.length === 0) regiondata.keyframes!.push(TK); - doclist.forEach(kf => { - let index = doclist.indexOf(kf); - let kfTime = NumCast(kf.time); - if ((kfTime < time && index === doclist.length - 1) || (kfTime < time && time < NumCast(doclist[index + 1].time))) { - regiondata.keyframes!.splice(index + 1, 0, TK); - return; + if (trackKeyFrames.length === 0) regiondata.keyframes!.push(newKeyFrame); + trackKeyFrames.map(kf => NumCast(kf.time)).forEach((kfTime, index) => { + if ((kfTime < time && index === trackKeyFrames.length - 1) || (kfTime < time && time < NumCast(trackKeyFrames[index + 1].time))) { + regiondata.keyframes!.splice(index + 1, 0, newKeyFrame); } }); - return TK; + return newKeyFrame; } @action - makeCopy = () => { - let doc = new Doc(); - this.primitiveWhitelist.forEach(key => { - let originalVal = this.props.node[key]; - if (key === "data") { - console.log(originalVal); - } - doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : this.props.node[key]; + copyDocDataToKeyFrame = (doc: Doc) => { + this.primitiveWhitelist.map(key => { + const originalVal = this.props.node[key]; + doc[key] = originalVal instanceof ObjectField ? originalVal[Copy]() : originalVal; }); - return doc; } /** * UI sstuff here. Not really much to change */ render() { - trace(); return (
    @@ -415,7 +372,7 @@ export class Track extends React.Component { onDoubleClick={this.onInnerDoubleClick} onPointerOver={() => Doc.BrushDoc(this.props.node)} onPointerOut={() => Doc.UnBrushDoc(this.props.node)} > - {DocListCast(this.regions).map((region, i) => { + {this.regions?.map((region, i) => { return ; })}
    -- cgit v1.2.3-70-g09d2 From c0ec0efe6fb03096c9abbee9e5d72e8cab54bf6b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 28 Apr 2020 23:02:23 -0400 Subject: from last --- src/client/views/animationtimeline/Timeline.tsx | 4 +--- src/client/views/animationtimeline/Track.tsx | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 234d560f1..295c1e18c 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -467,9 +467,7 @@ export class Timeline extends React.Component { timeIndicator(lengthString: string, totalTime: number) { if (this.props.Document.isATOn) { return ( - <> -
    {`Total: ${this.toReadTime(totalTime)}`}
    - +
    {`Total: ${this.toReadTime(totalTime)}`}
    ); } else { diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 9e2ec8886..382bf9b64 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -35,8 +35,8 @@ export class Track extends React.Component { private primitiveWhitelist = [ "x", "y", - "width", - "height", + "_width", + "_height", "opacity", ]; private objectWhitelist = [ -- cgit v1.2.3-70-g09d2 From 182aa5efffd0d68af7cf57e51fd54c889d7564ae Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 29 Apr 2020 00:40:09 -0400 Subject: fixed timeline's Track initialization --- src/client/views/animationtimeline/Track.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 382bf9b64..79eb60fae 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,7 +1,7 @@ import { action, computed, intercept, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; -import { Doc, DocListCast, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../new_fields/Doc"; import { Copy } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { ObjectField } from "../../../new_fields/ObjectField"; @@ -46,18 +46,15 @@ export class Track extends React.Component { @computed private get regions() { return DocListCast(this.props.node.regions); } @computed private get time() { return NumCast(KeyframeFunc.convertPixelTime(this.props.currentBarX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } - ////////// life cycle functions/////////////// - componentWillMount() { - if (!this.props.node.regions) this.props.node.regions = new List(); //if there is no region, then create new doc to store stuff + async componentDidMount() { + const regions = await DocListCastAsync(this.props.node.regions); + if (!regions) this.props.node.regions = new List(); //if there is no region, then create new doc to store stuff //these two lines are exactly same from timeline.tsx const relativeHeight = window.innerHeight / 20; this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness - } - - componentDidMount() { this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); - if (this.regions.length === 0) this.createRegion(this.time); + if (DocListCast(this.props.node.regions).length === 0) this.createRegion(this.time); this.props.node.hidden = false; this.props.node.opacity = 1; // this.autoCreateKeyframe(); -- cgit v1.2.3-70-g09d2 From f78d614f99fed4555f7a1211e522e34efc19f809 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 29 Apr 2020 11:53:55 -0400 Subject: cleanup of timeline before merge --- src/client/views/animationtimeline/Timeline.tsx | 16 +- src/client/views/linking/LinkFollowBox.tsx | 571 ------------------------ src/new_fields/Doc.ts | 18 - 3 files changed, 14 insertions(+), 591 deletions(-) delete mode 100644 src/client/views/linking/LinkFollowBox.tsx (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 295c1e18c..677267ca0 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -372,6 +372,18 @@ export class Timeline extends React.Component { this.changeCurrentBarX(currCurrent); } + + resetView(doc: Doc) { + doc._panX = doc._customOriginX ?? 0; + doc._panY = doc._customOriginY ?? 0; + doc.scale = doc._customOriginScale ?? 1; + } + + setView(doc: Doc) { + doc._customOriginX = doc._panX; + doc._customOriginY = doc._panY; + doc._customOriginScale = doc.scale; + } /** * zooming mechanism (increment and spacing changes) */ @@ -455,8 +467,8 @@ export class Timeline extends React.Component {
    {this.timeIndicator(lengthString, totalTime)} -
    Doc.resetView(this.props.Document)}>
    -
    Doc.setView(this.props.Document)}>
    +
    this.resetView(this.props.Document)}>
    +
    this.setView(this.props.Document)}>
    diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx deleted file mode 100644 index 705469fd2..000000000 --- a/src/client/views/linking/LinkFollowBox.tsx +++ /dev/null @@ -1,571 +0,0 @@ -import { observable, computed, action, runInAction, reaction, IReactionDisposer } from "mobx"; -import React = require("react"); -import { observer } from "mobx-react"; -import { FieldViewProps, FieldView } from "../nodes/FieldView"; -import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc"; -import { undoBatch } from "../../util/UndoManager"; -import { NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types"; -import { CollectionViewType } from "../collections/CollectionView"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { SelectionManager } from "../../util/SelectionManager"; -import { DocumentManager } from "../../util/DocumentManager"; -import { DocumentView } from "../nodes/DocumentView"; -import "./LinkFollowBox.scss"; -import { SearchUtil } from "../../util/SearchUtil"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { listSpec } from "../../../new_fields/Schema"; -import { DocServer } from "../../DocServer"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faTimes } from '@fortawesome/free-solid-svg-icons'; -import { docs_v1 } from "googleapis"; -import { Utils } from "../../../Utils"; -import { Link } from "@react-pdf/renderer"; - -export enum FollowModes { - OPENTAB = "Open in Tab", - OPENRIGHT = "Open in Right Split", - OPENFULL = "Open Full Screen", - PAN = "Pan to Document", - INPLACE = "Open In Place" -} - -enum FollowOptions { - ZOOM = "Zoom", - NOZOOM = "No Zoom", -} - -@observer -export class LinkFollowBox extends React.Component { - - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkFollowBox, fieldKey); } - public static Instance: LinkFollowBox | undefined; - @observable static linkDoc: Doc | undefined = undefined; - @observable static destinationDoc: Doc | undefined = undefined; - @observable static sourceDoc: Doc | undefined = undefined; - @observable selectedMode: string = ""; - @observable selectedContext: Doc | undefined = undefined; - @observable selectedContextAliases: Doc[] | undefined = undefined; - @observable selectedOption: string = ""; - @observable selectedContextString: string = ""; - @observable sourceView: DocumentView | undefined = undefined; - @observable canPan: boolean = false; - @observable shouldUseOnlyParentContext = false; - _contextDisposer?: IReactionDisposer; - - @observable private _docs: { col: Doc, target: Doc }[] = []; - @observable private _otherDocs: { col: Doc, target: Doc }[] = []; - - constructor(props: FieldViewProps) { - super(props); - LinkFollowBox.Instance = this; - this.resetVars(); - this.props.Document.isBackground = true; - } - - componentDidMount = () => { - this.resetVars(); - - this._contextDisposer = reaction( - () => this.selectedContextString, - async () => { - const ref = await DocServer.GetRefField(this.selectedContextString); - runInAction(() => { - if (ref instanceof Doc) { - this.selectedContext = ref; - } - }); - if (this.selectedContext instanceof Doc) { - const aliases = await SearchUtil.GetViewsOfDocument(this.selectedContext); - runInAction(() => { this.selectedContextAliases = aliases; }); - } - } - ); - } - - componentWillUnmount = () => { - this._contextDisposer && this._contextDisposer(); - } - - async resetPan() { - if (LinkFollowBox.destinationDoc && this.sourceView && this.sourceView.props.ContainingCollectionDoc) { - runInAction(() => this.canPan = false); - if (this.sourceView.props.ContainingCollectionDoc._viewType === CollectionViewType.Freeform) { - const docs = Cast(this.sourceView.props.ContainingCollectionDoc.data, listSpec(Doc), []); - const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(LinkFollowBox.destinationDoc)); - - aliases.forEach(alias => { - if (docs.filter(doc => doc === alias).length > 0) { - runInAction(() => { this.canPan = true; }); - } - }); - } - } - } - - @action - resetVars = () => { - this.selectedContext = undefined; - this.selectedContextString = ""; - this.selectedMode = ""; - this.selectedOption = ""; - LinkFollowBox.linkDoc = undefined; - LinkFollowBox.sourceDoc = undefined; - LinkFollowBox.destinationDoc = undefined; - this.sourceView = undefined; - this.canPan = false; - this.shouldUseOnlyParentContext = false; - } - - async fetchDocuments() { - if (LinkFollowBox.destinationDoc) { - const dest: Doc = LinkFollowBox.destinationDoc; - const aliases = await SearchUtil.GetViewsOfDocument(Doc.GetProto(dest)); - const { docs } = await SearchUtil.Search("", true, { fq: `data_l:"${dest[Id]}"` }); - const map: Map = new Map; - const allDocs = await Promise.all(aliases.map(doc => SearchUtil.Search("", true, { fq: `data_l:"${doc[Id]}"` }).then(result => result.docs))); - allDocs.forEach((docs, index) => docs.forEach(doc => map.set(doc, aliases[index]))); - docs.forEach(doc => map.delete(doc)); - runInAction(async () => { - this._docs = docs.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).map(doc => ({ col: doc, target: dest })); - this._otherDocs = Array.from(map.entries()).filter(entry => !Doc.AreProtosEqual(entry[0], CollectionDockingView.Instance.props.Document)).map(([col, target]) => ({ col, target })); - const tcontext = LinkFollowBox.linkDoc && (await Cast(LinkFollowBox.linkDoc.anchor2Context, Doc)) as Doc; - runInAction(() => tcontext && this._docs.splice(0, 0, { col: tcontext, target: dest })); - }); - } - } - - @action - setLinkDocs = (linkDoc: Doc, source: Doc, dest: Doc) => { - this.resetVars(); - - LinkFollowBox.linkDoc = linkDoc; - LinkFollowBox.sourceDoc = source; - LinkFollowBox.destinationDoc = dest; - this.fetchDocuments(); - - SelectionManager.SelectedDocuments().forEach(dv => { - if (dv.props.Document === LinkFollowBox.sourceDoc) { - this.sourceView = dv; - } - }); - - this.resetPan(); - } - - highlightDoc = () => LinkFollowBox.destinationDoc && Doc.linkFollowHighlight(LinkFollowBox.destinationDoc); - - @undoBatch - openFullScreen = () => { - if (LinkFollowBox.destinationDoc) { - const view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc); - view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view); - } - } - - @undoBatch - openColFullScreen = (options: { context: Doc }) => { - if (LinkFollowBox.destinationDoc) { - if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { - const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2; - const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2; - options.context._panX = newPanX; - options.context._panY = newPanY; - } - const view = DocumentManager.Instance.getDocumentView(options.context); - view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view); - this.highlightDoc(); - } - } - - // should container be a doc or documentview or what? This one needs work and is more long term - @undoBatch - openInContainer = (options: { container: Doc }) => { - - } - - static _addDocTab: (undefined | ((doc: Doc, dataDoc: Opt, where: string) => boolean)); - - static setAddDocTab = (addFunc: (doc: Doc, dataDoc: Opt, where: string) => boolean) => { - LinkFollowBox._addDocTab = addFunc; - } - - @undoBatch - openLinkColRight = (options: { context: Doc, shouldZoom: boolean }) => { - if (LinkFollowBox.destinationDoc) { - options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context; - if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { - const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2; - const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2; - options.context._panX = newPanX; - options.context._panY = newPanY; - } - (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, undefined, "onRight"); - - if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom }); - - this.highlightDoc(); - SelectionManager.DeselectAll(); - } - } - - @undoBatch - openLinkRight = () => { - if (LinkFollowBox.destinationDoc) { - const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc); - (LinkFollowBox._addDocTab || this.props.addDocTab)(alias, undefined, "onRight"); - this.highlightDoc(); - SelectionManager.DeselectAll(); - } - - } - - @undoBatch - jumpToLink = async (options: { shouldZoom: boolean }) => { - if (LinkFollowBox.sourceDoc && LinkFollowBox.linkDoc) { - const focus = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; - //let focus = (doc: Doc, maxLocation: string) => this.props.focus(docthis.props.focus(LinkFollowBox.destinationDoc, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)); - - DocumentManager.Instance.FollowLink(LinkFollowBox.linkDoc, LinkFollowBox.sourceDoc, focus, options && options.shouldZoom, false, undefined); - } - } - - @undoBatch - openLinkTab = () => { - if (LinkFollowBox.destinationDoc) { - const fullScreenAlias = Doc.MakeAlias(LinkFollowBox.destinationDoc); - // this.prosp.addDocTab is empty -- use the link source's addDocTab - (LinkFollowBox._addDocTab || this.props.addDocTab)(fullScreenAlias, undefined, "inTab"); - - this.highlightDoc(); - SelectionManager.DeselectAll(); - } - } - - @undoBatch - openLinkColTab = (options: { context: Doc, shouldZoom: boolean }) => { - if (LinkFollowBox.destinationDoc) { - options.context = Doc.IsPrototype(options.context) ? Doc.MakeDelegate(options.context) : options.context; - if (NumCast(options.context._viewType, CollectionViewType.Invalid) === CollectionViewType.Freeform) { - const newPanX = NumCast(LinkFollowBox.destinationDoc.x) + NumCast(LinkFollowBox.destinationDoc._width) / 2; - const newPanY = NumCast(LinkFollowBox.destinationDoc.y) + NumCast(LinkFollowBox.destinationDoc._height) / 2; - options.context._panX = newPanX; - options.context._panY = newPanY; - } - (LinkFollowBox._addDocTab || this.props.addDocTab)(options.context, undefined, "inTab"); - if (options.shouldZoom) this.jumpToLink({ shouldZoom: options.shouldZoom }); - - this.highlightDoc(); - SelectionManager.DeselectAll(); - } - } - - @undoBatch - openLinkInPlace = (options: { shouldZoom: boolean }) => { - - if (LinkFollowBox.destinationDoc && LinkFollowBox.sourceDoc) { - if (this.sourceView && this.sourceView.props.addDocument) { - const destViews = DocumentManager.Instance.getDocumentViews(LinkFollowBox.destinationDoc); - if (!destViews.find(dv => dv.props.ContainingCollectionView === this.sourceView!.props.ContainingCollectionView)) { - const alias = Doc.MakeAlias(LinkFollowBox.destinationDoc); - const y = NumCast(LinkFollowBox.sourceDoc.y); - const x = NumCast(LinkFollowBox.sourceDoc.x); - - const width = NumCast(LinkFollowBox.sourceDoc._width); - const height = NumCast(LinkFollowBox.sourceDoc._height); - - alias.x = x + width + 30; - alias.y = y; - alias._width = width; - alias._height = height; - - this.sourceView.props.addDocument(alias); - } - } - - this.jumpToLink({ shouldZoom: options.shouldZoom }); - - this.highlightDoc(); - SelectionManager.DeselectAll(); - } - } - - //set this to be the default link behavior, can be any of the above - public defaultLinkBehavior: (options?: any) => void = this.jumpToLink; - - @action - currentLinkBehavior = () => { - // this.resetPan(); - if (LinkFollowBox.destinationDoc) { - if (this.selectedContextString === "") { - this.selectedContextString = "self"; - this.selectedContext = LinkFollowBox.destinationDoc; - } - if (this.selectedOption === "") this.selectedOption = FollowOptions.NOZOOM; - const shouldZoom: boolean = this.selectedOption === FollowOptions.NOZOOM ? false : true; - const notOpenInContext: boolean = this.selectedContextString === "self" || this.selectedContextString === LinkFollowBox.destinationDoc[Id]; - - if (this.selectedMode === FollowModes.INPLACE) { - if (shouldZoom !== undefined) this.openLinkInPlace({ shouldZoom: shouldZoom }); - } - else if (this.selectedMode === FollowModes.OPENFULL) { - if (notOpenInContext) this.openFullScreen(); - else this.selectedContext && this.openColFullScreen({ context: this.selectedContext }); - } - else if (this.selectedMode === FollowModes.OPENRIGHT) { - if (notOpenInContext) this.openLinkRight(); - else this.selectedContext && this.openLinkColRight({ context: this.selectedContext, shouldZoom: shouldZoom }); - } - else if (this.selectedMode === FollowModes.OPENTAB) { - if (notOpenInContext) this.openLinkTab(); - else this.selectedContext && this.openLinkColTab({ context: this.selectedContext, shouldZoom: shouldZoom }); - } - else if (this.selectedMode === FollowModes.PAN) { - this.jumpToLink({ shouldZoom: shouldZoom }); - } - else return; - } - } - - @action - handleModeChange = (e: React.ChangeEvent) => { - const target = e.target as HTMLInputElement; - this.selectedMode = target.value; - this.selectedContext = undefined; - this.selectedContextString = ""; - - this.shouldUseOnlyParentContext = (this.selectedMode === FollowModes.INPLACE || this.selectedMode === FollowModes.PAN); - - if (this.shouldUseOnlyParentContext) { - if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) { - this.selectedContext = this.sourceView.props.ContainingCollectionDoc; - this.selectedContextString = (StrCast(this.sourceView.props.ContainingCollectionDoc.title)); - } - } - } - - @action - handleOptionChange = (e: React.ChangeEvent) => { - const target = e.target as HTMLInputElement; - this.selectedOption = target.value; - } - - @action - handleContextChange = (e: React.ChangeEvent) => { - const target = e.target as HTMLInputElement; - this.selectedContextString = target.value; - // selectedContext is updated in reaction - this.selectedOption = ""; - } - - @computed - get canOpenInPlace() { - if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) { - const colDoc = this.sourceView.props.ContainingCollectionDoc; - if (colDoc._viewType === CollectionViewType.Freeform) return true; - } - return false; - } - - @computed - get availableModes() { - return ( -
    -
    -
    -
    -
    -
    -
    - ); - } - - @computed - get parentName() { - if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) { - return this.sourceView.props.ContainingCollectionDoc.title; - } - } - - @computed - get parentID(): string { - if (this.sourceView && this.sourceView.props.ContainingCollectionDoc) { - return StrCast(this.sourceView.props.ContainingCollectionDoc[Id]); - } - return "col"; - } - - @computed - get availableContexts() { - return ( - this.shouldUseOnlyParentContext ? - - : -
    -
    - {[...this._docs, ...this._otherDocs].map(doc => { - if (doc && doc.target && doc.col.title !== "Recently Closed") { - return

    ; - } - })} -
    - ); - } - - @computed - get shouldShowZoom(): boolean { - if (this.selectedMode === FollowModes.OPENFULL) return false; - if (this.shouldUseOnlyParentContext) return true; - if (LinkFollowBox.destinationDoc ? this.selectedContextString === LinkFollowBox.destinationDoc[Id] : "self") return false; - - let contextMatch: boolean = false; - if (this.selectedContextAliases) { - this.selectedContextAliases.forEach(alias => { - if (alias._viewType === CollectionViewType.Freeform) contextMatch = true; - }); - } - if (contextMatch) return true; - - return false; - } - - @computed - get availableOptions() { - if (LinkFollowBox.destinationDoc) { - return ( - this.shouldShowZoom ? -
    -
    -
    -
    - : -
    No Available Options
    - ); - } - return null; - } - - render() { - return ( -
    -
    -
    - {LinkFollowBox.linkDoc ? "Link Title: " + StrCast(LinkFollowBox.linkDoc.title) : "No Link Selected"} -
    this.props.Document.isMinimized = true} className="closeDocument">
    -
    -
    {LinkFollowBox.linkDoc ? - LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc ? "Source: " + StrCast(LinkFollowBox.sourceDoc.title) + ", Destination: " + StrCast(LinkFollowBox.destinationDoc.title) - : "" : ""}
    -
    -
    -
    -
    Mode
    -
    - {LinkFollowBox.linkDoc ? this.availableModes : "Please select a link to view modes"} -
    -
    -
    -
    Context
    -
    - {this.selectedMode !== "" ? this.availableContexts : "Please select a mode to view contexts"} -
    -
    -
    -
    Options
    -
    - {this.selectedContextString !== "" ? this.availableOptions : "Please select a context to view options"} -
    -
    -
    -
    - -
    - -
    -
    - ); - } -} \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 5eaaa3bae..153af933a 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -255,24 +255,6 @@ export namespace Doc { // }); // } - export function resetView(doc: Doc) { - doc._panX = doc._customOriginX ?? 0; - doc._panY = doc._customOriginY ?? 0; - doc.scale = doc._customOriginScale ?? 1; - } - - export function resetViewToOrigin(doc: Doc) { - doc._panX = 0; - doc._panY = 0; - doc.scale = 1; - } - - export function setView(doc: Doc) { - doc._customOriginX = doc._panX; - doc._customOriginY = doc._panY; - doc._customOriginScale = doc.scale; - } - export function RunCachedUpdate(doc: Doc, field: string) { const update = doc[CachedUpdates][field]; if (update) { -- cgit v1.2.3-70-g09d2 From d66aaffc27405f4231a29cd6edda3477077ae946 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 29 Apr 2020 13:48:13 -0400 Subject: fixes for text layout strings. --- src/client/views/animationtimeline/Keyframe.tsx | 4 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentBox.tsx | 27 ++- .../views/nodes/formattedText/DashDocView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 5 +- .../views/nodes/formattedText/RichTextSchema.tsx | 181 --------------------- .../authentication/models/current_user_utils.ts | 14 +- 7 files changed, 28 insertions(+), 207 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index a3407f653..bbd7b2676 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -225,7 +225,7 @@ export class Keyframe extends React.Component { this._mouseToggled = true; } const left = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions)!; - const right = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions!); + const right = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions)!; const prevX = this.regiondata.position; const futureX = this.regiondata.position + KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement); if (futureX <= 0) { @@ -495,7 +495,7 @@ export class Keyframe extends React.Component { } else { return
    -
    +
    ; } }); } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 2d3bb6f3c..c70301b2f 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -113,7 +113,7 @@ export class MarqueeView extends React.Component; @@ -28,7 +27,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent this.contentDoc[this.props.fieldKey], (data) => { + this._prevSelectionDisposer = reaction(() => this.layoutDoc[this.props.fieldKey], (data) => { if (data instanceof Doc && !this.isSelectionLocked()) { this._selections.indexOf(data) !== -1 && this._selections.splice(this._selections.indexOf(data), 1); this._selections.push(data); @@ -42,22 +41,20 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { const funcs: ContextMenuProps[] = []; funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" }); - funcs.push({ description: (this.props.Document.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => Doc.GetProto(this.props.Document).excludeCollections = !this.props.Document.excludeCollections, icon: "expand-arrows-alt" }); - funcs.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }); + funcs.push({ description: (this.layoutDoc.excludeCollections ? "Include" : "Exclude") + " Collections", event: () => this.layoutDoc.excludeCollections = !this.layoutDoc.excludeCollections, icon: "expand-arrows-alt" }); + funcs.push({ description: `${this.layoutDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.layoutDoc.forceActive = !this.layoutDoc.forceActive, icon: "project-diagram" }); + funcs.push({ description: `Show ${this.layoutDoc.childTemplateName !== "keyValue" ? "key values" : "contents"}`, event: () => this.layoutDoc.childTemplateName = this.layoutDoc.childTemplateName ? undefined : "keyValue", icon: "project-diagram" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } - @computed get contentDoc() { - return (this.props.Document.isTemplateDoc || this.props.Document.isTemplateForField ? this.props.Document : Doc.GetProto(this.props.Document)); - } lockSelection = () => { - this.contentDoc[this.props.fieldKey] = this.props.Document[this.props.fieldKey]; + this.layoutDoc[this.props.fieldKey] = this.layoutDoc[this.props.fieldKey]; } showSelection = () => { - this.contentDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`); + this.layoutDoc[this.props.fieldKey] = ComputedField.MakeFunction(`selectedDocs(self,this.excludeCollections,[_last_])?.[0]`); } isSelectionLocked = () => { - const kvpstring = Field.toKeyValueString(this.contentDoc, this.props.fieldKey); + const kvpstring = Field.toKeyValueString(this.layoutDoc, this.props.fieldKey); return !kvpstring || kvpstring.includes("DOC"); } toggleLockSelection = () => { @@ -67,13 +64,13 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { this.lockSelection(); if (this._curSelection > 0) { - this.contentDoc[this.props.fieldKey] = this._selections[--this._curSelection]; + this.layoutDoc[this.props.fieldKey] = this._selections[--this._curSelection]; return true; } } nextSelection = () => { if (this._curSelection < this._selections.length - 1 && this._selections.length) { - this.contentDoc[this.props.fieldKey] = this._selections[++this._curSelection]; + this.layoutDoc[this.props.fieldKey] = this._selections[++this._curSelection]; return true; } } @@ -107,8 +104,8 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent this.props.PanelHeight() - 2 * this.yPad; getTransform = () => this.props.ScreenToLocalTransform().translate(-this.xPad, -this.yPad); get renderContents() { - const containedDoc = Cast(this.contentDoc[this.props.fieldKey], Doc, null); - const childTemplateName = StrCast(this.props.Document.childTemplateName); + const containedDoc = Cast(this.dataDoc[this.props.fieldKey], Doc, null); + const childTemplateName = StrCast(this.layoutDoc.childTemplateName); if (containedDoc && childTemplateName && !containedDoc["layout_" + childTemplateName]) { setTimeout(() => { Doc.createCustomView(containedDoc, Docs.Create.StackingDocument, childTemplateName); @@ -145,7 +142,7 @@ export class DocHolderBox extends ViewBoxAnnotatableComponent { if (dashDocBase instanceof Doc) { const aliasedDoc = Doc.MakeAlias(dashDocBase, docid + alias); aliasedDoc.layoutKey = "layout"; - node.attrs.fieldKey && DocumentView.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined); + node.attrs.fieldKey && Doc.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined); this._dashDoc = aliasedDoc; // self.doRender(aliasedDoc, removeDoc, node, view, getPos); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index c4e387e5a..782a91547 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -418,9 +418,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const cm = ContextMenu.Instance; const funcs: ContextMenuProps[] = []; - this.props.Document.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.props.Document), icon: "eye" }); + this.rootDoc.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.props.Document), icon: "eye" }); + !this.rootDoc.isTemplateDoc && funcs.push({ description: "Show Template", event: async () => this.props.addDocTab(Doc.GetProto(this.layoutDoc), "onRight"), icon: "eye" }); funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); - !this.props.Document.rootDocument && funcs.push({ + !this.rootDoc.isTemplateDoc && funcs.push({ description: "Make Template", event: () => { this.props.Document.isTemplateDoc = makeTemplate(this.props.Document); Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.props.Document); diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx index 33caf5751..cdb7374f8 100644 --- a/src/client/views/nodes/formattedText/RichTextSchema.tsx +++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx @@ -342,187 +342,6 @@ export class DashDocView { } } -export class DashFieldView { - _fieldWrapper: HTMLDivElement; // container for label and value - _labelSpan: HTMLSpanElement; // field label - _fieldSpan: HTMLSpanElement; // field value - _fieldCheck: HTMLInputElement; - _enumerables: HTMLDivElement; // field value - _reactionDisposer: IReactionDisposer | undefined; - _textBoxDoc: Doc; - @observable _dashDoc: Doc | undefined; - _fieldKey: string; - _options: Doc[] = []; - - constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { - this._fieldKey = node.attrs.fieldKey; - this._textBoxDoc = tbox.props.Document; - this._fieldWrapper = document.createElement("p"); - this._fieldWrapper.style.width = node.attrs.width; - this._fieldWrapper.style.height = node.attrs.height; - this._fieldWrapper.style.fontWeight = "bold"; - this._fieldWrapper.style.position = "relative"; - this._fieldWrapper.style.display = "inline-block"; - - const self = this; - - this._enumerables = document.createElement("div"); - this._enumerables.style.width = "10px"; - this._enumerables.style.height = "10px"; - this._enumerables.style.position = "relative"; - this._enumerables.style.display = "none"; - - //Moved - this._enumerables.onpointerdown = async (e) => { - e.stopPropagation(); - const collview = await Doc.addFieldEnumerations(self._textBoxDoc, self._fieldKey, [{ title: self._fieldSpan.innerText }]); - collview instanceof Doc && tbox.props.addDocTab(collview, "onRight"); - }; - //Moved - const updateText = (forceMatch: boolean) => { - self._enumerables.style.display = "none"; - const newText = self._fieldSpan.innerText.startsWith(":=") || self._fieldSpan.innerText.startsWith("=:=") ? ":=-computed-" : self._fieldSpan.innerText; - - // look for a document whose id === the fieldKey being displayed. If there's a match, then that document - // holds the different enumerated values for the field in the titles of its collected documents. - // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down. - DocServer.GetRefField(self._fieldKey).then(options => { - let modText = ""; - (options instanceof Doc) && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title))); - if (modText) { - self._fieldSpan.innerHTML = self._dashDoc![self._fieldKey] = modText; - Doc.addFieldEnumerations(self._textBoxDoc, self._fieldKey, []); - } // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key - else if (self._fieldSpan.innerText.startsWith(":=")) { - self._dashDoc![self._fieldKey] = ComputedField.MakeFunction(self._fieldSpan.innerText.substring(2)); - } else if (self._fieldSpan.innerText.startsWith("=:=")) { - Doc.Layout(tbox.props.Document)[self._fieldKey] = ComputedField.MakeFunction(self._fieldSpan.innerText.substring(3)); - } else { - self._dashDoc![self._fieldKey] = newText; - } - }); - }; - - //Moved - this._fieldCheck = document.createElement("input"); - this._fieldCheck.id = Utils.GenerateGuid(); - this._fieldCheck.type = "checkbox"; - this._fieldCheck.style.position = "relative"; - this._fieldCheck.style.display = "none"; - this._fieldCheck.style.minWidth = "12px"; - this._fieldCheck.style.backgroundColor = "rgba(155, 155, 155, 0.24)"; - this._fieldCheck.onchange = function (e: any) { - self._dashDoc![self._fieldKey] = e.target.checked; - }; - - this._fieldSpan = document.createElement("span"); - this._fieldSpan.id = Utils.GenerateGuid(); - this._fieldSpan.contentEditable = "true"; - this._fieldSpan.style.position = "relative"; - this._fieldSpan.style.display = "none"; - this._fieldSpan.style.minWidth = "12px"; - this._fieldSpan.style.fontSize = "large"; - this._fieldSpan.onkeypress = function (e: any) { e.stopPropagation(); }; - this._fieldSpan.onkeyup = function (e: any) { e.stopPropagation(); }; - this._fieldSpan.onmousedown = function (e: any) { e.stopPropagation(); self._enumerables.style.display = "inline-block"; }; - this._fieldSpan.onblur = function (e: any) { updateText(false); }; - - // MOVED - const setDashDoc = (doc: Doc) => { - self._dashDoc = doc; - if (self._options?.length && !self._dashDoc[self._fieldKey]) { - self._dashDoc[self._fieldKey] = StrCast(self._options[0].title); - } - this._labelSpan.innerHTML = `${self._fieldKey}: `; - const fieldVal = Cast(this._dashDoc?.[self._fieldKey], "boolean", null); - this._fieldCheck.style.display = (fieldVal === true || fieldVal === false) ? "inline-block" : "none"; - this._fieldSpan.style.display = !(fieldVal === true || fieldVal === false) ? StrCast(this._dashDoc?.[self._fieldKey]) ? "" : "inline-block" : "none"; - }; - - //Moved - this._fieldSpan.onkeydown = function (e: any) { - e.stopPropagation(); - if ((e.key === "a" && e.ctrlKey) || (e.key === "a" && e.metaKey)) { - if (window.getSelection) { - const range = document.createRange(); - range.selectNodeContents(self._fieldSpan); - window.getSelection()!.removeAllRanges(); - window.getSelection()!.addRange(range); - } - e.preventDefault(); - } - if (e.key === "Enter") { - e.preventDefault(); - e.ctrlKey && Doc.addFieldEnumerations(self._textBoxDoc, self._fieldKey, [{ title: self._fieldSpan.innerText }]); - updateText(true); - } - }; - - this._labelSpan = document.createElement("span"); - this._labelSpan.style.position = "relative"; - this._labelSpan.style.fontSize = "small"; - this._labelSpan.title = "click to see related tags"; - this._labelSpan.style.fontSize = "x-small"; - this._labelSpan.onpointerdown = function (e: any) { - e.stopPropagation(); - let container = tbox.props.ContainingCollectionView; - while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) { - container = container.props.ContainingCollectionView; - } - if (container) { - const alias = Doc.MakeAlias(container.props.Document); - alias.viewType = CollectionViewType.Time; - let list = Cast(alias.schemaColumns, listSpec(SchemaHeaderField)); - if (!list) { - alias.schemaColumns = list = new List(); - } - list.map(c => c.heading).indexOf(self._fieldKey) === -1 && list.push(new SchemaHeaderField(self._fieldKey, "#f1efeb")); - list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb")); - alias._pivotField = self._fieldKey; - tbox.props.addDocTab(alias, "onRight"); - } - }; - this._labelSpan.innerHTML = `${self._fieldKey}: `; - //MOVED - if (node.attrs.docid) { - DocServer.GetRefField(node.attrs.docid). - then(async dashDoc => dashDoc instanceof Doc && runInAction(() => setDashDoc(dashDoc))); - } else { - setDashDoc(tbox.props.DataDoc || tbox.dataDoc); - } - - //Moved - this._reactionDisposer?.(); - this._reactionDisposer = reaction(() => { // this reaction will update the displayed text whenever the document's fieldKey's value changes - const dashVal = this._dashDoc?.[self._fieldKey]; - return StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(tbox.props.Document)[self._fieldKey] : dashVal; - }, fval => { - const boolVal = Cast(fval, "boolean", null); - if (boolVal === true || boolVal === false) { - this._fieldCheck.checked = boolVal; - } else { - this._fieldSpan.innerHTML = Field.toString(fval as Field) || ""; - } - this._fieldCheck.style.display = (boolVal === true || boolVal === false) ? "inline-block" : "none"; - this._fieldSpan.style.display = !(fval === true || fval === false) ? (StrCast(fval) ? "" : "inline-block") : "none"; - }, { fireImmediately: true }); - - //MOVED IN ORDER - this._fieldWrapper.appendChild(this._labelSpan); - this._fieldWrapper.appendChild(this._fieldCheck); - this._fieldWrapper.appendChild(this._fieldSpan); - this._fieldWrapper.appendChild(this._enumerables); - (this as any).dom = this._fieldWrapper; - //updateText(false); - } - //MOVED - destroy() { - this._reactionDisposer?.(); - } - //moved - selectNode() { } -} - export class FootnoteView { innerView: any; outerView: any; diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 08dc21460..4b2aafac1 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -72,8 +72,8 @@ export class CurrentUserUtils { } if (doc["template-button-description"] === undefined) { - const descriptionTemplate = Docs.Create.TextDocument("", { title: "text", _height: 100, _showTitle: "title" }); - Doc.GetProto(descriptionTemplate).layout = FormattedTextBox.LayoutString("description"); + const descriptionTemplate = Docs.Create.TextDocument("", { title: "header", _height: 100 }); + Doc.GetProto(descriptionTemplate).layout = "
    "; descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView"); doc["template-button-description"] = CurrentUserUtils.ficon({ @@ -181,9 +181,13 @@ export class CurrentUserUtils { doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc, doc["template-note-Todo"] as any as Doc], { title: "Note Layouts", _height: 75 })); } else { - const noteTypes = Cast(doc["template-notes"], Doc, null); - DocListCastAsync(noteTypes).then(list => noteTypes.data = new List([doc["template-note-Note"] as any as Doc, - doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc, doc["template-note-Todo"] as any as Doc])); + const curNoteTypes = Cast(doc["template-notes"], Doc, null); + const requiredTypes = [doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, + doc["template-note-Topic"] as any as Doc, doc["template-note-Todo"] as any as Doc]; + DocListCastAsync(curNoteTypes.data).then(async curNotes => { + await Promise.all(curNotes!); + requiredTypes.map(ntype => Doc.AddDocToList(curNoteTypes, "data", ntype)); + }); } return doc["template-notes"] as Doc; -- cgit v1.2.3-70-g09d2 From 43e573ea0cf4634b65b513c633f90be84846f8df Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 29 Apr 2020 21:08:37 -0400 Subject: changed detailedView template to be a layoutstring. fixed text boxes to allow new templates from templated text boxes. fixed __LAYOUT__ to work with comound layout strings --- src/client/documents/Documents.ts | 19 +++++++++++++++++-- src/client/views/animationtimeline/Timeline.tsx | 4 +--- .../views/nodes/formattedText/FormattedTextBox.tsx | 20 +++++++++++++++++--- src/new_fields/Doc.ts | 2 +- .../authentication/models/current_user_utils.ts | 2 +- 5 files changed, 37 insertions(+), 10 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 811bb5fb2..0d1d73ca3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -541,8 +541,23 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COLOR), "", options); } - export function TextDocument(text: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.RTF), text, options, undefined, "text"); + export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = "text") { + const rtf = { + doc: { + type: "doc", content: [{ + type: "paragraph", + content: [{ + type: "text", + text + }] + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + + const field = text ? new RichTextField(JSON.stringify(rtf), text) : undefined; + return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 677267ca0..fe1e40778 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -119,9 +119,7 @@ export class Timeline extends React.Component { } componentWillUnmount() { - runInAction(() => { - this.props.Document.AnimationLength = this._time; //save animation length - }); + this.props.Document.AnimationLength = this._time; //save animation length } ///////////////////////////////////////////////// diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 782a91547..2038efbc6 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -419,14 +419,28 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const funcs: ContextMenuProps[] = []; this.rootDoc.isTemplateDoc && funcs.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.props.Document), icon: "eye" }); - !this.rootDoc.isTemplateDoc && funcs.push({ description: "Show Template", event: async () => this.props.addDocTab(Doc.GetProto(this.layoutDoc), "onRight"), icon: "eye" }); funcs.push({ description: "Reset Default Layout", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" }); - !this.rootDoc.isTemplateDoc && funcs.push({ + !this.layoutDoc.isTemplateDoc && funcs.push({ description: "Make Template", event: () => { - this.props.Document.isTemplateDoc = makeTemplate(this.props.Document); + this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc); Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.props.Document); }, icon: "eye" }); + this.layoutDoc.isTemplateDoc && funcs.push({ + description: "Make New Template", event: () => { + const title = this.rootDoc.title as string; + this.rootDoc.layout = (this.layoutDoc as Doc).layout as string; + this.rootDoc.title = this.layoutDoc.isTemplateForField as string; + this.rootDoc.isTemplateDoc = false; + this.rootDoc.isTemplateForField = ""; + this.rootDoc.layoutKey = "layout"; + this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); + this.rootDoc._width = this.layoutDoc._width || 300; // the width and height are stored on the template, since we're getting rid of the old template + this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields + this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null); + Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc); + }, icon: "eye" + }); funcs.push({ description: "Toggle Single Line", event: () => this.props.Document._singleLine = !this.props.Document._singleLine, icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Sidebar", event: () => this.props.Document._showSidebar = !this.props.Document._showSidebar, icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Dictation Icon", event: () => this.props.Document._showAudio = !this.props.Document._showAudio, icon: "expand-arrows-alt" }); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 153af933a..77eee03ce 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -183,7 +183,7 @@ export class Doc extends RefField { let renderFieldKey: any; const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, "layout")]; if (typeof layoutField === "string") { - renderFieldKey = layoutField.split("'")[1]; + renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0];//layoutField.split("'")[1]; } else { return Cast(layoutField, Doc, null); } diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 4b2aafac1..eedd3ee67 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -72,7 +72,7 @@ export class CurrentUserUtils { } if (doc["template-button-description"] === undefined) { - const descriptionTemplate = Docs.Create.TextDocument("", { title: "header", _height: 100 }); + const descriptionTemplate = Docs.Create.TextDocument(" ", { title: "header", _height: 100 }, "header"); // text needs to be a space to allow templateText to be created Doc.GetProto(descriptionTemplate).layout = "
    "; descriptionTemplate.isTemplateDoc = makeTemplate(descriptionTemplate, true, "descriptionView"); -- cgit v1.2.3-70-g09d2 From 90c45914694a971c1b3cb356921c04f337625db5 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 30 Apr 2020 00:06:08 -0400 Subject: fixes for snapping & timeline. changed looi of document decorations --- src/client/util/DragManager.ts | 8 ++--- src/client/views/DocumentDecorations.scss | 34 +++++++++++++++++++++- src/client/views/DocumentDecorations.tsx | 9 +++--- src/client/views/MainView.tsx | 1 - src/client/views/MetadataEntryMenu.scss | 9 +++--- src/client/views/animationtimeline/Timeline.tsx | 33 ++++++++------------- .../collectionFreeForm/CollectionFreeFormView.tsx | 14 ++++++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 +++-- 8 files changed, 74 insertions(+), 42 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 36c26fe2c..bccdf38ce 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -310,10 +310,10 @@ export namespace DragManager { const currTop = e.pageY - yFromTop; const currRight = e.pageX + xFromRight; const currBottom = e.pageY + yFromBottom; - const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev); - const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev); - const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev); - const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev); + const closestLeft = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currLeft) > Math.abs(curr - currLeft) ? curr : prev, currLeft); + const closestTop = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currTop) > Math.abs(curr - currTop) ? curr : prev, currTop); + const closestRight = vertSnapLines.reduce((prev, curr) => Math.abs(prev - currRight) > Math.abs(curr - currRight) ? curr : prev, currRight); + const closestBottom = horizSnapLines.reduce((prev, curr) => Math.abs(prev - currBottom) > Math.abs(curr - currBottom) ? curr : prev, currBottom); const distFromClosestLeft = Math.abs(e.pageX - xFromLeft - closestLeft); const distFromClosestTop = Math.abs(e.pageY - yFromTop - closestTop); const distFromClosestRight = Math.abs(e.pageX + xFromRight - closestRight); diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 28cf9fd47..61d517d43 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -21,9 +21,13 @@ $linkGap : 3px; background: none; } + .documentDecorations-resizer { pointer-events: auto; background: $alt-accent; + opacity: 0.1; + } + .documentDecorations-resizer:hover { opacity: 1; } @@ -80,7 +84,20 @@ $linkGap : 3px; #documentDecorations-topLeftResizer, #documentDecorations-bottomRightResizer { cursor: nwse-resize; - background: dimGray; + background: unset; + opacity: 1; + } + #documentDecorations-topLeftResizer { + border-left: black 2px solid; + border-top: black solid 2px; + } + #documentDecorations-bottomRightResizer { + border-right: black 2px solid; + border-bottom: black solid 2px; + } + #documentDecorations-topLeftResizer:hover, + #documentDecorations-bottomRightResizer:hover { + opacity: 1; } #documentDecorations-bottomRightResizer { @@ -89,8 +106,23 @@ $linkGap : 3px; #documentDecorations-topRightResizer, #documentDecorations-bottomLeftResizer { + cursor: nesw-resize; + background: unset; + opacity: 1; + } + #documentDecorations-topRightResizer { + border-right: black 2px solid; + border-top: black 2px solid; + } + #documentDecorations-bottomLeftResizer { + border-left: black 2px solid; + border-bottom: black 2px solid; + } + #documentDecorations-topRightResizer:hover, + #documentDecorations-bottomLeftResizer:hover { cursor: nesw-resize; background: dimGray; + opacity: 1; } #documentDecorations-topResizer, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 312acd5b2..973ec2e89 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -473,10 +473,11 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}>
    e.preventDefault()}>
    - {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
    e.preventDefault()}> - -
    } + {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : +
    e.preventDefault()}> + +
    }
    e.preventDefault()}>
    diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 62b2d1d18..e5a8ebcb5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -42,7 +42,6 @@ import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import { ScriptField } from '../../new_fields/ScriptField'; -import { DragManager } from '../util/DragManager'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; @observer diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss index 5776cf070..28de0b7a5 100644 --- a/src/client/views/MetadataEntryMenu.scss +++ b/src/client/views/MetadataEntryMenu.scss @@ -9,9 +9,10 @@ } .metadataEntry-autoSuggester { - width: 100%; + width: 80%; height: 100%; - padding-right: 10px; + margin: 0; + display: inline-block; } #metadataEntry-outer { @@ -25,7 +26,7 @@ flex-direction: column; } .metadataEntry-inputArea { - display:flex; + display:inline-block; flex-direction: row; } @@ -44,7 +45,7 @@ .react-autosuggest__input { border: 1px solid #aaa; border-radius: 4px; - width: 100%; + width: 75%; } .react-autosuggest__input--focused { diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index fe1e40778..77656b85f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -71,7 +71,6 @@ export class Timeline extends React.Component { @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; @observable private _time = 100000; //DEFAULT @observable private _playButton = faPlayCircle; - @observable private _timelineVisible = false; @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; @observable private _titleHeight = 0; @@ -336,20 +335,6 @@ export class Timeline extends React.Component { } - /** - * context menu function. - * opens the timeline or closes the timeline. - * Used in: Freeform - */ - timelineContextMenu = (e: React.MouseEvent): void => { - ContextMenu.Instance.addItem({ - description: (this._timelineVisible ? "Close" : "Open") + " Animation Timeline", event: action(() => { - this._timelineVisible = !this._timelineVisible; - }), icon: this._timelineVisible ? faEyeSlash : faEye - }); - } - - /** * timeline zoom function * use mouse middle button to zoom in/out the timeline @@ -463,7 +448,7 @@ export class Timeline extends React.Component {
    -
    +
    {this.timeIndicator(lengthString, totalTime)}
    this.resetView(this.props.Document)}>
    this.setView(this.props.Document)}>
    @@ -481,10 +466,16 @@ export class Timeline extends React.Component { ); } else { + const ctime = `Current: ${this.getCurrentTime()}`; + const ttime = `Total: ${this.toReadTime(this._time)}`; return (
    -
    {`Current: ${this.getCurrentTime()}`}
    -
    {`Total: ${this.toReadTime(this._time)}`}
    +
    + {ctime} +
    +
    + {ttime} +
    ); } @@ -601,8 +592,8 @@ export class Timeline extends React.Component { trace(); // change visible and total width return ( -
    -
    +
    +
    {this.drawTicks()} @@ -611,7 +602,7 @@ export class Timeline extends React.Component {
    {DocListCast(this.children).map(doc => - this.mapOfTracks.push(ref)} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={this._timelineVisible} /> + this.mapOfTracks.push(ref)} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={true} /> )}
    diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0c9403429..77de486d9 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,5 +1,5 @@ import { library } from "@fortawesome/fontawesome-svg-core"; -import { faEye } from "@fortawesome/free-regular-svg-icons"; +import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons"; import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; @@ -1093,7 +1093,6 @@ export class CollectionFreeFormView extends CollectionSubView { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document.scale = 1; }, icon: "compress-arrows-alt" }); optionItems.push({ description: `${this.Document._LODdisable ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._LODdisable = !this.Document._LODdisable, icon: "table" }); optionItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); @@ -1130,8 +1129,15 @@ export class CollectionFreeFormView extends CollectionSubView { + this._timelineVisible = !this._timelineVisible; + }), icon: this._timelineVisible ? faEyeSlash : faEye + }); } + @observable _timelineVisible = false; intersectRect(r1: { left: number, top: number, width: number, height: number }, r2: { left: number, top: number, width: number, height: number }) { @@ -1215,7 +1221,7 @@ export class CollectionFreeFormView extends CollectionSubView {this.children} - + {this._timelineVisible ? : (null)} ; } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 2038efbc6..4df693c9a 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -435,9 +435,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.rootDoc.isTemplateForField = ""; this.rootDoc.layoutKey = "layout"; this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); - this.rootDoc._width = this.layoutDoc._width || 300; // the width and height are stored on the template, since we're getting rid of the old template - this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields - this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null); + setTimeout(() => { + this.rootDoc._width = this.layoutDoc._width || 300; // the width and height are stored on the template, since we're getting rid of the old template + this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields + this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, "string", null); + }, 10); Doc.AddDocToList(Cast(Doc.UserDoc()["template-notes"], Doc, null), "data", this.rootDoc); }, icon: "eye" }); -- cgit v1.2.3-70-g09d2 From 5e6352c78be5b2a9fe791bd87da9b2415ced4839 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 1 May 2020 19:35:37 -0400 Subject: added childLayoutTemplate to render collection children w/o modifying them. fixed docComponent's layoutDoc to use LayoutDoc prop. change buxton template to use new template mechanisms. fixed fixed lint errors. --- src/client/documents/Documents.ts | 5 +- src/client/util/DragManager.ts | 2 +- src/client/views/DocComponent.tsx | 2 +- src/client/views/animationtimeline/Timeline.tsx | 4 +- .../views/animationtimeline/TimelineMenu.tsx | 81 +++++++++++----------- src/client/views/animationtimeline/Track.tsx | 50 ++++++------- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 17 ----- .../views/collections/CollectionTreeView.tsx | 36 ++++------ .../collectionFreeForm/CollectionFreeFormView.tsx | 11 ++- src/client/views/nodes/PresBox.tsx | 2 +- .../views/nodes/formattedText/DashFieldView.tsx | 2 +- src/new_fields/documentSchemas.ts | 2 +- 13 files changed, 94 insertions(+), 122 deletions(-) (limited to 'src/client/views/animationtimeline') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a8b10bc7b..434b26312 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -93,7 +93,8 @@ export interface DocumentOptions { scale?: number; isDisplayPanel?: boolean; // whether the panel functions as GoldenLayout "stack" used to display documents forceActive?: boolean; - layout?: string | Doc; + layout?: string | Doc; // default layout string for a document + childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view) hideFilterView?: boolean; // whether to hide the filter popout on collections hideHeadings?: boolean; // whether stacking view column headings should be hidden isTemplateForField?: string; // the field key for which the containing document is a rendering template @@ -123,7 +124,7 @@ export interface DocumentOptions { borderRounding?: string; boxShadow?: string; dontRegisterChildren?: boolean; - "onDoubleClick-rawScript"?: string // onDoubleClick script in raw text form + "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form "onClick-rawScript"?: string; // onClick script in raw text form "onCheckedClick-rawScript"?: string; // onChecked script in raw text form "onCheckedClick-params"?: List; // parameter list for onChecked treeview functions diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c48611eff..c06ad3d60 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -316,7 +316,7 @@ export namespace DragManager { return closestDists[minIndex] < snapThreshold ? closestPts[minIndex] + offs[minIndex] : drag; } return drag; - } + }; return { thisX: snapVal([xFromLeft, xFromRight], e.pageX, vertSnapLines), thisY: snapVal([yFromTop, yFromBottom], e.pageY, horizSnapLines) }; } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 0a8f0c9a7..629b0f447 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -20,7 +20,7 @@ export function DocComponent

    (schemaCtor: (doc: D // This is the "The Document" -- it encapsulates, data, layout, and any templates @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; } // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info - @computed get layoutDoc() { return Doc.Layout(this.props.Document); } + @computed get layoutDoc() { return Doc.Layout(this.props.Document, this.props.LayoutDoc?.()); } // This is the data part of a document -- ie, the data that is constant across all views of the document @computed get dataDoc() { return this.props.Document[DataSym] as Doc; } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 77656b85f..466cbb867 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -525,8 +525,8 @@ export class Timeline extends React.Component { @action.bound changeLengths() { if (this._infoContainer.current) { - this._visibleLength = this._infoContainer.current!.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) - this._visibleStart = this._infoContainer.current!.scrollLeft; //where the div starts + this._visibleLength = this._infoContainer.current.getBoundingClientRect().width; //the visible length of the timeline (the length that you current see) + this._visibleStart = this._infoContainer.current.scrollLeft; //where the div starts } } diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index 59c25596e..53ca9acad 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -1,7 +1,7 @@ import * as React from "react"; -import {observable, action, runInAction} from "mobx"; -import {observer} from "mobx-react"; -import "./TimelineMenu.scss"; +import { observable, action, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import "./TimelineMenu.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons"; import { Utils } from "../../../Utils"; @@ -9,67 +9,66 @@ import { Utils } from "../../../Utils"; @observer export class TimelineMenu extends React.Component { - public static Instance:TimelineMenu; + public static Instance: TimelineMenu; @observable private _opacity = 0; - @observable private _x = 0; - @observable private _y = 0; - @observable private _currentMenu:JSX.Element[] = []; + @observable private _x = 0; + @observable private _y = 0; + @observable private _currentMenu: JSX.Element[] = []; - constructor (props:Readonly<{}>){ - super(props); - TimelineMenu.Instance = this; + constructor(props: Readonly<{}>) { + super(props); + TimelineMenu.Instance = this; } - + @action - openMenu = (x?:number, y?:number) => { - this._opacity = 1; - x ? this._x = x : this._x = 0; - y ? this._y = y : this._y = 0; + openMenu = (x?: number, y?: number) => { + this._opacity = 1; + x ? this._x = x : this._x = 0; + y ? this._y = y : this._y = 0; } @action closeMenu = () => { - this._opacity = 0; - this._currentMenu = []; - this._x = -1000000; - this._y = -1000000; + this._opacity = 0; + this._currentMenu = []; + this._x = -1000000; + this._y = -1000000; } @action - addItem = (type: "input" | "button", title: string, event: (e:any, ...args:any[]) => void) => { - if (type === "input"){ - let inputRef = React.createRef(); - let text = ""; - this._currentMenu.push(

    { + addItem = (type: "input" | "button", title: string, event: (e: any, ...args: any[]) => void) => { + if (type === "input") { + const inputRef = React.createRef(); + let text = ""; + this._currentMenu.push(
    { e.stopPropagation(); text = e.target.value; }} onKeyDown={(e) => { if (e.keyCode === 13) { - event(text); - this.closeMenu(); - e.stopPropagation(); - } - }}/>
    ); + event(text); + this.closeMenu(); + e.stopPropagation(); + } + }} />
    ); } else if (type === "button") { - let buttonRef = React.createRef(); - this._currentMenu.push(

    { - e.preventDefault(); - e.stopPropagation(); - event(e); - this.closeMenu(); - }}>{title}

    ); - } + this._currentMenu.push(

    { + e.preventDefault(); + e.stopPropagation(); + event(e); + this.closeMenu(); + }}>{title}

    ); + } } - @action - addMenu = (title:string) => { - this._currentMenu.unshift(

    {title}

    ); + @action + addMenu = (title: string) => { + this._currentMenu.unshift(

    {title}

    ); } render() { return ( -
    +
    {this._currentMenu}
    ); diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 79eb60fae..461db4858 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -90,9 +90,9 @@ export class Track extends React.Component { */ @action saveKeyframe = async () => { - let keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List; - let kfIndex = keyframes.indexOf(this.saveStateKf!); - let kf = keyframes[kfIndex] as Doc; //index in the keyframe + const keyframes = Cast(this.saveStateRegion?.keyframes, listSpec(Doc)) as List; + const kfIndex = keyframes.indexOf(this.saveStateKf!); + const kf = keyframes[kfIndex] as Doc; //index in the keyframe if (this._newKeyframe) { DocListCast(this.saveStateRegion?.keyframes).forEach((kf, index) => { this.copyDocDataToKeyFrame(kf); @@ -103,17 +103,17 @@ export class Track extends React.Component { if (!kf) return; if (kf.type === KeyframeFunc.KeyframeType.default) { // only save for non-fades this.copyDocDataToKeyFrame(kf); - let leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists - let rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists + const leftkf = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, kf); // lef keyframe, if it exists + const rightkf = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, kf); //right keyframe, if it exists if (leftkf?.type === KeyframeFunc.KeyframeType.fade) { //replicating this keyframe to fades - let edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf); + const edge = KeyframeFunc.calcMinLeft(this.saveStateRegion!, this.time, leftkf); edge && this.copyDocDataToKeyFrame(edge); leftkf && this.copyDocDataToKeyFrame(leftkf); - edge && (edge!.opacity = 0.1); - leftkf && (leftkf!.opacity = 1); + edge && (edge.opacity = 0.1); + leftkf && (leftkf.opacity = 1); } if (rightkf?.type === KeyframeFunc.KeyframeType.fade) { - let edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf); + const edge = KeyframeFunc.calcMinRight(this.saveStateRegion!, this.time, rightkf); edge && this.copyDocDataToKeyFrame(edge); rightkf && this.copyDocDataToKeyFrame(rightkf); edge && (edge.opacity = 0.1); @@ -142,7 +142,7 @@ export class Track extends React.Component { //check for region const region = this.findRegion(this.time); if (region !== undefined) { //if region at scrub time exist - let r = region as RegionData; //for some region is returning undefined... which is not the case + const r = region as RegionData; //for some region is returning undefined... which is not the case if (DocListCast(r.keyframes).find(kf => kf.time === this.time) === undefined) { //basically when there is no additional keyframe at that timespot this.makeKeyData(r, this.time, KeyframeFunc.KeyframeType.default); } @@ -222,11 +222,11 @@ export class Track extends React.Component { } else if (this._newKeyframe) { await this.saveKeyframe(); } - let regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on + const regiondata = await this.findRegion(Math.round(this.time)); //finds a region that the scrubber is on if (regiondata) { - let leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists - let rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists - let currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe + const leftkf: (Doc | undefined) = await KeyframeFunc.calcMinLeft(regiondata, this.time); // lef keyframe, if it exists + const rightkf: (Doc | undefined) = await KeyframeFunc.calcMinRight(regiondata, this.time); //right keyframe, if it exists + const currentkf: (Doc | undefined) = await this.calcCurrent(regiondata); //if the scrubber is on top of the keyframe if (currentkf) { console.log("is current"); await this.applyKeys(currentkf); @@ -248,7 +248,7 @@ export class Track extends React.Component { if (!kf[key]) { this.props.node[key] = undefined; } else { - let stored = kf[key]; + const stored = kf[key]; this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); @@ -261,7 +261,7 @@ export class Track extends React.Component { @action calcCurrent = (region: Doc) => { let currentkf: (Doc | undefined) = undefined; - let keyframes = DocListCast(region.keyframes!); + const keyframes = DocListCast(region.keyframes!); keyframes.forEach((kf) => { if (NumCast(kf.time) === Math.round(this.time)) currentkf = kf; }); @@ -276,12 +276,12 @@ export class Track extends React.Component { interpolate = async (left: Doc, right: Doc) => { this.primitiveWhitelist.forEach(key => { if (left[key] && right[key] && typeof (left[key]) === "number" && typeof (right[key]) === "number") { //if it is number, interpolate - let dif = NumCast(right[key]) - NumCast(left[key]); - let deltaLeft = this.time - NumCast(left.time); - let ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); + const dif = NumCast(right[key]) - NumCast(left[key]); + const deltaLeft = this.time - NumCast(left.time); + const ratio = deltaLeft / (NumCast(right.time) - NumCast(left.time)); this.props.node[key] = NumCast(left[key]) + (dif * ratio); } else { // case data - let stored = left[key]; + const stored = left[key]; this.props.node[key] = stored instanceof ObjectField ? stored[Copy]() : stored; } }); @@ -301,8 +301,8 @@ export class Track extends React.Component { */ @action onInnerDoubleClick = (e: React.MouseEvent) => { - let inner = this._inner.current!; - let offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); + const inner = this._inner.current!; + const offsetX = Math.round((e.clientX - inner.getBoundingClientRect().left) * this.props.transform.Scale); this.createRegion(KeyframeFunc.convertPixelTime(offsetX, "mili", "time", this.props.tickSpacing, this.props.tickIncrement)); } @@ -313,10 +313,10 @@ export class Track extends React.Component { @action createRegion = (time: number) => { if (this.findRegion(time) === undefined) { //check if there is a region where double clicking (prevents phantom regions) - let regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data + const regiondata = KeyframeFunc.defaultKeyframe(); //create keyframe data regiondata.position = time; //set position - let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); + const rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, regiondata, this.regions); if (rightRegion && rightRegion.position - regiondata.position <= 4000) { //edge case when there is less than default 4000 duration space between this and right region regiondata.duration = rightRegion.position - regiondata.position; @@ -332,7 +332,7 @@ export class Track extends React.Component { @action makeKeyData = (regiondata: RegionData, time: number, type: KeyframeFunc.KeyframeType = KeyframeFunc.KeyframeType.default) => { //Kfpos is mouse offsetX, representing time - const trackKeyFrames = DocListCast(regiondata.keyframes)!; + const trackKeyFrames = DocListCast(regiondata.keyframes); const existingkf = trackKeyFrames.find(TK => TK.time === time); if (existingkf) return existingkf; //else creates a new doc. diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 6c230d5b1..01766f65f 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -128,7 +128,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return layoutDoc._fitWidth ? wid * NumCast(layoutDoc.scrollHeight, nh) / (nw || 1) : layoutDoc[HeightSym](); } componentDidMount() { - super.componentDidMount(); + super.componentDidMount?.(); // reset section headers when a new filter is inputted this._pivotFieldDisposer = reaction( diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 8cc1af55b..e44bbae78 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -58,7 +58,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - private _childLayoutDisposer?: IReactionDisposer; protected _mainCont?: HTMLDivElement; protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this.dropDisposer?.(); @@ -75,25 +74,9 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: this.createDashEventsTarget(ele); } - componentDidMount() { - this._childLayoutDisposer = reaction(() => ({ childDocs: this.childDocs, childLayout: Cast(this.props.Document.childLayout, Doc) }), - ({ childDocs, childLayout }) => { - if (childLayout instanceof Doc) { - childDocs.map(doc => { - doc.layout_fromParent = childLayout; - doc.layoutKey = "layout_fromParent"; - }); - } - else if (!(childLayout instanceof Promise)) { - childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layout_fromParent" && (doc.layoutKey = "layout")); - } - }, { fireImmediately: true }); - - } componentWillUnmount() { this.gestureDisposer?.(); this.multiTouchDisposer?.(); - this._childLayoutDisposer?.(); } @computed get dataDoc() { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 71358a8ec..296c1a39c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -725,14 +725,6 @@ export class CollectionTreeView extends CollectionSubView { - DocListCast(this.dataDoc[this.props.fieldKey]).map(d => { - DocListCast(d.data).map((img, i) => { - const caption = (d.captions as any)[i]; - if (caption) { - Doc.GetProto(img).caption = caption; - } - }); - }); const { ImageDocument } = Docs.Create; const { Document } = this.props; const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; @@ -742,21 +734,19 @@ export class CollectionTreeView extends CollectionSubView(["dropAction"]), icon: "portrait", - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), - })); - - Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", - Docs.Create.FontIconDocument({ - title: "detail view", _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, dropAction: "alias", - dragFactory: detailView, removeDropProperties: new List(["dropAction"]), icon: "file-alt", - onDragStart: ScriptField.MakeFunction('getCopy(this.dragFactory, true)'), - })); - - Document.childLayout = heroView; + const doubleClickView = ImageDocument("http://cs.brown.edu/~bcz/face.gif", { _width: 400 }); // replace with desired double click target + DocListCast(this.dataDoc[this.props.fieldKey]).map(d => { + DocListCast(d.data).map((img, i) => { + const caption = (d.captions as any)[i]; + if (caption) { + Doc.GetProto(img).caption = caption; + Doc.GetProto(img).doubleClickView = doubleClickView; + } + }); + d.layout = ImageBox.LayoutString("hero"); + }); + + Document.childLayoutTemplate = heroView; Document.childDetailView = detailView; Document._viewType = CollectionViewType.Time; Document._forceActive = true; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b4eb22444..45ef0455e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -855,9 +855,10 @@ export class CollectionFreeFormView extends CollectionSubView BoolCast(this.Document.useClusters); @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); } + backgroundHalo = () => BoolCast(this.Document.useClusters); parentActive = () => this.props.active() || this.backgroundActive ? true : false; + childLayoutFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { ...this.props, @@ -867,12 +868,12 @@ export class CollectionFreeFormView extends CollectionSubView this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); get doLayoutComputation() { const { newPool, computedElementData } = this.doInternalLayoutComputation; runInAction(() => @@ -1025,7 +1025,6 @@ export class CollectionFreeFormView extends CollectionSubView this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); @@ -1156,7 +1155,7 @@ export class CollectionFreeFormView extends CollectionSubView !doc.isBackground && doc.z === undefined).map(doc => isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 72bbc9e4b..6e3420f22 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -260,7 +260,7 @@ export class PresBox extends ViewBoxBaseComponent panelHeight = () => this.props.PanelHeight() - 20; active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.layoutDoc.isBackground) && - (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false); + (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 1b22ed4cd..d87d6e424 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -104,7 +104,7 @@ export class DashFieldViewInternal extends React.Component this._showEnumerables = true)); }} > {strVal} - + ; } } } diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 7bf1c03c8..fd9a304f9 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -78,7 +78,7 @@ export const positionSchema = createSchema({ }); export const collectionSchema = createSchema({ - childLayout: Doc, // layout template for children of a collecion + childLayoutTemplate: Doc, // layout template for children of a collecion childDetailView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to use this field) onChildClick: ScriptField, // script to run for each child when its clicked onChildDoubleClick: ScriptField, // script to run for each child when its clicked -- cgit v1.2.3-70-g09d2 From 2b79008596351f6948d8de80c7887446d97b068c Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 15 May 2020 00:03:41 -0700 Subject: renamed new_fields to fields --- src/client/ClientRecommender.tsx | 16 +- src/client/DocServer.ts | 6 +- src/client/apis/GoogleAuthenticationManager.tsx | 2 +- src/client/apis/IBM_Recommender.ts | 2 +- .../apis/google_docs/GoogleApiClientUtils.ts | 2 +- .../apis/google_docs/GooglePhotosClientUtils.ts | 12 +- src/client/apis/youtube/YoutubeBox.tsx | 4 +- src/client/cognitive_services/CognitiveServices.ts | 8 +- src/client/documents/Documents.ts | 22 +- src/client/util/CurrentUserUtils.ts | 18 +- src/client/util/DictationManager.ts | 12 +- src/client/util/DocumentManager.ts | 6 +- src/client/util/DragManager.ts | 16 +- src/client/util/DropConverter.ts | 12 +- src/client/util/History.ts | 2 +- .../util/Import & Export/DirectoryImportBox.tsx | 12 +- src/client/util/Import & Export/ImageUtils.ts | 8 +- .../util/Import & Export/ImportMetadataEntry.tsx | 4 +- src/client/util/LinkManager.ts | 8 +- src/client/util/Scripting.ts | 2 +- src/client/util/SearchUtil.ts | 4 +- src/client/util/SelectionManager.ts | 4 +- src/client/util/SerializationHelper.ts | 2 +- src/client/util/SharingManager.tsx | 6 +- src/client/views/DocComponent.tsx | 14 +- src/client/views/DocumentButtonBar.tsx | 6 +- src/client/views/DocumentDecorations.tsx | 24 +- src/client/views/EditableView.tsx | 4 +- src/client/views/GestureOverlay.tsx | 12 +- src/client/views/GlobalKeyHandler.ts | 14 +- src/client/views/InkingControl.tsx | 6 +- src/client/views/InkingStroke.tsx | 10 +- src/client/views/MainView.tsx | 14 +- src/client/views/MainViewNotifs.tsx | 2 +- src/client/views/MetadataEntryMenu.tsx | 2 +- src/client/views/OverlayView.tsx | 6 +- src/client/views/Palette.tsx | 4 +- src/client/views/PreviewCursor.tsx | 4 +- src/client/views/RecommendationsBox.tsx | 8 +- src/client/views/ScriptBox.tsx | 6 +- src/client/views/SearchDocBox.tsx | 6 +- src/client/views/TemplateMenu.tsx | 8 +- src/client/views/animationtimeline/Keyframe.tsx | 8 +- src/client/views/animationtimeline/Timeline.tsx | 8 +- src/client/views/animationtimeline/Track.tsx | 12 +- .../views/collections/CollectionCarouselView.tsx | 10 +- .../views/collections/CollectionDockingView.tsx | 29 +- .../views/collections/CollectionLinearView.tsx | 10 +- src/client/views/collections/CollectionMapView.tsx | 10 +- .../collections/CollectionMasonryViewFieldRow.tsx | 8 +- .../views/collections/CollectionPileView.tsx | 6 +- .../views/collections/CollectionSchemaCells.tsx | 8 +- .../views/collections/CollectionSchemaHeaders.tsx | 2 +- .../CollectionSchemaMovableTableHOC.tsx | 6 +- .../views/collections/CollectionSchemaView.tsx | 14 +- .../views/collections/CollectionStackingView.tsx | 16 +- .../CollectionStackingViewFieldColumn.tsx | 16 +- .../views/collections/CollectionStaffView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 14 +- .../views/collections/CollectionTimeView.tsx | 12 +- .../views/collections/CollectionTreeView.tsx | 16 +- src/client/views/collections/CollectionView.tsx | 20 +- .../views/collections/CollectionViewChromes.tsx | 10 +- .../views/collections/ParentDocumentSelector.tsx | 6 +- .../CollectionFreeFormLayoutEngines.tsx | 14 +- .../CollectionFreeFormLinkView.tsx | 6 +- .../CollectionFreeFormLinksView.tsx | 4 +- .../CollectionFreeFormRemoteCursors.tsx | 10 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 23 +- .../collections/collectionFreeForm/MarqueeView.tsx | 12 +- .../CollectionMulticolumnView.tsx | 10 +- .../CollectionMultirowView.tsx | 10 +- .../collectionMulticolumn/MulticolumnResizer.tsx | 4 +- .../MulticolumnWidthLabel.tsx | 4 +- .../collectionMulticolumn/MultirowHeightLabel.tsx | 4 +- .../collectionMulticolumn/MultirowResizer.tsx | 4 +- src/client/views/linking/LinkEditor.tsx | 4 +- src/client/views/linking/LinkMenu.tsx | 2 +- src/client/views/linking/LinkMenuGroup.tsx | 6 +- src/client/views/linking/LinkMenuItem.tsx | 4 +- src/client/views/nodes/AudioBox.tsx | 16 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 8 +- src/client/views/nodes/ColorBox.tsx | 6 +- .../views/nodes/ContentFittingDocumentView.tsx | 6 +- src/client/views/nodes/DocHolderBox.tsx | 12 +- src/client/views/nodes/DocumentContentsView.tsx | 8 +- src/client/views/nodes/DocumentIcon.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 20 +- src/client/views/nodes/FaceRectangles.tsx | 6 +- src/client/views/nodes/FieldView.tsx | 10 +- src/client/views/nodes/FontIconBox.tsx | 8 +- src/client/views/nodes/ImageBox.tsx | 20 +- src/client/views/nodes/KeyValueBox.tsx | 14 +- src/client/views/nodes/KeyValuePair.tsx | 2 +- src/client/views/nodes/LabelBox.tsx | 10 +- src/client/views/nodes/LinkAnchorBox.tsx | 10 +- src/client/views/nodes/LinkBox.tsx | 6 +- src/client/views/nodes/PDFBox.tsx | 12 +- src/client/views/nodes/PresBox.tsx | 16 +- src/client/views/nodes/QueryBox.tsx | 10 +- src/client/views/nodes/ScreenshotBox.tsx | 8 +- src/client/views/nodes/ScriptingBox.tsx | 10 +- src/client/views/nodes/SliderBox.tsx | 8 +- src/client/views/nodes/VideoBox.tsx | 16 +- src/client/views/nodes/WebBox.tsx | 14 +- .../nodes/formattedText/DashDocCommentView.tsx | 16 +- .../views/nodes/formattedText/DashDocView.tsx | 10 +- .../views/nodes/formattedText/DashFieldView.tsx | 12 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 24 +- .../formattedText/FormattedTextBoxComment.tsx | 4 +- .../views/nodes/formattedText/ImageResizeView.tsx | 2 +- .../formattedText/ProsemirrorExampleTransfer.ts | 6 +- .../views/nodes/formattedText/RichTextMenu.tsx | 6 +- .../views/nodes/formattedText/RichTextRules.ts | 10 +- .../views/nodes/formattedText/RichTextSchema.tsx | 16 +- src/client/views/nodes/formattedText/marks_rts.ts | 2 +- src/client/views/pdf/Annotation.tsx | 8 +- src/client/views/pdf/PDFMenu.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 20 +- .../views/presentationview/PresElementBox.tsx | 10 +- src/client/views/search/FilterBox.tsx | 6 +- src/client/views/search/SearchBox.tsx | 8 +- src/client/views/search/SearchItem.tsx | 6 +- src/debug/Repl.tsx | 6 +- src/debug/Test.tsx | 2 +- src/debug/Viewer.tsx | 16 +- src/fields/CursorField.ts | 66 ++ src/fields/DateField.ts | 36 + src/fields/Doc.ts | 1109 ++++++++++++++++++++ src/fields/FieldSymbols.ts | 12 + src/fields/HtmlField.ts | 26 + src/fields/IconField.ts | 26 + src/fields/InkField.ts | 50 + src/fields/List.ts | 330 ++++++ src/fields/ListSpec.ts | 0 src/fields/ObjectField.ts | 20 + src/fields/PresField.ts | 6 + src/fields/Proxy.ts | 126 +++ src/fields/RefField.ts | 21 + src/fields/RichTextField.ts | 41 + src/fields/RichTextUtils.ts | 519 +++++++++ src/fields/Schema.ts | 120 +++ src/fields/SchemaHeaderField.ts | 122 +++ src/fields/ScriptField.ts | 193 ++++ src/fields/Types.ts | 108 ++ src/fields/URLField.ts | 53 + src/fields/documentSchemas.ts | 100 ++ src/fields/util.ts | 199 ++++ src/mobile/ImageUpload.tsx | 8 +- src/mobile/MobileInkOverlay.tsx | 6 +- src/mobile/MobileInterface.tsx | 12 +- src/new_fields/CursorField.ts | 66 -- src/new_fields/DateField.ts | 36 - src/new_fields/Doc.ts | 1109 -------------------- src/new_fields/FieldSymbols.ts | 12 - src/new_fields/HtmlField.ts | 26 - src/new_fields/IconField.ts | 26 - src/new_fields/InkField.ts | 50 - src/new_fields/List.ts | 330 ------ src/new_fields/ListSpec.ts | 0 src/new_fields/ObjectField.ts | 20 - src/new_fields/PresField.ts | 6 - src/new_fields/Proxy.ts | 126 --- src/new_fields/RefField.ts | 21 - src/new_fields/RichTextField.ts | 41 - src/new_fields/RichTextUtils.ts | 519 --------- src/new_fields/Schema.ts | 120 --- src/new_fields/SchemaHeaderField.ts | 122 --- src/new_fields/ScriptField.ts | 193 ---- src/new_fields/Types.ts | 108 -- src/new_fields/URLField.ts | 53 - src/new_fields/documentSchemas.ts | 100 -- src/new_fields/util.ts | 199 ---- src/pen-gestures/GestureUtils.ts | 6 +- src/server/ApiManagers/GooglePhotosManager.ts | 2 +- src/server/ApiManagers/UserManager.ts | 2 +- src/server/DashUploadUtils.ts | 2 +- src/server/Message.ts | 2 +- src/server/Recommender.ts | 6 +- src/server/apis/google/GoogleApiServerUtils.ts | 2 +- src/server/database.ts | 2 +- test/test.ts | 8 +- 182 files changed, 3889 insertions(+), 3889 deletions(-) create mode 100644 src/fields/CursorField.ts create mode 100644 src/fields/DateField.ts create mode 100644 src/fields/Doc.ts create mode 100644 src/fields/FieldSymbols.ts create mode 100644 src/fields/HtmlField.ts create mode 100644 src/fields/IconField.ts create mode 100644 src/fields/InkField.ts create mode 100644 src/fields/List.ts create mode 100644 src/fields/ListSpec.ts create mode 100644 src/fields/ObjectField.ts create mode 100644 src/fields/PresField.ts create mode 100644 src/fields/Proxy.ts create mode 100644 src/fields/RefField.ts create mode 100644 src/fields/RichTextField.ts create mode 100644 src/fields/RichTextUtils.ts create mode 100644 src/fields/Schema.ts create mode 100644 src/fields/SchemaHeaderField.ts create mode 100644 src/fields/ScriptField.ts create mode 100644 src/fields/Types.ts create mode 100644 src/fields/URLField.ts create mode 100644 src/fields/documentSchemas.ts create mode 100644 src/fields/util.ts delete mode 100644 src/new_fields/CursorField.ts delete mode 100644 src/new_fields/DateField.ts delete mode 100644 src/new_fields/Doc.ts delete mode 100644 src/new_fields/FieldSymbols.ts delete mode 100644 src/new_fields/HtmlField.ts delete mode 100644 src/new_fields/IconField.ts delete mode 100644 src/new_fields/InkField.ts delete mode 100644 src/new_fields/List.ts delete mode 100644 src/new_fields/ListSpec.ts delete mode 100644 src/new_fields/ObjectField.ts delete mode 100644 src/new_fields/PresField.ts delete mode 100644 src/new_fields/Proxy.ts delete mode 100644 src/new_fields/RefField.ts delete mode 100644 src/new_fields/RichTextField.ts delete mode 100644 src/new_fields/RichTextUtils.ts delete mode 100644 src/new_fields/Schema.ts delete mode 100644 src/new_fields/SchemaHeaderField.ts delete mode 100644 src/new_fields/ScriptField.ts delete mode 100644 src/new_fields/Types.ts delete mode 100644 src/new_fields/URLField.ts delete mode 100644 src/new_fields/documentSchemas.ts delete mode 100644 src/new_fields/util.ts (limited to 'src/client/views/animationtimeline') diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx index 537e331ab..d18669b02 100644 --- a/src/client/ClientRecommender.tsx +++ b/src/client/ClientRecommender.tsx @@ -1,6 +1,6 @@ -import { Doc, FieldResult } from "../new_fields/Doc"; -import { StrCast, Cast } from "../new_fields/Types"; -import { List } from "../new_fields/List"; +import { Doc, FieldResult } from "../fields/Doc"; +import { StrCast, Cast } from "../fields/Types"; +import { List } from "../fields/List"; import { CognitiveServices, Confidence, Tag, Service } from "./cognitive_services/CognitiveServices"; import React = require("react"); import { observer } from "mobx-react"; @@ -11,11 +11,11 @@ import { observable, action, computed, reaction } from "mobx"; // var https = require('https'); import "./ClientRecommender.scss"; import { JSXElement } from "babel-types"; -import { RichTextField } from "../new_fields/RichTextField"; -import { ToPlainText } from "../new_fields/FieldSymbols"; -import { listSpec } from "../new_fields/Schema"; -import { ComputedField } from "../new_fields/ScriptField"; -import { ImageField } from "../new_fields/URLField"; +import { RichTextField } from "../fields/RichTextField"; +import { ToPlainText } from "../fields/FieldSymbols"; +import { listSpec } from "../fields/Schema"; +import { ComputedField } from "../fields/ScriptField"; +import { ImageField } from "../fields/URLField"; import { KeyphraseQueryView } from "./views/KeyphraseQueryView"; import { Networking } from "./Network"; diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index ae125694b..ac5b7a218 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,10 +1,10 @@ import * as OpenSocket from 'socket.io-client'; import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message"; -import { Opt, Doc } from '../new_fields/Doc'; +import { Opt, Doc } from '../fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { SerializationHelper } from './util/SerializationHelper'; -import { RefField } from '../new_fields/RefField'; -import { Id, HandleUpdate } from '../new_fields/FieldSymbols'; +import { RefField } from '../fields/RefField'; +import { Id, HandleUpdate } from '../fields/FieldSymbols'; import GestureOverlay from './views/GestureOverlay'; import MobileInkOverlay from '../mobile/MobileInkOverlay'; import { runInAction } from 'mobx'; diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx index 22d6fb582..bf4469aeb 100644 --- a/src/client/apis/GoogleAuthenticationManager.tsx +++ b/src/client/apis/GoogleAuthenticationManager.tsx @@ -2,7 +2,7 @@ import { observable, action, reaction, runInAction, IReactionDisposer } from "mo import { observer } from "mobx-react"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Opt } from "../../new_fields/Doc"; +import { Opt } from "../../fields/Doc"; import { Networking } from "../Network"; import "./GoogleAuthenticationManager.scss"; import { Scripting } from "../util/Scripting"; diff --git a/src/client/apis/IBM_Recommender.ts b/src/client/apis/IBM_Recommender.ts index 4e1c541c8..480b9cb1c 100644 --- a/src/client/apis/IBM_Recommender.ts +++ b/src/client/apis/IBM_Recommender.ts @@ -1,4 +1,4 @@ -// import { Opt } from "../../new_fields/Doc"; +// import { Opt } from "../../fields/Doc"; // const NaturalLanguageUnderstandingV1 = require('ibm-watson/natural-language-understanding/v1'); // const { IamAuthenticator } = require('ibm-watson/auth'); diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts index 2f3cac8d3..551dca073 100644 --- a/src/client/apis/google_docs/GoogleApiClientUtils.ts +++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts @@ -1,5 +1,5 @@ import { docs_v1 } from "googleapis"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt } from "../../../fields/Doc"; import { isArray } from "util"; import { EditorState } from "prosemirror-state"; import { Networking } from "../../Network"; diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 1e4c120bc..fef71ffeb 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -1,11 +1,11 @@ import { AssertionError } from "assert"; import { EditorState } from "prosemirror-state"; -import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { RichTextField } from "../../../new_fields/RichTextField"; -import { RichTextUtils } from "../../../new_fields/RichTextUtils"; -import { Cast, StrCast } from "../../../new_fields/Types"; -import { ImageField } from "../../../new_fields/URLField"; +import { Doc, DocListCastAsync, Opt } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; +import { RichTextField } from "../../../fields/RichTextField"; +import { RichTextUtils } from "../../../fields/RichTextUtils"; +import { Cast, StrCast } from "../../../fields/Types"; +import { ImageField } from "../../../fields/URLField"; import { MediaItem, NewMediaItemResult } from "../../../server/apis/google/SharedTypes"; import { Utils } from "../../../Utils"; import { Docs, DocumentOptions } from "../../documents/Documents"; diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 1575e53fc..ce7f49e64 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -1,7 +1,7 @@ import { action, observable, runInAction } from 'mobx'; import { observer } from "mobx-react"; -import { Doc, DocListCastAsync } from "../../../new_fields/Doc"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Doc, DocListCastAsync } from "../../../fields/Doc"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs } from "../../documents/Documents"; diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts index 8c63ae906..d4df7ce57 100644 --- a/src/client/cognitive_services/CognitiveServices.ts +++ b/src/client/cognitive_services/CognitiveServices.ts @@ -1,12 +1,12 @@ import * as request from "request-promise"; -import { Doc, Field } from "../../new_fields/Doc"; -import { Cast } from "../../new_fields/Types"; +import { Doc, Field } from "../../fields/Doc"; +import { Cast } from "../../fields/Types"; import { Docs } from "../documents/Documents"; import { Utils } from "../../Utils"; -import { InkData } from "../../new_fields/InkField"; +import { InkData } from "../../fields/InkField"; import { UndoManager } from "../util/UndoManager"; import requestPromise = require("request-promise"); -import { List } from "../../new_fields/List"; +import { List } from "../../fields/List"; import { ClientRecommender } from "../ClientRecommender"; type APIManager = { converter: BodyConverter, requester: RequestExecutor }; diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 125a531f4..13687e4e8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -9,14 +9,14 @@ import { ScriptingBox } from "../views/nodes/ScriptingBox"; import { VideoBox } from "../views/nodes/VideoBox"; import { WebBox } from "../views/nodes/WebBox"; import { OmitKeys, JSONUtils, Utils } from "../../Utils"; -import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../new_fields/Doc"; -import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField"; -import { HtmlField } from "../../new_fields/HtmlField"; -import { List } from "../../new_fields/List"; -import { Cast, NumCast, StrCast } from "../../new_fields/Types"; +import { Field, Doc, Opt, DocListCastAsync, FieldResult, DocListCast } from "../../fields/Doc"; +import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../fields/URLField"; +import { HtmlField } from "../../fields/HtmlField"; +import { List } from "../../fields/List"; +import { Cast, NumCast, StrCast } from "../../fields/Types"; import { DocServer } from "../DocServer"; import { dropActionType } from "../util/DragManager"; -import { DateField } from "../../new_fields/DateField"; +import { DateField } from "../../fields/DateField"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { LinkManager } from "../util/LinkManager"; @@ -26,10 +26,10 @@ import { Scripting } from "../util/Scripting"; import { LabelBox } from "../views/nodes/LabelBox"; import { SliderBox } from "../views/nodes/SliderBox"; import { FontIconBox } from "../views/nodes/FontIconBox"; -import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { PresBox } from "../views/nodes/PresBox"; -import { ComputedField, ScriptField } from "../../new_fields/ScriptField"; -import { ProxyField } from "../../new_fields/Proxy"; +import { ComputedField, ScriptField } from "../../fields/ScriptField"; +import { ProxyField } from "../../fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { RecommendationsBox } from "../views/RecommendationsBox"; import { PresElementBox } from "../views/presentationview/PresElementBox"; @@ -39,9 +39,9 @@ import { ColorBox } from "../views/nodes/ColorBox"; import { LinkAnchorBox } from "../views/nodes/LinkAnchorBox"; import { DocHolderBox } from "../views/nodes/DocHolderBox"; import { InkingStroke } from "../views/InkingStroke"; -import { InkField } from "../../new_fields/InkField"; +import { InkField } from "../../fields/InkField"; import { InkingControl } from "../views/InkingControl"; -import { RichTextField } from "../../new_fields/RichTextField"; +import { RichTextField } from "../../fields/RichTextField"; import { extname } from "path"; import { MessageStore } from "../../server/Message"; import { ContextMenuProps } from "../views/ContextMenuItem"; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index b67c3ea60..d9faed36d 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -4,23 +4,23 @@ import { Utils } from "../../Utils"; import { DocServer } from "../DocServer"; import { Docs, DocumentOptions } from "../documents/Documents"; import { UndoManager } from "./UndoManager"; -import { Doc, DocListCast, DocListCastAsync } from "../../new_fields/Doc"; -import { List } from "../../new_fields/List"; -import { listSpec } from "../../new_fields/Schema"; -import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; -import { Cast, PromiseValue, StrCast, NumCast } from "../../new_fields/Types"; -import { nullAudio } from "../../new_fields/URLField"; +import { Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; +import { List } from "../../fields/List"; +import { listSpec } from "../../fields/Schema"; +import { ScriptField, ComputedField } from "../../fields/ScriptField"; +import { Cast, PromiseValue, StrCast, NumCast } from "../../fields/Types"; +import { nullAudio } from "../../fields/URLField"; import { DragManager } from "./DragManager"; import { InkingControl } from "../views/InkingControl"; import { Scripting } from "./Scripting"; import { CollectionViewType } from "../views/collections/CollectionView"; import { makeTemplate } from "./DropConverter"; -import { RichTextField } from "../../new_fields/RichTextField"; -import { PrefetchProxy } from "../../new_fields/Proxy"; +import { RichTextField } from "../../fields/RichTextField"; +import { PrefetchProxy } from "../../fields/Proxy"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { MainView } from "../views/MainView"; import { DocumentType } from "../documents/DocumentTypes"; -import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; export class CurrentUserUtils { diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index b3295ece0..e46225b4a 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -3,15 +3,15 @@ import { DocumentView } from "../views/nodes/DocumentView"; import { UndoManager } from "./UndoManager"; import * as interpreter from "words-to-numbers"; import { DocumentType } from "../documents/DocumentTypes"; -import { Doc, Opt } from "../../new_fields/Doc"; -import { List } from "../../new_fields/List"; +import { Doc, Opt } from "../../fields/Doc"; +import { List } from "../../fields/List"; import { Docs } from "../documents/Documents"; import { CollectionViewType } from "../views/collections/CollectionView"; -import { Cast, CastCtor } from "../../new_fields/Types"; -import { listSpec } from "../../new_fields/Schema"; -import { AudioField, ImageField } from "../../new_fields/URLField"; +import { Cast, CastCtor } from "../../fields/Types"; +import { listSpec } from "../../fields/Schema"; +import { AudioField, ImageField } from "../../fields/URLField"; import { Utils } from "../../Utils"; -import { RichTextField } from "../../new_fields/RichTextField"; +import { RichTextField } from "../../fields/RichTextField"; import { DictationOverlay } from "../views/DictationOverlay"; /** diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 99d85125a..ab087335e 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,7 +1,7 @@ import { action, computed, observable } from 'mobx'; -import { Doc, DocListCastAsync, DocListCast, Opt } from '../../new_fields/Doc'; -import { Id } from '../../new_fields/FieldSymbols'; -import { Cast, NumCast, StrCast } from '../../new_fields/Types'; +import { Doc, DocListCastAsync, DocListCast, Opt } from '../../fields/Doc'; +import { Id } from '../../fields/FieldSymbols'; +import { Cast, NumCast, StrCast } from '../../fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionView } from '../views/collections/CollectionView'; import { DocumentView, DocFocusFunc } from '../views/nodes/DocumentView'; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index d1d7f2a8a..66c7ecf7d 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -1,12 +1,12 @@ import { action, observable, runInAction } from "mobx"; -import { DateField } from "../../new_fields/DateField"; -import { Doc, Field, Opt } from "../../new_fields/Doc"; -import { List } from "../../new_fields/List"; -import { PrefetchProxy } from "../../new_fields/Proxy"; -import { listSpec } from "../../new_fields/Schema"; -import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; -import { ScriptField } from "../../new_fields/ScriptField"; -import { Cast, NumCast, ScriptCast, StrCast } from "../../new_fields/Types"; +import { DateField } from "../../fields/DateField"; +import { Doc, Field, Opt } from "../../fields/Doc"; +import { List } from "../../fields/List"; +import { PrefetchProxy } from "../../fields/Proxy"; +import { listSpec } from "../../fields/Schema"; +import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; +import { ScriptField } from "../../fields/ScriptField"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types"; import { emptyFunction } from "../../Utils"; import { Docs, DocUtils } from "../documents/Documents"; import * as globalCssVariables from "../views/globalCssVariables.scss"; diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index d6db882b8..752c1cfc5 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -1,12 +1,12 @@ import { DragManager } from "./DragManager"; -import { Doc, DocListCast, Opt } from "../../new_fields/Doc"; +import { Doc, DocListCast, Opt } from "../../fields/Doc"; import { DocumentType } from "../documents/DocumentTypes"; -import { ObjectField } from "../../new_fields/ObjectField"; -import { StrCast } from "../../new_fields/Types"; +import { ObjectField } from "../../fields/ObjectField"; +import { StrCast } from "../../fields/Types"; import { Docs } from "../documents/Documents"; -import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; -import { RichTextField } from "../../new_fields/RichTextField"; -import { ImageField } from "../../new_fields/URLField"; +import { ScriptField, ComputedField } from "../../fields/ScriptField"; +import { RichTextField } from "../../fields/RichTextField"; +import { ImageField } from "../../fields/URLField"; import { Scripting } from "./Scripting"; // diff --git a/src/client/util/History.ts b/src/client/util/History.ts index 2c53d7e52..7b7d4b835 100644 --- a/src/client/util/History.ts +++ b/src/client/util/History.ts @@ -1,4 +1,4 @@ -import { Doc } from "../../new_fields/Doc"; +import { Doc } from "../../fields/Doc"; import { DocServer } from "../DocServer"; import { MainView } from "../views/MainView"; import * as qs from 'query-string'; diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 438904688..1e8f07049 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -1,6 +1,6 @@ import "fs"; import React = require("react"); -import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; import { action, observable, runInAction, computed, reaction, IReactionDisposer } from "mobx"; import { FieldViewProps, FieldView } from "../../views/nodes/FieldView"; import Measure, { ContentRect } from "react-measure"; @@ -12,12 +12,12 @@ import { observer } from "mobx-react"; import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from "./ImportMetadataEntry"; import { Utils } from "../../../Utils"; import { DocumentManager } from "../DocumentManager"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; -import { Cast, BoolCast, NumCast } from "../../../new_fields/Types"; -import { listSpec } from "../../../new_fields/Schema"; +import { Id } from "../../../fields/FieldSymbols"; +import { List } from "../../../fields/List"; +import { Cast, BoolCast, NumCast } from "../../../fields/Types"; +import { listSpec } from "../../../fields/Schema"; import { GooglePhotos } from "../../apis/google_docs/GooglePhotosClientUtils"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import "./DirectoryImportBox.scss"; import { Networking } from "../../Network"; import { BatchedArray } from "array-batcher"; diff --git a/src/client/util/Import & Export/ImageUtils.ts b/src/client/util/Import & Export/ImageUtils.ts index c8d1530b3..072e5f58a 100644 --- a/src/client/util/Import & Export/ImageUtils.ts +++ b/src/client/util/Import & Export/ImageUtils.ts @@ -1,9 +1,9 @@ -import { Doc } from "../../../new_fields/Doc"; -import { ImageField } from "../../../new_fields/URLField"; -import { Cast, StrCast } from "../../../new_fields/Types"; +import { Doc } from "../../../fields/Doc"; +import { ImageField } from "../../../fields/URLField"; +import { Cast, StrCast } from "../../../fields/Types"; import { Docs } from "../../documents/Documents"; import { Networking } from "../../Network"; -import { Id } from "../../../new_fields/FieldSymbols"; +import { Id } from "../../../fields/FieldSymbols"; import { Utils } from "../../../Utils"; export namespace ImageUtils { diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx index 8e1c50bea..dcb94e2e0 100644 --- a/src/client/util/Import & Export/ImportMetadataEntry.tsx +++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx @@ -5,8 +5,8 @@ import { action, computed } from "mobx"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { library } from '@fortawesome/fontawesome-svg-core'; -import { Doc } from "../../../new_fields/Doc"; -import { StrCast, BoolCast } from "../../../new_fields/Types"; +import { Doc } from "../../../fields/Doc"; +import { StrCast, BoolCast } from "../../../fields/Types"; interface KeyValueProps { Document: Doc; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index e236c7f47..8e6ccf098 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -1,7 +1,7 @@ -import { Doc, DocListCast } from "../../new_fields/Doc"; -import { List } from "../../new_fields/List"; -import { listSpec } from "../../new_fields/Schema"; -import { Cast, StrCast } from "../../new_fields/Types"; +import { Doc, DocListCast } from "../../fields/Doc"; +import { List } from "../../fields/List"; +import { listSpec } from "../../fields/Schema"; +import { Cast, StrCast } from "../../fields/Types"; import { Docs } from "../documents/Documents"; import { Scripting } from "./Scripting"; diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 8b7b9c9c7..ab577315c 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -9,7 +9,7 @@ export { ts }; // @ts-ignore import * as typescriptlib from '!!raw-loader!./type_decls.d'; -import { Doc, Field } from '../../new_fields/Doc'; +import { Doc, Field } from '../../fields/Doc'; export interface ScriptSucccess { success: true; diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 6501da34a..5679c0a14 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -1,7 +1,7 @@ import * as rp from 'request-promise'; import { DocServer } from '../DocServer'; -import { Doc } from '../../new_fields/Doc'; -import { Id } from '../../new_fields/FieldSymbols'; +import { Doc } from '../../fields/Doc'; +import { Id } from '../../fields/FieldSymbols'; import { Utils } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 11d2cafb2..bd743c28e 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,8 +1,8 @@ import { observable, action, runInAction, ObservableMap } from "mobx"; -import { Doc } from "../../new_fields/Doc"; +import { Doc } from "../../fields/Doc"; import { DocumentView } from "../views/nodes/DocumentView"; import { computedFn } from "mobx-utils"; -import { List } from "../../new_fields/List"; +import { List } from "../../fields/List"; export namespace SelectionManager { diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index 1f6b939d3..ad0309fa7 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -1,5 +1,5 @@ import { PropSchema, serialize, deserialize, custom, setDefaultModelSchema, getDefaultModelSchema } from "serializr"; -import { Field } from "../../new_fields/Doc"; +import { Field } from "../../fields/Doc"; import { ClientUtils } from "./ClientUtils"; let serializing = 0; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 3ce6de80d..dc67145fc 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,13 +1,13 @@ import { observable, runInAction, action } from "mobx"; import * as React from "react"; import MainViewModal from "../views/MainViewModal"; -import { Doc, Opt, DocCastAsync } from "../../new_fields/Doc"; +import { Doc, Opt, DocCastAsync } from "../../fields/Doc"; import { DocServer } from "../DocServer"; -import { Cast, StrCast } from "../../new_fields/Types"; +import { Cast, StrCast } from "../../fields/Types"; import * as RequestPromise from "request-promise"; import { Utils } from "../../Utils"; import "./SharingManager.scss"; -import { Id } from "../../new_fields/FieldSymbols"; +import { Id } from "../../fields/FieldSymbols"; import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { library } from '@fortawesome/fontawesome-svg-core'; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 6517f2fb9..1ba9fcc32 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -1,14 +1,14 @@ -import { Doc, Opt, DataSym, DocListCast } from '../../new_fields/Doc'; +import { Doc, Opt, DataSym, DocListCast } from '../../fields/Doc'; import { Touchable } from './Touchable'; import { computed, action, observable } from 'mobx'; -import { Cast, BoolCast, ScriptCast } from '../../new_fields/Types'; -import { listSpec } from '../../new_fields/Schema'; +import { Cast, BoolCast, ScriptCast } from '../../fields/Types'; +import { listSpec } from '../../fields/Schema'; import { InkingControl } from './InkingControl'; -import { InkTool } from '../../new_fields/InkField'; +import { InkTool } from '../../fields/InkField'; import { InteractionUtils } from '../util/InteractionUtils'; -import { List } from '../../new_fields/List'; -import { DateField } from '../../new_fields/DateField'; -import { ScriptField } from '../../new_fields/ScriptField'; +import { List } from '../../fields/List'; +import { DateField } from '../../fields/DateField'; +import { ScriptField } from '../../fields/ScriptField'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 10d9ec401..2db5cd3ba 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -3,9 +3,9 @@ import { faArrowAltCircleDown, faPhotoVideo, faArrowAltCircleUp, faArrowAltCircl import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../new_fields/Doc"; -import { RichTextField } from '../../new_fields/RichTextField'; -import { NumCast, StrCast, Cast } from "../../new_fields/Types"; +import { Doc, DocListCast } from "../../fields/Doc"; +import { RichTextField } from '../../fields/RichTextField'; +import { NumCast, StrCast, Cast } from "../../fields/Types"; import { emptyFunction, setupMoveUpEvents } from "../../Utils"; import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; import { UndoManager } from "../util/UndoManager"; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 313d8be23..1f4f45f0e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -3,10 +3,10 @@ import { faCaretUp, faFilePdf, faFilm, faImage, faObjectGroup, faStickyNote, faT import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../new_fields/Doc"; -import { Document } from '../../new_fields/documentSchemas'; -import { ScriptField } from '../../new_fields/ScriptField'; -import { Cast, StrCast, NumCast } from "../../new_fields/Types"; +import { Doc, DataSym, Field, WidthSym, HeightSym } from "../../fields/Doc"; +import { Document } from '../../fields/documentSchemas'; +import { ScriptField } from '../../fields/ScriptField'; +import { Cast, StrCast, NumCast } from "../../fields/Types"; import { Utils, setupMoveUpEvents, emptyFunction, returnFalse, simulateMouseClick } from "../../Utils"; import { DocUtils } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; @@ -17,7 +17,7 @@ import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); -import { Id } from '../../new_fields/FieldSymbols'; +import { Id } from '../../fields/FieldSymbols'; import e = require('express'); import { CollectionDockingView } from './collections/CollectionDockingView'; import { SnappingManager } from '../util/SnappingManager'; @@ -266,16 +266,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const fixedAspect = first.layoutDoc._nativeWidth ? NumCast(first.layoutDoc._nativeWidth) / NumCast(first.layoutDoc._nativeHeight) : 0; if (fixedAspect && (this._resizeHdlId === "documentDecorations-bottomRightResizer" || this._resizeHdlId === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles const project = (p: number[], a: number[], b: number[]) => { - var atob = [b[0] - a[0], b[1] - a[1]]; - var atop = [p[0] - a[0], p[1] - a[1]]; - var len = atob[0] * atob[0] + atob[1] * atob[1]; - var dot = atop[0] * atob[0] + atop[1] * atob[1]; - var t = dot / len; + const atob = [b[0] - a[0], b[1] - a[1]]; + const atop = [p[0] - a[0], p[1] - a[1]]; + const len = atob[0] * atob[0] + atob[1] * atob[1]; + let dot = atop[0] * atob[0] + atop[1] * atob[1]; + const t = dot / len; dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]); return [a[0] + atob[0] * t, a[1] + atob[1] * t]; - } + }; const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]) + const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]); thisPt = DragManager.snapDragAspect(drag, fixedAspect); } else { thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY); diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index c51173ad3..e0e205df9 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -2,8 +2,8 @@ import React = require('react'); import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as Autosuggest from 'react-autosuggest'; -import { ObjectField } from '../../new_fields/ObjectField'; -import { SchemaHeaderField } from '../../new_fields/SchemaHeaderField'; +import { ObjectField } from '../../fields/ObjectField'; +import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; import "./EditableView.scss"; export interface EditableProps { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 812ff3b93..aadf5f535 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -6,13 +6,13 @@ import { computed, observable, action, runInAction, IReactionDisposer, reaction, import { GestureUtils } from "../../pen-gestures/GestureUtils"; import { InteractionUtils } from "../util/InteractionUtils"; import { InkingControl } from "./InkingControl"; -import { InkTool, InkData } from "../../new_fields/InkField"; -import { Doc } from "../../new_fields/Doc"; +import { InkTool, InkData } from "../../fields/InkField"; +import { Doc } from "../../fields/Doc"; import { LinkManager } from "../util/LinkManager"; import { DocUtils, Docs } from "../documents/Documents"; import { undoBatch } from "../util/UndoManager"; import { Scripting } from "../util/Scripting"; -import { FieldValue, Cast, NumCast, BoolCast } from "../../new_fields/Types"; +import { FieldValue, Cast, NumCast, BoolCast } from "../../fields/Types"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import HorizontalPalette from "./Palette"; import { Utils, emptyPath, emptyFunction, returnFalse, returnOne, returnEmptyString, returnTrue, numberRange, returnZero } from "../../Utils"; @@ -22,9 +22,9 @@ import { DocumentContentsView } from "./nodes/DocumentContentsView"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; import { DocServer } from "../DocServer"; import htmlToImage from "html-to-image"; -import { ScriptField } from "../../new_fields/ScriptField"; -import { listSpec } from "../../new_fields/Schema"; -import { List } from "../../new_fields/List"; +import { ScriptField } from "../../fields/ScriptField"; +import { listSpec } from "../../fields/Schema"; +import { List } from "../../fields/List"; import { CollectionViewType } from "./collections/CollectionView"; import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu"; import MobileInterface from "../../mobile/MobileInterface"; diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index b52a0063b..255142771 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -4,23 +4,23 @@ import { CollectionDockingView } from "./collections/CollectionDockingView"; import { MainView } from "./MainView"; import { DragManager } from "../util/DragManager"; import { action, runInAction } from "mobx"; -import { Doc, DocListCast } from "../../new_fields/Doc"; +import { Doc, DocListCast } from "../../fields/Doc"; import { DictationManager } from "../util/DictationManager"; import SharingManager from "../util/SharingManager"; -import { Cast, PromiseValue, NumCast } from "../../new_fields/Types"; -import { ScriptField } from "../../new_fields/ScriptField"; +import { Cast, PromiseValue, NumCast } from "../../fields/Types"; +import { ScriptField } from "../../fields/ScriptField"; import { InkingControl } from "./InkingControl"; -import { InkTool } from "../../new_fields/InkField"; +import { InkTool } from "../../fields/InkField"; import { DocumentView } from "./nodes/DocumentView"; import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager"; import { CollectionFreeFormView } from "./collections/collectionFreeForm/CollectionFreeFormView"; import { MarqueeView } from "./collections/collectionFreeForm/MarqueeView"; -import { Id } from "../../new_fields/FieldSymbols"; +import { Id } from "../../fields/FieldSymbols"; import { DocumentDecorations } from "./DocumentDecorations"; import { DocumentType } from "../documents/DocumentTypes"; import { DocServer } from "../DocServer"; -import { List } from "../../new_fields/List"; -import { DateField } from "../../new_fields/DateField"; +import { List } from "../../fields/List"; +import { DateField } from "../../fields/DateField"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise; diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 5115995b0..156f58ec1 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -1,8 +1,8 @@ import { action, computed, observable } from "mobx"; import { ColorState } from 'react-color'; -import { Doc } from "../../new_fields/Doc"; -import { InkTool } from "../../new_fields/InkField"; -import { FieldValue, NumCast, StrCast } from "../../new_fields/Types"; +import { Doc } from "../../fields/Doc"; +import { InkTool } from "../../fields/InkField"; +import { FieldValue, NumCast, StrCast } from "../../fields/Types"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { Scripting } from "../util/Scripting"; import { SelectionManager } from "../util/SelectionManager"; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 7a318d5c2..25ea50a27 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -1,14 +1,14 @@ import { observer } from "mobx-react"; -import { documentSchema } from "../../new_fields/documentSchemas"; -import { InkData, InkField, InkTool } from "../../new_fields/InkField"; -import { makeInterface } from "../../new_fields/Schema"; -import { Cast, StrCast, NumCast } from "../../new_fields/Types"; +import { documentSchema } from "../../fields/documentSchemas"; +import { InkData, InkField, InkTool } from "../../fields/InkField"; +import { makeInterface } from "../../fields/Schema"; +import { Cast, StrCast, NumCast } from "../../fields/Types"; import { ViewBoxBaseComponent } from "./DocComponent"; import { InkingControl } from "./InkingControl"; import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); -import { TraceMobx } from "../../new_fields/util"; +import { TraceMobx } from "../../fields/util"; import { InteractionUtils } from "../util/InteractionUtils"; import { ContextMenu } from "./ContextMenu"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9bc08de3e..978cf7868 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -13,12 +13,12 @@ import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; import Measure from 'react-measure'; -import { Doc, DocListCast, Field, Opt } from '../../new_fields/Doc'; -import { Id } from '../../new_fields/FieldSymbols'; -import { List } from '../../new_fields/List'; -import { listSpec } from '../../new_fields/Schema'; -import { BoolCast, Cast, FieldValue, StrCast } from '../../new_fields/Types'; -import { TraceMobx } from '../../new_fields/util'; +import { Doc, DocListCast, Field, Opt } from '../../fields/Doc'; +import { Id } from '../../fields/FieldSymbols'; +import { List } from '../../fields/List'; +import { listSpec } from '../../fields/Schema'; +import { BoolCast, Cast, FieldValue, StrCast } from '../../fields/Types'; +import { TraceMobx } from '../../fields/util'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { emptyFunction, emptyPath, returnFalse, returnOne, returnZero, returnTrue, Utils } from '../../Utils'; import GoogleAuthenticationManager from '../apis/GoogleAuthenticationManager'; @@ -48,7 +48,7 @@ import { RadialMenu } from './nodes/RadialMenu'; import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; -import { ScriptField } from '../../new_fields/ScriptField'; +import { ScriptField } from '../../fields/ScriptField'; import { TimelineMenu } from './animationtimeline/TimelineMenu'; import { DragManager } from '../util/DragManager'; import { SnappingManager } from '../util/SnappingManager'; diff --git a/src/client/views/MainViewNotifs.tsx b/src/client/views/MainViewNotifs.tsx index 82e07c449..05f890485 100644 --- a/src/client/views/MainViewNotifs.tsx +++ b/src/client/views/MainViewNotifs.tsx @@ -2,7 +2,7 @@ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; -import { Doc, DocListCast, Opt } from '../../new_fields/Doc'; +import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { emptyFunction } from '../../Utils'; import { SetupDrag } from '../util/DragManager'; import "./MainViewNotifs.scss"; diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx index 8bc80ed06..e100d3f52 100644 --- a/src/client/views/MetadataEntryMenu.tsx +++ b/src/client/views/MetadataEntryMenu.tsx @@ -3,7 +3,7 @@ import "./MetadataEntryMenu.scss"; import { observer } from 'mobx-react'; import { observable, action, runInAction, trace, computed, IReactionDisposer, reaction } from 'mobx'; import { KeyValueBox } from './nodes/KeyValueBox'; -import { Doc, Field, DocListCastAsync } from '../../new_fields/Doc'; +import { Doc, Field, DocListCastAsync } from '../../fields/Doc'; import * as Autosuggest from 'react-autosuggest'; import { undoBatch } from '../util/UndoManager'; import { emptyFunction, emptyPath } from '../../Utils'; diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index afb6bfb7d..bfa44fe47 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,9 +1,9 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; -import { Doc, DocListCast, Opt } from "../../new_fields/Doc"; -import { Id } from "../../new_fields/FieldSymbols"; -import { NumCast } from "../../new_fields/Types"; +import { Doc, DocListCast, Opt } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; +import { NumCast } from "../../fields/Types"; import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero, Utils } from "../../Utils"; import { Transform } from "../util/Transform"; import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView"; diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx index 63744cb50..108eb83d6 100644 --- a/src/client/views/Palette.tsx +++ b/src/client/views/Palette.tsx @@ -1,8 +1,8 @@ import { IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; -import { Doc } from "../../new_fields/Doc"; -import { NumCast } from "../../new_fields/Types"; +import { Doc } from "../../fields/Doc"; +import { NumCast } from "../../fields/Types"; import { emptyFunction, emptyPath, returnEmptyString, returnZero, returnFalse, returnOne, returnTrue } from "../../Utils"; import { Transform } from "../util/Transform"; import { DocumentView } from "./nodes/DocumentView"; diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index f50ac34c8..dd65681d4 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -4,11 +4,11 @@ import "normalize.css"; import * as React from 'react'; import "./PreviewCursor.scss"; import { Docs } from '../documents/Documents'; -import { Doc } from '../../new_fields/Doc'; +import { Doc } from '../../fields/Doc'; import { Transform } from "../util/Transform"; import { DocServer } from '../DocServer'; import { undoBatch } from '../util/UndoManager'; -import { NumCast } from '../../new_fields/Types'; +import { NumCast } from '../../fields/Types'; @observer export class PreviewCursor extends React.Component<{}> { diff --git a/src/client/views/RecommendationsBox.tsx b/src/client/views/RecommendationsBox.tsx index e66fd3eb4..8ca81c070 100644 --- a/src/client/views/RecommendationsBox.tsx +++ b/src/client/views/RecommendationsBox.tsx @@ -3,17 +3,17 @@ import React = require("react"); import { observable, action, computed, runInAction } from "mobx"; import Measure from "react-measure"; import "./RecommendationsBox.scss"; -import { Doc, DocListCast, WidthSym, HeightSym } from "../../new_fields/Doc"; +import { Doc, DocListCast, WidthSym, HeightSym } from "../../fields/Doc"; import { DocumentIcon } from "./nodes/DocumentIcon"; -import { StrCast, NumCast } from "../../new_fields/Types"; +import { StrCast, NumCast } from "../../fields/Types"; import { returnFalse, emptyFunction, returnEmptyString, returnOne, emptyPath, returnZero } from "../../Utils"; import { Transform } from "../util/Transform"; -import { ObjectField } from "../../new_fields/ObjectField"; +import { ObjectField } from "../../fields/ObjectField"; import { DocumentView } from "./nodes/DocumentView"; import { DocumentType } from '../documents/DocumentTypes'; import { ClientRecommender } from "../ClientRecommender"; import { DocServer } from "../DocServer"; -import { Id } from "../../new_fields/FieldSymbols"; +import { Id } from "../../fields/FieldSymbols"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import { DocumentManager } from "../util/DocumentManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index 66d3b937e..888f84dfa 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -5,11 +5,11 @@ import { observable, action } from "mobx"; import "./ScriptBox.scss"; import { OverlayView } from "./OverlayView"; import { DocumentIconContainer } from "./nodes/DocumentIcon"; -import { Opt, Doc } from "../../new_fields/Doc"; +import { Opt, Doc } from "../../fields/Doc"; import { emptyFunction } from "../../Utils"; -import { ScriptCast } from "../../new_fields/Types"; +import { ScriptCast } from "../../fields/Types"; import { CompileScript } from "../util/Scripting"; -import { ScriptField } from "../../new_fields/ScriptField"; +import { ScriptField } from "../../fields/ScriptField"; import { DragManager } from "../util/DragManager"; import { EditableView } from "./EditableView"; import { getEffectiveTypeRoots } from "typescript"; diff --git a/src/client/views/SearchDocBox.tsx b/src/client/views/SearchDocBox.tsx index 7bd689b19..e038d8213 100644 --- a/src/client/views/SearchDocBox.tsx +++ b/src/client/views/SearchDocBox.tsx @@ -3,9 +3,9 @@ import { faBullseye, faLink } from "@fortawesome/free-solid-svg-icons"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; //import "./SearchBoxDoc.scss"; -import { Doc, DocListCast } from "../../new_fields/Doc"; -import { Id } from "../../new_fields/FieldSymbols"; -import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types"; +import { Doc, DocListCast } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; +import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types"; import { returnFalse, returnZero } from "../../Utils"; import { Docs } from "../documents/Documents"; import { SearchUtil } from "../util/SearchUtil"; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 43debfe99..f5e95e4fd 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -6,15 +6,15 @@ import './TemplateMenu.scss'; import { DocumentView } from "./nodes/DocumentView"; import { Template } from "./Templates"; import React = require("react"); -import { Doc, DocListCast } from "../../new_fields/Doc"; +import { Doc, DocListCast } from "../../fields/Doc"; import { Docs, } from "../documents/Documents"; -import { StrCast, Cast } from "../../new_fields/Types"; +import { StrCast, Cast } from "../../fields/Types"; import { CollectionTreeView } from "./collections/CollectionTreeView"; import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero } from "../../Utils"; import { Transform } from "../util/Transform"; -import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; +import { ScriptField, ComputedField } from "../../fields/ScriptField"; import { Scripting } from "../util/Scripting"; -import { List } from "../../new_fields/List"; +import { List } from "../../fields/List"; @observer class TemplateToggle extends React.Component<{ template: Template, checked: boolean, toggle: (event: React.ChangeEvent, template: Template) => void }> { diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index bbd7b2676..92b0f05b3 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -4,10 +4,10 @@ import "./Timeline.scss"; import "../globalCssVariables.scss"; import { observer } from "mobx-react"; import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx"; -import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc"; -import { Cast, NumCast } from "../../../new_fields/Types"; -import { List } from "../../../new_fields/List"; -import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema"; +import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc"; +import { Cast, NumCast } from "../../../fields/Types"; +import { List } from "../../../fields/List"; +import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../fields/Schema"; import { Transform } from "../../util/Transform"; import { TimelineMenu } from "./TimelineMenu"; import { Docs } from "../../documents/Documents"; diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 466cbb867..30692944d 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -1,12 +1,12 @@ import * as React from "react"; import "./Timeline.scss"; -import { listSpec } from "../../../new_fields/Schema"; +import { listSpec } from "../../../fields/Schema"; import { observer } from "mobx-react"; import { Track } from "./Track"; import { observable, action, computed, runInAction, IReactionDisposer, reaction, trace } from "mobx"; -import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; -import { List } from "../../../new_fields/List"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types"; +import { List } from "../../../fields/List"; +import { Doc, DocListCast } from "../../../fields/Doc"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPlayCircle, faBackward, faForward, faGripLines, faPauseCircle, faEyeSlash, faEye, faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons"; import { ContextMenu } from "../ContextMenu"; diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 461db4858..fc96c320a 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -1,12 +1,12 @@ import { action, computed, intercept, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; -import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../new_fields/Doc"; -import { Copy } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import { listSpec } from "../../../new_fields/Schema"; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Doc, DocListCast, Opt, DocListCastAsync } from "../../../fields/Doc"; +import { Copy } from "../../../fields/FieldSymbols"; +import { List } from "../../../fields/List"; +import { ObjectField } from "../../../fields/ObjectField"; +import { listSpec } from "../../../fields/Schema"; +import { Cast, NumCast } from "../../../fields/Types"; import { Transform } from "../../util/Transform"; import { Keyframe, KeyframeFunc, RegionData } from "./Keyframe"; import "./Track.scss"; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index a04136e51..39bb9bc23 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -2,18 +2,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { observable, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { documentSchema, collectionSchema } from '../../../new_fields/documentSchemas'; -import { makeInterface } from '../../../new_fields/Schema'; -import { NumCast, StrCast, ScriptCast, Cast } from '../../../new_fields/Types'; +import { documentSchema, collectionSchema } from '../../../fields/documentSchemas'; +import { makeInterface } from '../../../fields/Schema'; +import { NumCast, StrCast, ScriptCast, Cast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; import "./CollectionCarouselView.scss"; import { CollectionSubView } from './CollectionSubView'; import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons'; -import { Doc } from '../../../new_fields/Doc'; +import { Doc } from '../../../fields/Doc'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { ContextMenu } from '../ContextMenu'; -import { ObjectField } from '../../../new_fields/ObjectField'; +import { ObjectField } from '../../../fields/ObjectField'; import { returnFalse } from '../../../Utils'; type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 581625222..745476ef7 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -7,13 +7,13 @@ import { observer } from "mobx-react"; import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; import * as GoldenLayout from "../../../client/goldenLayout"; -import { DateField } from '../../../new_fields/DateField'; -import { Doc, DocListCast, Field, Opt, DataSym } from "../../../new_fields/Doc"; -import { Id } from '../../../new_fields/FieldSymbols'; -import { List } from '../../../new_fields/List'; -import { FieldId } from "../../../new_fields/RefField"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { TraceMobx } from '../../../new_fields/util'; +import { DateField } from '../../../fields/DateField'; +import { Doc, DocListCast, Field, Opt, DataSym } from "../../../fields/Doc"; +import { Id } from '../../../fields/FieldSymbols'; +import { List } from '../../../fields/List'; +import { FieldId } from "../../../fields/RefField"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnOne, returnTrue, Utils, returnZero } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs } from '../../documents/Documents'; @@ -196,15 +196,16 @@ export class CollectionDockingView extends React.Component => { - for (let i = 0; i < child.contentItems.length; i++) { - if (child.contentItems[i].isRow || child.contentItems[i].isColumn || child.contentItems[i].isStack) { - const val = replaceTab(doc, child.contentItems[i]); + for (const contentItem of child.contentItems) { + const { config, isStack, isRow, isColumn } = contentItem; + if (isRow || isColumn || isStack) { + const val = replaceTab(doc, contentItem); if (val) return val; - } else if (child.contentItems[i].config.component === "DocumentFrameRenderer" && - child.contentItems[i].config.props.documentId === doc[Id]) { + } else if (config.component === "DocumentFrameRenderer" && + config.props.documentId === doc[Id]) { const alias = Doc.MakeAlias(doc); - child.contentItems[i].config.props.documentId = alias[Id]; - child.contentItems[i].config.title = alias.title; + config.props.documentId = alias[Id]; + config.title = alias.title; instance.stateChanged(); return alias; } diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 344dca23a..f1002044a 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -1,9 +1,9 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, HeightSym, WidthSym } from '../../../new_fields/Doc'; -import { makeInterface } from '../../../new_fields/Schema'; -import { BoolCast, NumCast, StrCast, Cast, ScriptCast } from '../../../new_fields/Types'; +import { Doc, HeightSym, WidthSym } from '../../../fields/Doc'; +import { makeInterface } from '../../../fields/Schema'; +import { BoolCast, NumCast, StrCast, Cast, ScriptCast } from '../../../fields/Types'; import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils, returnFalse, returnZero } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; import { Transform } from '../../util/Transform'; @@ -11,8 +11,8 @@ import "./CollectionLinearView.scss"; import { CollectionViewType } from './CollectionView'; import { CollectionSubView } from './CollectionSubView'; import { DocumentView } from '../nodes/DocumentView'; -import { documentSchema } from '../../../new_fields/documentSchemas'; -import { Id } from '../../../new_fields/FieldSymbols'; +import { documentSchema } from '../../../fields/documentSchemas'; +import { Id } from '../../../fields/FieldSymbols'; type LinearDocument = makeInterface<[typeof documentSchema,]>; diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index 618285293..da902cecd 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,10 +1,10 @@ import { GoogleApiWrapper, Map as GeoMap, MapProps as IMapProps, Marker } from "google-maps-react"; import { observer } from "mobx-react"; -import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../new_fields/Doc"; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; +import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { Id } from "../../../fields/FieldSymbols"; +import { makeInterface } from "../../../fields/Schema"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import "./CollectionMapView.scss"; import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 95c7643c9..d6cb174cc 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -4,10 +4,10 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; -import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { StrCast, NumCast } from "../../../new_fields/Types"; +import { Doc } from "../../../fields/Doc"; +import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; +import { ScriptField } from "../../../fields/ScriptField"; +import { StrCast, NumCast } from "../../../fields/Types"; import { numberRange, setupMoveUpEvents, emptyFunction } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index d3ae21f3a..e3bcf2a21 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -1,8 +1,8 @@ import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types"; +import { HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { ScriptField } from "../../../fields/ScriptField"; +import { BoolCast, NumCast, StrCast } from "../../../fields/Types"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 8a5450b0c..62aed67ed 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -4,8 +4,8 @@ import { observer } from "mobx-react"; import { CellInfo } from "react-table"; import "react-table/react-table.css"; import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils"; -import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; +import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; import { KeyCodes } from "../../util/KeyCodes"; import { SetupDrag, DragManager } from "../../util/DragManager"; import { CompileScript } from "../../util/Scripting"; @@ -16,11 +16,11 @@ import { EditableView } from "../EditableView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; import { CollectionView } from "./CollectionView"; -import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../new_fields/Types"; +import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../fields/Types"; import { Docs } from "../../documents/Documents"; import { library } from '@fortawesome/fontawesome-svg-core'; import { faExpand } from '@fortawesome/free-solid-svg-icons'; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; import { SnappingManager } from "../../util/SnappingManager"; diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index 507ee89e4..dae0600b1 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -7,7 +7,7 @@ import { library, IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { ColumnType } from "./CollectionSchemaView"; import { faFile } from "@fortawesome/free-regular-svg-icons"; -import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField, PastelSchemaPalette } from "../../../fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; diff --git a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx index 5aec46a83..6f1e8ac1f 100644 --- a/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx +++ b/src/client/views/collections/CollectionSchemaMovableTableHOC.tsx @@ -2,16 +2,16 @@ import React = require("react"); import { ReactTableDefaults, TableCellRenderer, RowInfo } from "react-table"; import "./CollectionSchemaView.scss"; import { Transform } from "../../util/Transform"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc } from "../../../fields/Doc"; import { DragManager, SetupDrag, dropActionType } from "../../util/DragManager"; -import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; +import { Cast, FieldValue, StrCast } from "../../../fields/Types"; import { ContextMenu } from "../ContextMenu"; import { action } from "mobx"; import { library } from '@fortawesome/fontawesome-svg-core'; import { faGripVertical, faTrash } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { DocumentManager } from "../../util/DocumentManager"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; import { SnappingManager } from "../../util/SnappingManager"; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 51ad6c81b..35f892d65 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -6,13 +6,13 @@ import { action, computed, observable, untracked } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table"; import "react-table/react-table.css"; -import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; -import { listSpec } from "../../../new_fields/Schema"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; -import { ComputedField } from "../../../new_fields/ScriptField"; -import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; +import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; +import { List } from "../../../fields/List"; +import { listSpec } from "../../../fields/Schema"; +import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; +import { ComputedField } from "../../../fields/ScriptField"; +import { Cast, FieldValue, NumCast, StrCast, BoolCast } from "../../../fields/Types"; import { Docs, DocumentOptions } from "../../documents/Documents"; import { CompileScript, Transformer, ts } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index d97f26daf..cc6077d98 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -4,14 +4,14 @@ import { CursorProperty } from "csstype"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import Switch from 'rc-switch'; -import { DataSym, Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; -import { listSpec, makeInterface } from "../../../new_fields/Schema"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../new_fields/Types"; -import { TraceMobx } from "../../../new_fields/util"; +import { DataSym, Doc, HeightSym, WidthSym } from "../../../fields/Doc"; +import { collectionSchema, documentSchema } from "../../../fields/documentSchemas"; +import { Id } from "../../../fields/FieldSymbols"; +import { List } from "../../../fields/List"; +import { listSpec, makeInterface } from "../../../fields/Schema"; +import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; +import { TraceMobx } from "../../../fields/util"; import { emptyFunction, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils, smoothScroll } from "../../../Utils"; import { DragManager, dropActionType } from "../../util/DragManager"; import { Transform } from "../../util/Transform"; diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index dccef7983..53435ccc9 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -4,13 +4,13 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { RichTextField } from "../../../new_fields/RichTextField"; -import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; -import { ImageField } from "../../../new_fields/URLField"; -import { TraceMobx } from "../../../new_fields/util"; +import { Doc, DocListCast } from "../../../fields/Doc"; +import { RichTextField } from "../../../fields/RichTextField"; +import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField"; +import { ScriptField } from "../../../fields/ScriptField"; +import { NumCast, StrCast, Cast } from "../../../fields/Types"; +import { ImageField } from "../../../fields/URLField"; +import { TraceMobx } from "../../../fields/util"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { Transform } from "../../util/Transform"; @@ -21,7 +21,7 @@ import { EditableView } from "../EditableView"; import { CollectionStackingView } from "./CollectionStackingView"; import { setupMoveUpEvents, emptyFunction } from "../../../Utils"; import "./CollectionStackingView.scss"; -import { listSpec } from "../../../new_fields/Schema"; +import { listSpec } from "../../../fields/Schema"; import { SnappingManager } from "../../util/SnappingManager"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx index 5b9a69bf7..c5c3f96e8 100644 --- a/src/client/views/collections/CollectionStaffView.tsx +++ b/src/client/views/collections/CollectionStaffView.tsx @@ -1,7 +1,7 @@ import { CollectionSubView } from "./CollectionSubView"; import React = require("react"); import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx"; -import { NumCast } from "../../../new_fields/Types"; +import { NumCast } from "../../../fields/Types"; import "./CollectionStaffView.scss"; import { observer } from "mobx-react"; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index bff6d121b..a4e843c8e 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,12 +1,12 @@ import { action, computed, IReactionDisposer, reaction } from "mobx"; import { basename } from 'path'; -import CursorField from "../../../new_fields/CursorField"; -import { Doc, Opt } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; -import { listSpec } from "../../../new_fields/Schema"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { Cast, ScriptCast } from "../../../new_fields/Types"; +import CursorField from "../../../fields/CursorField"; +import { Doc, Opt } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; +import { List } from "../../../fields/List"; +import { listSpec } from "../../../fields/Schema"; +import { ScriptField } from "../../../fields/ScriptField"; +import { Cast, ScriptCast } from "../../../fields/Types"; import { GestureUtils } from "../../../pen-gestures/GestureUtils"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { Upload } from "../../../server/SharedMediaTypes"; diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index a2d4774c8..15bc0bfd5 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -1,11 +1,11 @@ import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, DocCastAsync } from "../../../new_fields/Doc"; -import { List } from "../../../new_fields/List"; -import { ObjectField } from "../../../new_fields/ObjectField"; -import { RichTextField } from "../../../new_fields/RichTextField"; -import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; -import { NumCast, StrCast, BoolCast, Cast } from "../../../new_fields/Types"; +import { Doc, Opt, DocCastAsync } from "../../../fields/Doc"; +import { List } from "../../../fields/List"; +import { ObjectField } from "../../../fields/ObjectField"; +import { RichTextField } from "../../../fields/RichTextField"; +import { ComputedField, ScriptField } from "../../../fields/ScriptField"; +import { NumCast, StrCast, BoolCast, Cast } from "../../../fields/Types"; import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; import { Scripting } from "../../util/Scripting"; import { ContextMenu } from "../ContextMenu"; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2f332e77d..3e99af724 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,13 +1,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc'; -import { Id } from '../../../new_fields/FieldSymbols'; -import { List } from '../../../new_fields/List'; -import { PrefetchProxy } from '../../../new_fields/Proxy'; -import { Document, listSpec } from '../../../new_fields/Schema'; -import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; +import { DataSym, Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; +import { List } from '../../../fields/List'; +import { PrefetchProxy } from '../../../fields/Proxy'; +import { Document, listSpec } from '../../../fields/Schema'; +import { ComputedField, ScriptField } from '../../../fields/ScriptField'; +import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { emptyFunction, emptyPath, returnFalse, returnOne, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from "../../documents/DocumentTypes"; @@ -32,7 +32,7 @@ import "./CollectionTreeView.scss"; import { CollectionViewType } from './CollectionView'; import React = require("react"); import { makeTemplate } from '../../util/DropConverter'; -import { TraceMobx } from '../../../new_fields/util'; +import { TraceMobx } from '../../../fields/util'; export interface TreeViewProps { document: Doc; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 4b3b2e234..3b2e5e4fc 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -7,12 +7,12 @@ import { observer } from "mobx-react"; import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app -import { DateField } from '../../../new_fields/DateField'; -import { DataSym, Doc, DocListCast, Field, Opt } from '../../../new_fields/Doc'; -import { List } from '../../../new_fields/List'; -import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../new_fields/Types'; -import { ImageField } from '../../../new_fields/URLField'; -import { TraceMobx } from '../../../new_fields/util'; +import { DateField } from '../../../fields/DateField'; +import { DataSym, Doc, DocListCast, Field, Opt } from '../../../fields/Doc'; +import { List } from '../../../fields/List'; +import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from '../../../fields/Types'; +import { ImageField } from '../../../fields/URLField'; +import { TraceMobx } from '../../../fields/util'; import { Utils, setupMoveUpEvents, returnFalse, returnZero, emptyPath, emptyFunction, returnOne } from '../../../Utils'; import { DocumentType } from '../../documents/DocumentTypes'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; @@ -36,12 +36,12 @@ import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; import { CollectionViewBaseChrome } from './CollectionViewChromes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { Id } from '../../../new_fields/FieldSymbols'; -import { listSpec } from '../../../new_fields/Schema'; +import { Id } from '../../../fields/FieldSymbols'; +import { listSpec } from '../../../fields/Schema'; import { Docs } from '../../documents/Documents'; -import { ScriptField, ComputedField } from '../../../new_fields/ScriptField'; +import { ScriptField, ComputedField } from '../../../fields/ScriptField'; import { InteractionUtils } from '../../util/InteractionUtils'; -import { ObjectField } from '../../../new_fields/ObjectField'; +import { ObjectField } from '../../../fields/ObjectField'; import CollectionMapView from './CollectionMapView'; import { CollectionPileView } from './CollectionPileView'; const higflyout = require("@hig/flyout"); diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 62b03bbdc..5dc0b09ac 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -2,11 +2,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; -import { listSpec } from "../../../new_fields/Schema"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Doc, DocListCast } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; +import { List } from "../../../fields/List"; +import { listSpec } from "../../../fields/Schema"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; import { Utils, emptyFunction, setupMoveUpEvents } from "../../../Utils"; import { DragManager } from "../../util/DragManager"; import { undoBatch } from "../../util/UndoManager"; diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 6e4f801c0..649406e6c 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -1,12 +1,12 @@ import * as React from "react"; import './ParentDocumentSelector.scss'; -import { Doc } from "../../../new_fields/Doc"; +import { Doc } from "../../../fields/Doc"; import { observer } from "mobx-react"; import { observable, action, runInAction, trace, computed, reaction, IReactionDisposer } from "mobx"; -import { Id } from "../../../new_fields/FieldSymbols"; +import { Id } from "../../../fields/FieldSymbols"; import { SearchUtil } from "../../util/SearchUtil"; import { CollectionDockingView } from "./CollectionDockingView"; -import { NumCast, StrCast } from "../../../new_fields/Types"; +import { NumCast, StrCast } from "../../../fields/Types"; import { CollectionViewType } from "./CollectionView"; import { DocumentButtonBar } from "../DocumentButtonBar"; import { DocumentManager } from "../../util/DocumentManager"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 9a864078a..3860ce2d7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -1,15 +1,15 @@ -import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../new_fields/Doc"; -import { NumCast, StrCast, Cast } from "../../../../new_fields/Types"; +import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../fields/Doc"; +import { NumCast, StrCast, Cast } from "../../../../fields/Types"; import { ScriptBox } from "../../ScriptBox"; import { CompileScript } from "../../../util/Scripting"; -import { ScriptField } from "../../../../new_fields/ScriptField"; +import { ScriptField } from "../../../../fields/ScriptField"; import { OverlayView, OverlayElementOptions } from "../../OverlayView"; import { emptyFunction, aggregateBounds } from "../../../../Utils"; import React = require("react"); -import { Id, ToString } from "../../../../new_fields/FieldSymbols"; -import { ObjectField } from "../../../../new_fields/ObjectField"; -import { RefField } from "../../../../new_fields/RefField"; -import { listSpec } from "../../../../new_fields/Schema"; +import { Id, ToString } from "../../../../fields/FieldSymbols"; +import { ObjectField } from "../../../../fields/ObjectField"; +import { RefField } from "../../../../fields/RefField"; +import { listSpec } from "../../../../fields/Schema"; export interface ViewDefBounds { type: string; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index ba71aff32..f3fc04752 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react"; -import { Doc } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../fields/Doc"; import { Utils } from '../../../../Utils'; import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinkView.scss"; @@ -7,8 +7,8 @@ import React = require("react"); import v5 = require("uuid/v5"); import { DocumentType } from "../../../documents/DocumentTypes"; import { observable, action, reaction, IReactionDisposer } from "mobx"; -import { StrCast, Cast } from "../../../../new_fields/Types"; -import { Id } from "../../../../new_fields/FieldSymbols"; +import { StrCast, Cast } from "../../../../fields/Types"; +import { Id } from "../../../../fields/FieldSymbols"; import { SnappingManager } from "../../../util/SnappingManager"; export interface CollectionFreeFormLinkViewProps { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index 1208fb324..ae81b4b36 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -1,7 +1,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../../new_fields/Doc"; -import { Id } from "../../../../new_fields/FieldSymbols"; +import { Doc } from "../../../../fields/Doc"; +import { Id } from "../../../../fields/FieldSymbols"; import { DocumentManager } from "../../../util/DocumentManager"; import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinksView.scss"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx index 9a5b2c27c..548ad78a5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx @@ -1,16 +1,16 @@ import { observer } from "mobx-react"; import * as mobxUtils from 'mobx-utils'; -import CursorField from "../../../../new_fields/CursorField"; -import { listSpec } from "../../../../new_fields/Schema"; -import { Cast } from "../../../../new_fields/Types"; +import CursorField from "../../../../fields/CursorField"; +import { listSpec } from "../../../../fields/Schema"; +import { Cast } from "../../../../fields/Types"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { CollectionViewProps } from "../CollectionSubView"; import "./CollectionFreeFormView.scss"; import React = require("react"); import v5 = require("uuid/v5"); import { computed } from "mobx"; -import { FieldResult } from "../../../../new_fields/Doc"; -import { List } from "../../../../new_fields/List"; +import { FieldResult } from "../../../../fields/Doc"; +import { List } from "../../../../fields/List"; @observer export class CollectionFreeFormRemoteCursors extends React.Component { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6c4660c39..fb8bd25da 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -4,16 +4,16 @@ import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrows import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, _allowStateChangesInsideComputed } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; -import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc"; -import { documentSchema, collectionSchema } from "../../../../new_fields/documentSchemas"; -import { Id } from "../../../../new_fields/FieldSymbols"; -import { InkData, InkField, InkTool, PointData } from "../../../../new_fields/InkField"; -import { List } from "../../../../new_fields/List"; -import { RichTextField } from "../../../../new_fields/RichTextField"; -import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema"; -import { ScriptField } from "../../../../new_fields/ScriptField"; -import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types"; -import { TraceMobx } from "../../../../new_fields/util"; +import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../fields/Doc"; +import { documentSchema, collectionSchema } from "../../../../fields/documentSchemas"; +import { Id } from "../../../../fields/FieldSymbols"; +import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkField"; +import { List } from "../../../../fields/List"; +import { RichTextField } from "../../../../fields/RichTextField"; +import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema"; +import { ScriptField } from "../../../../fields/ScriptField"; +import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; +import { TraceMobx } from "../../../../fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; import { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; @@ -1034,8 +1034,7 @@ export class CollectionFreeFormView extends CollectionSubView { - for (let i = 0; i < array.length; i++) { - const entry = array[i]; + for (const entry of array) { const lastPos = this._cachedPool.get(entry[0]); // last computed pos const newPos = entry[1]; if (!lastPos || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 8e20b39d2..04d37934e 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -1,11 +1,11 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt } from "../../../../new_fields/Doc"; -import { InkData, InkField } from "../../../../new_fields/InkField"; -import { List } from "../../../../new_fields/List"; -import { RichTextField } from "../../../../new_fields/RichTextField"; -import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField"; -import { Cast, FieldValue, NumCast, StrCast } from "../../../../new_fields/Types"; +import { Doc, Opt } from "../../../../fields/Doc"; +import { InkData, InkField } from "../../../../fields/InkField"; +import { List } from "../../../../fields/List"; +import { RichTextField } from "../../../../fields/RichTextField"; +import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; +import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types"; import { Utils } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents"; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index a09124304..c0e1a0232 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -1,10 +1,10 @@ import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from "react"; -import { Doc } from '../../../../new_fields/Doc'; -import { documentSchema } from '../../../../new_fields/documentSchemas'; -import { makeInterface } from '../../../../new_fields/Schema'; -import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../new_fields/Types'; +import { Doc } from '../../../../fields/Doc'; +import { documentSchema } from '../../../../fields/documentSchemas'; +import { makeInterface } from '../../../../fields/Schema'; +import { BoolCast, NumCast, ScriptCast, StrCast, Cast } from '../../../../fields/Types'; import { DragManager, dropActionType } from '../../../util/DragManager'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; @@ -13,7 +13,7 @@ import { CollectionSubView } from '../CollectionSubView'; import "./collectionMulticolumnView.scss"; import ResizeBar from './MulticolumnResizer'; import WidthLabel from './MulticolumnWidthLabel'; -import { List } from '../../../../new_fields/List'; +import { List } from '../../../../fields/List'; import { returnZero, returnFalse, returnOne } from '../../../../Utils'; type MulticolumnDocument = makeInterface<[typeof documentSchema]>; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 4326723b1..602246d07 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -1,10 +1,10 @@ import { observer } from 'mobx-react'; -import { makeInterface } from '../../../../new_fields/Schema'; -import { documentSchema } from '../../../../new_fields/documentSchemas'; +import { makeInterface } from '../../../../fields/Schema'; +import { documentSchema } from '../../../../fields/documentSchemas'; import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import * as React from "react"; -import { Doc } from '../../../../new_fields/Doc'; -import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../new_fields/Types'; +import { Doc } from '../../../../fields/Doc'; +import { NumCast, StrCast, BoolCast, ScriptCast } from '../../../../fields/Types'; import { ContentFittingDocumentView } from '../../nodes/ContentFittingDocumentView'; import { Utils, returnZero, returnFalse, returnOne } from '../../../../Utils'; import "./collectionMultirowView.scss"; @@ -14,7 +14,7 @@ import HeightLabel from './MultirowHeightLabel'; import ResizeBar from './MultirowResizer'; import { undoBatch } from '../../../util/UndoManager'; import { DragManager, dropActionType } from '../../../util/DragManager'; -import { List } from '../../../../new_fields/List'; +import { List } from '../../../../fields/List'; type MultirowDocument = makeInterface<[typeof documentSchema]>; const MultirowDocument = makeInterface(documentSchema); diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx index a24eb1631..734915a93 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx @@ -1,8 +1,8 @@ import * as React from "react"; import { observer } from "mobx-react"; import { observable, action } from "mobx"; -import { Doc } from "../../../../new_fields/Doc"; -import { NumCast, StrCast } from "../../../../new_fields/Types"; +import { Doc } from "../../../../fields/Doc"; +import { NumCast, StrCast } from "../../../../fields/Types"; import { DimUnit } from "./CollectionMulticolumnView"; import { UndoManager } from "../../../util/UndoManager"; diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx index 5b2054428..9985a9fba 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx @@ -1,8 +1,8 @@ import * as React from "react"; import { observer } from "mobx-react"; import { computed } from "mobx"; -import { Doc } from "../../../../new_fields/Doc"; -import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types"; +import { Doc } from "../../../../fields/Doc"; +import { NumCast, StrCast, BoolCast } from "../../../../fields/Types"; import { EditableView } from "../../EditableView"; import { DimUnit } from "./CollectionMulticolumnView"; diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx index 899577fd5..aa5439fa4 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx @@ -1,8 +1,8 @@ import * as React from "react"; import { observer } from "mobx-react"; import { computed } from "mobx"; -import { Doc } from "../../../../new_fields/Doc"; -import { NumCast, StrCast, BoolCast } from "../../../../new_fields/Types"; +import { Doc } from "../../../../fields/Doc"; +import { NumCast, StrCast, BoolCast } from "../../../../fields/Types"; import { EditableView } from "../../EditableView"; import { DimUnit } from "./CollectionMultirowView"; diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx index 5f00b18b9..d0bc4d01c 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx @@ -1,8 +1,8 @@ import * as React from "react"; import { observer } from "mobx-react"; import { observable, action } from "mobx"; -import { Doc } from "../../../../new_fields/Doc"; -import { NumCast, StrCast } from "../../../../new_fields/Types"; +import { Doc } from "../../../../fields/Doc"; +import { NumCast, StrCast } from "../../../../fields/Types"; import { DimUnit } from "./CollectionMultirowView"; import { UndoManager } from "../../../util/UndoManager"; diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index b7f3dd995..5fb4cf3c6 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -3,8 +3,8 @@ import { faArrowLeft, faCog, faEllipsisV, faExchangeAlt, faPlus, faTable, faTime import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; -import { StrCast } from "../../../new_fields/Types"; +import { Doc } from "../../../fields/Doc"; +import { StrCast } from "../../../fields/Types"; import { Utils } from "../../../Utils"; import { LinkManager } from "../../util/LinkManager"; import './LinkEditor.scss'; diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index b768eacc3..786d6be47 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -4,7 +4,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); -import { Doc } from "../../../new_fields/Doc"; +import { Doc } from "../../../fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { LinkMenuGroup } from "./LinkMenuGroup"; import { faTrash } from '@fortawesome/free-solid-svg-icons'; diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index c97e1062d..89deb3a55 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -1,9 +1,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { Doc } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; +import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { Docs } from "../../documents/Documents"; import { DragManager, SetupDrag } from "../../util/DragManager"; import { LinkManager } from "../../util/LinkManager"; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index b0c61cb04..17cd33241 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -3,8 +3,8 @@ import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc, DocListCast } from '../../../new_fields/Doc'; -import { Cast, StrCast } from '../../../new_fields/Types'; +import { Doc, DocListCast } from '../../../fields/Doc'; +import { Cast, StrCast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { ContextMenu } from '../ContextMenu'; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 1c5e13620..1a935d9b0 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -2,23 +2,23 @@ import React = require("react"); import { FieldViewProps, FieldView } from './FieldView'; import { observer } from "mobx-react"; import "./AudioBox.scss"; -import { Cast, DateCast, NumCast } from "../../../new_fields/Types"; -import { AudioField, nullAudio } from "../../../new_fields/URLField"; +import { Cast, DateCast, NumCast } from "../../../fields/Types"; +import { AudioField, nullAudio } from "../../../fields/URLField"; import { ViewBoxBaseComponent } from "../DocComponent"; -import { makeInterface, createSchema } from "../../../new_fields/Schema"; -import { documentSchema } from "../../../new_fields/documentSchemas"; +import { makeInterface, createSchema } from "../../../fields/Schema"; +import { documentSchema } from "../../../fields/documentSchemas"; import { Utils, returnTrue, emptyFunction, returnOne, returnTransparent, returnFalse, returnZero } from "../../../Utils"; import { runInAction, observable, reaction, IReactionDisposer, computed, action } from "mobx"; -import { DateField } from "../../../new_fields/DateField"; +import { DateField } from "../../../fields/DateField"; import { SelectionManager } from "../../util/SelectionManager"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../fields/Doc"; import { ContextMenuProps } from "../ContextMenuItem"; import { ContextMenu } from "../ContextMenu"; -import { Id } from "../../../new_fields/FieldSymbols"; +import { Id } from "../../../fields/FieldSymbols"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { DocumentView } from "./DocumentView"; import { Docs, DocUtils } from "../../documents/Documents"; -import { ComputedField } from "../../../new_fields/ScriptField"; +import { ComputedField } from "../../../fields/ScriptField"; import { Networking } from "../../Network"; import { LinkAnchorBox } from "./LinkAnchorBox"; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index cdbe506a5..5d6e587d9 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,14 +1,14 @@ import { computed, IReactionDisposer, observable, reaction, trace } from "mobx"; import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Doc, HeightSym, WidthSym } from "../../../fields/Doc"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { Transform } from "../../util/Transform"; import { DocComponent } from "../DocComponent"; import "./CollectionFreeFormDocumentView.scss"; import { DocumentView, DocumentViewProps } from "./DocumentView"; import React = require("react"); -import { Document } from "../../../new_fields/documentSchemas"; -import { TraceMobx } from "../../../new_fields/util"; +import { Document } from "../../../fields/documentSchemas"; +import { TraceMobx } from "../../../fields/util"; import { ContentFittingDocumentView } from "./ContentFittingDocumentView"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index bc84204b9..bef6609c4 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -1,9 +1,9 @@ import React = require("react"); import { observer } from "mobx-react"; import { SketchPicker } from 'react-color'; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { makeInterface } from "../../../new_fields/Schema"; -import { StrCast } from "../../../new_fields/Types"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface } from "../../../fields/Schema"; +import { StrCast } from "../../../fields/Types"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { SelectionManager } from "../../util/SelectionManager"; import { ViewBoxBaseComponent } from "../DocComponent"; diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index 1c6250b94..a90b4668e 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -2,9 +2,9 @@ import React = require("react"); import { computed } from "mobx"; import { observer } from "mobx-react"; import "react-table/react-table.css"; -import { Doc, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc"; -import { NumCast, StrCast, Cast } from "../../../new_fields/Types"; -import { TraceMobx } from "../../../new_fields/util"; +import { Doc, Opt, WidthSym, HeightSym } from "../../../fields/Doc"; +import { NumCast, StrCast, Cast } from "../../../fields/Types"; +import { TraceMobx } from "../../../fields/util"; import { emptyFunction, returnOne } from "../../../Utils"; import '../DocumentDecorations.scss'; import { DocumentView, DocumentViewProps } from "../nodes/DocumentView"; diff --git a/src/client/views/nodes/DocHolderBox.tsx b/src/client/views/nodes/DocHolderBox.tsx index af8dc704c..0c5239d66 100644 --- a/src/client/views/nodes/DocHolderBox.tsx +++ b/src/client/views/nodes/DocHolderBox.tsx @@ -1,12 +1,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Field } from "../../../new_fields/Doc"; -import { collectionSchema, documentSchema } from "../../../new_fields/documentSchemas"; -import { makeInterface, listSpec } from "../../../new_fields/Schema"; -import { ComputedField } from "../../../new_fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { TraceMobx } from "../../../new_fields/util"; +import { Doc, Field } from "../../../fields/Doc"; +import { collectionSchema, documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface, listSpec } from "../../../fields/Schema"; +import { ComputedField } from "../../../fields/ScriptField"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { TraceMobx } from "../../../fields/util"; import { emptyPath, returnFalse, returnOne, returnZero } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { DragManager } from "../../util/DragManager"; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 2fc82e524..f4785bb0c 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -1,7 +1,7 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Opt, Field } from "../../../new_fields/Doc"; -import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; +import { Doc, Opt, Field } from "../../../fields/Doc"; +import { Cast, StrCast, NumCast } from "../../../fields/Types"; import { OmitKeys, Without, emptyPath } from "../../../Utils"; import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; import { CollectionDockingView } from "../collections/CollectionDockingView"; @@ -35,8 +35,8 @@ import { WebBox } from "./WebBox"; import { InkingStroke } from "../InkingStroke"; import React = require("react"); import { RecommendationsBox } from "../RecommendationsBox"; -import { TraceMobx } from "../../../new_fields/util"; -import { ScriptField } from "../../../new_fields/ScriptField"; +import { TraceMobx } from "../../../fields/util"; +import { ScriptField } from "../../../fields/ScriptField"; import XRegExp = require("xregexp"); const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx index f56f5e829..fb54f18e8 100644 --- a/src/client/views/nodes/DocumentIcon.tsx +++ b/src/client/views/nodes/DocumentIcon.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import { DocumentView } from "./DocumentView"; import { DocumentManager } from "../../util/DocumentManager"; import { Transformer, Scripting, ts } from "../../util/Scripting"; -import { Field } from "../../../new_fields/Doc"; +import { Field } from "../../../fields/Doc"; @observer export class DocumentIcon extends React.Component<{ view: DocumentView, index: number }> { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d821bb8ac..340fa06a8 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -4,16 +4,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym } from "../../../new_fields/Doc"; -import { Document } from '../../../new_fields/documentSchemas'; -import { Id } from '../../../new_fields/FieldSymbols'; -import { InkTool } from '../../../new_fields/InkField'; -import { listSpec } from "../../../new_fields/Schema"; -import { SchemaHeaderField } from '../../../new_fields/SchemaHeaderField'; -import { ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { ImageField } from '../../../new_fields/URLField'; -import { TraceMobx } from '../../../new_fields/util'; +import { Doc, DocListCast, HeightSym, Opt, WidthSym, DataSym } from "../../../fields/Doc"; +import { Document } from '../../../fields/documentSchemas'; +import { Id } from '../../../fields/FieldSymbols'; +import { InkTool } from '../../../fields/InkField'; +import { listSpec } from "../../../fields/Schema"; +import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; +import { ScriptField } from '../../../fields/ScriptField'; +import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; +import { ImageField } from '../../../fields/URLField'; +import { TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { emptyFunction, OmitKeys, returnOne, returnTransparent, Utils, emptyPath } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; diff --git a/src/client/views/nodes/FaceRectangles.tsx b/src/client/views/nodes/FaceRectangles.tsx index 3c7f1f206..92ca276cb 100644 --- a/src/client/views/nodes/FaceRectangles.tsx +++ b/src/client/views/nodes/FaceRectangles.tsx @@ -1,8 +1,8 @@ import React = require("react"); -import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { Cast, NumCast } from "../../../new_fields/Types"; +import { Doc, DocListCast } from "../../../fields/Doc"; +import { Cast, NumCast } from "../../../fields/Types"; import { observer } from "mobx-react"; -import { Id } from "../../../new_fields/FieldSymbols"; +import { Id } from "../../../fields/FieldSymbols"; import FaceRectangle from "./FaceRectangle"; interface FaceRectanglesProps { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 2ab0a416d..e9dc43bd8 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -1,11 +1,11 @@ import React = require("react"); import { computed } from "mobx"; import { observer } from "mobx-react"; -import { DateField } from "../../../new_fields/DateField"; -import { Doc, FieldResult, Opt, Field } from "../../../new_fields/Doc"; -import { List } from "../../../new_fields/List"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { AudioField, VideoField } from "../../../new_fields/URLField"; +import { DateField } from "../../../fields/DateField"; +import { Doc, FieldResult, Opt, Field } from "../../../fields/Doc"; +import { List } from "../../../fields/List"; +import { ScriptField } from "../../../fields/ScriptField"; +import { AudioField, VideoField } from "../../../fields/URLField"; import { Transform } from "../../util/Transform"; import { CollectionView } from "../collections/CollectionView"; import { AudioBox } from "./AudioBox"; diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index 0d597b3a8..cf0b16c7c 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -1,16 +1,16 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { createSchema, makeInterface } from '../../../new_fields/Schema'; +import { createSchema, makeInterface } from '../../../fields/Schema'; import { DocComponent } from '../DocComponent'; import './FontIconBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; -import { StrCast, Cast } from '../../../new_fields/Types'; +import { StrCast, Cast } from '../../../fields/Types'; import { Utils } from "../../../Utils"; import { runInAction, observable, reaction, IReactionDisposer } from 'mobx'; -import { Doc } from '../../../new_fields/Doc'; +import { Doc } from '../../../fields/Doc'; import { ContextMenu } from '../ContextMenu'; -import { ScriptField } from '../../../new_fields/ScriptField'; +import { ScriptField } from '../../../fields/ScriptField'; const FontIconSchema = createSchema({ icon: "string" }); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index aaebceaa2..47e7607d6 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -4,16 +4,16 @@ import { faAsterisk, faBrain, faFileAudio, faImage, faPaintBrush } from '@fortaw import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; -import { documentSchema } from '../../../new_fields/documentSchemas'; -import { Id } from '../../../new_fields/FieldSymbols'; -import { List } from '../../../new_fields/List'; -import { ObjectField } from '../../../new_fields/ObjectField'; -import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; -import { ComputedField } from '../../../new_fields/ScriptField'; -import { Cast, NumCast, StrCast } from '../../../new_fields/Types'; -import { AudioField, ImageField } from '../../../new_fields/URLField'; -import { TraceMobx } from '../../../new_fields/util'; +import { DataSym, Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; +import { Id } from '../../../fields/FieldSymbols'; +import { List } from '../../../fields/List'; +import { ObjectField } from '../../../fields/ObjectField'; +import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; +import { ComputedField } from '../../../fields/ScriptField'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { AudioField, ImageField } from '../../../fields/URLField'; +import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnOne, Utils, returnZero } from '../../../Utils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs } from '../../documents/Documents'; diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 39d7109b1..e983852ea 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -1,13 +1,13 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, Field, FieldResult } from "../../../new_fields/Doc"; -import { List } from "../../../new_fields/List"; -import { RichTextField } from "../../../new_fields/RichTextField"; -import { listSpec } from "../../../new_fields/Schema"; -import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; -import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; -import { ImageField } from "../../../new_fields/URLField"; +import { Doc, Field, FieldResult } from "../../../fields/Doc"; +import { List } from "../../../fields/List"; +import { RichTextField } from "../../../fields/RichTextField"; +import { listSpec } from "../../../fields/Schema"; +import { ComputedField, ScriptField } from "../../../fields/ScriptField"; +import { Cast, FieldValue, NumCast } from "../../../fields/Types"; +import { ImageField } from "../../../fields/URLField"; import { Docs } from "../../documents/Documents"; import { SetupDrag } from "../../util/DragManager"; import { CompiledScript, CompileScript, ScriptOptions } from "../../util/Scripting"; diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 6dc4ae578..956d6556b 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -1,6 +1,6 @@ import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc, Field, Opt } from '../../../new_fields/Doc'; +import { Doc, Field, Opt } from '../../../fields/Doc'; import { emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { Transform } from '../../util/Transform'; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index ac27640bd..2d27ec441 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -3,11 +3,11 @@ import { faEdit } from '@fortawesome/free-regular-svg-icons'; import { action } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast } from '../../../new_fields/Doc'; -import { documentSchema } from '../../../new_fields/documentSchemas'; -import { List } from '../../../new_fields/List'; -import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; -import { Cast, NumCast, StrCast } from '../../../new_fields/Types'; +import { Doc, DocListCast } from '../../../fields/Doc'; +import { documentSchema } from '../../../fields/documentSchemas'; +import { List } from '../../../fields/List'; +import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index bc36e056e..098aa58e9 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -1,9 +1,9 @@ import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Doc, DocListCast } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface } from "../../../fields/Schema"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { Utils, setupMoveUpEvents, emptyFunction } from '../../../Utils'; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager } from "../../util/DragManager"; @@ -16,7 +16,7 @@ import { ContextMenu } from "../ContextMenu"; import { LinkEditor } from "../linking/LinkEditor"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { SelectionManager } from "../../util/SelectionManager"; -import { TraceMobx } from "../../../new_fields/util"; +import { TraceMobx } from "../../../fields/util"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 740f2ef04..3f942e87b 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -1,13 +1,13 @@ import React = require("react"); import { observer } from "mobx-react"; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { makeInterface, listSpec } from "../../../new_fields/Schema"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface, listSpec } from "../../../fields/Schema"; import { returnFalse, returnZero } from "../../../Utils"; import { CollectionTreeView } from "../collections/CollectionTreeView"; import { ViewBoxBaseComponent } from "../DocComponent"; import { FieldView, FieldViewProps } from './FieldView'; import "./LinkBox.scss"; -import { Cast } from "../../../new_fields/Types"; +import { Cast } from "../../../fields/Types"; type LinkDocument = makeInterface<[typeof documentSchema]>; const LinkDocument = makeInterface(documentSchema); diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3712c648e..493f23dc4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -3,11 +3,11 @@ import { action, observable, runInAction, reaction, IReactionDisposer, trace, un import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; -import { Opt, WidthSym, Doc, HeightSym } from "../../../new_fields/Doc"; -import { makeInterface } from "../../../new_fields/Schema"; -import { ScriptField } from '../../../new_fields/ScriptField'; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { PdfField, URLField } from "../../../new_fields/URLField"; +import { Opt, WidthSym, Doc, HeightSym } from "../../../fields/Doc"; +import { makeInterface } from "../../../fields/Schema"; +import { ScriptField } from '../../../fields/ScriptField'; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { PdfField, URLField } from "../../../fields/URLField"; import { Utils } from '../../../Utils'; import { undoBatch } from '../../util/UndoManager'; import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -20,7 +20,7 @@ import { pageSchema } from "./ImageBox"; import { KeyCodes } from '../../util/KeyCodes'; import "./PDFBox.scss"; import React = require("react"); -import { documentSchema } from '../../../new_fields/documentSchemas'; +import { documentSchema } from '../../../fields/documentSchemas'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 42552ec73..342a8a215 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -2,11 +2,11 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, DocCastAsync } from "../../../new_fields/Doc"; -import { InkTool } from "../../../new_fields/InkField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Doc, DocListCast, DocCastAsync } from "../../../fields/Doc"; +import { InkTool } from "../../../fields/InkField"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; import { returnFalse } from "../../../Utils"; -import { documentSchema } from "../../../new_fields/documentSchemas"; +import { documentSchema } from "../../../fields/documentSchemas"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; import { CollectionDockingView } from "../collections/CollectionDockingView"; @@ -15,11 +15,11 @@ import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; import "./PresBox.scss"; import { ViewBoxBaseComponent } from "../DocComponent"; -import { makeInterface } from "../../../new_fields/Schema"; -import { List } from "../../../new_fields/List"; +import { makeInterface } from "../../../fields/Schema"; +import { List } from "../../../fields/List"; import { Docs } from "../../documents/Documents"; -import { PrefetchProxy } from "../../../new_fields/Proxy"; -import { ScriptField } from "../../../new_fields/ScriptField"; +import { PrefetchProxy } from "../../../fields/Proxy"; +import { ScriptField } from "../../../fields/ScriptField"; import { Scripting } from "../../util/Scripting"; type PresBoxSchema = makeInterface<[typeof documentSchema]>; diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx index 2a21a380d..0fff0b57f 100644 --- a/src/client/views/nodes/QueryBox.tsx +++ b/src/client/views/nodes/QueryBox.tsx @@ -1,15 +1,15 @@ import React = require("react"); import { IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { Id } from '../../../new_fields/FieldSymbols'; -import { makeInterface, listSpec } from "../../../new_fields/Schema"; -import { StrCast, Cast } from "../../../new_fields/Types"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { Id } from '../../../fields/FieldSymbols'; +import { makeInterface, listSpec } from "../../../fields/Schema"; +import { StrCast, Cast } from "../../../fields/Types"; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { SearchBox } from "../search/SearchBox"; import { FieldView, FieldViewProps } from './FieldView'; import "./QueryBox.scss"; -import { List } from "../../../new_fields/List"; +import { List } from "../../../fields/List"; import { SnappingManager } from "../../util/SnappingManager"; type QueryDocument = makeInterface<[typeof documentSchema]>; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index a0ecc9ff5..5d4af2d77 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -5,10 +5,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from 'request-promise'; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast } from "../../../new_fields/Types"; -import { VideoField } from "../../../new_fields/URLField"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { makeInterface } from "../../../fields/Schema"; +import { Cast, NumCast } from "../../../fields/Types"; +import { VideoField } from "../../../fields/URLField"; import { emptyFunction, returnFalse, returnOne, Utils, returnZero } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index c607d6614..0944edf60 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -1,10 +1,10 @@ import { action, observable, computed } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { createSchema, makeInterface, listSpec } from "../../../new_fields/Schema"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { StrCast, ScriptCast, Cast } from "../../../new_fields/Types"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { createSchema, makeInterface, listSpec } from "../../../fields/Schema"; +import { ScriptField } from "../../../fields/ScriptField"; +import { StrCast, ScriptCast, Cast } from "../../../fields/Types"; import { InteractionUtils } from "../../util/InteractionUtils"; import { CompileScript, isCompileError, ScriptParam } from "../../util/Scripting"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; @@ -13,7 +13,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./ScriptingBox.scss"; import { OverlayView } from "../OverlayView"; import { DocumentIconContainer } from "./DocumentIcon"; -import { List } from "../../../new_fields/List"; +import { List } from "../../../fields/List"; const ScriptingSchema = createSchema({}); type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>; diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx index cb2526769..9a1aefba9 100644 --- a/src/client/views/nodes/SliderBox.tsx +++ b/src/client/views/nodes/SliderBox.tsx @@ -4,10 +4,10 @@ import { runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider'; -import { documentSchema } from '../../../new_fields/documentSchemas'; -import { createSchema, makeInterface } from '../../../new_fields/Schema'; -import { ScriptField } from '../../../new_fields/ScriptField'; -import { Cast, NumCast, StrCast } from '../../../new_fields/Types'; +import { documentSchema } from '../../../fields/documentSchemas'; +import { createSchema, makeInterface } from '../../../fields/Schema'; +import { ScriptField } from '../../../fields/ScriptField'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 9602eac52..ccf1f5588 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -5,12 +5,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked } from "mobx"; import { observer } from "mobx-react"; import * as rp from 'request-promise'; -import { Doc } from "../../../new_fields/Doc"; -import { InkTool } from "../../../new_fields/InkField"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { Cast, StrCast } from "../../../new_fields/Types"; -import { VideoField } from "../../../new_fields/URLField"; +import { Doc } from "../../../fields/Doc"; +import { InkTool } from "../../../fields/InkField"; +import { createSchema, makeInterface } from "../../../fields/Schema"; +import { ScriptField } from "../../../fields/ScriptField"; +import { Cast, StrCast } from "../../../fields/Types"; +import { VideoField } from "../../../fields/URLField"; import { Utils, emptyFunction, returnOne, returnZero } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; @@ -21,7 +21,7 @@ import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; import "./VideoBox.scss"; -import { documentSchema } from "../../../new_fields/documentSchemas"; +import { documentSchema } from "../../../fields/documentSchemas"; const path = require('path'); export const timeSchema = createSchema({ @@ -337,7 +337,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { const curTime = (this.layoutDoc.currentTimecode || -1); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 6d4cc1a07..82f05012a 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -2,13 +2,13 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faStickyNote, faPen, faMousePointer } from '@fortawesome/free-solid-svg-icons'; import { action, computed, observable, trace, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, FieldResult } from "../../../new_fields/Doc"; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { HtmlField } from "../../../new_fields/HtmlField"; -import { InkTool } from "../../../new_fields/InkField"; -import { makeInterface } from "../../../new_fields/Schema"; -import { Cast, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; -import { WebField } from "../../../new_fields/URLField"; +import { Doc, FieldResult } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { HtmlField } from "../../../fields/HtmlField"; +import { InkTool } from "../../../fields/InkField"; +import { makeInterface } from "../../../fields/Schema"; +import { Cast, NumCast, BoolCast, StrCast } from "../../../fields/Types"; +import { WebField } from "../../../fields/URLField"; import { Utils, returnOne, emptyFunction, returnZero } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; diff --git a/src/client/views/nodes/formattedText/DashDocCommentView.tsx b/src/client/views/nodes/formattedText/DashDocCommentView.tsx index d94fe7fc6..d56b87ae5 100644 --- a/src/client/views/nodes/formattedText/DashDocCommentView.tsx +++ b/src/client/views/nodes/formattedText/DashDocCommentView.tsx @@ -8,14 +8,14 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s import { StepMap } from "prosemirror-transform"; import { EditorView } from "prosemirror-view"; import * as ReactDOM from 'react-dom'; -import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../new_fields/Doc"; -import { Id } from "../../../../new_fields/FieldSymbols"; -import { List } from "../../../../new_fields/List"; -import { ObjectField } from "../../../../new_fields/ObjectField"; -import { listSpec } from "../../../../new_fields/Schema"; -import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField"; -import { ComputedField } from "../../../../new_fields/ScriptField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../../new_fields/Types"; +import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../fields/Doc"; +import { Id } from "../../../../fields/FieldSymbols"; +import { List } from "../../../../fields/List"; +import { ObjectField } from "../../../../fields/ObjectField"; +import { listSpec } from "../../../../fields/Schema"; +import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; +import { ComputedField } from "../../../../fields/ScriptField"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 7130fee2b..05e6a5959 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -1,10 +1,10 @@ import { IReactionDisposer, reaction } from "mobx"; import { NodeSelection } from "prosemirror-state"; -import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc"; -import { Id } from "../../../../new_fields/FieldSymbols"; -import { ObjectField } from "../../../../new_fields/ObjectField"; -import { ComputedField } from "../../../../new_fields/ScriptField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../../new_fields/Types"; +import { Doc, HeightSym, WidthSym } from "../../../../fields/Doc"; +import { Id } from "../../../../fields/FieldSymbols"; +import { ObjectField } from "../../../../fields/ObjectField"; +import { ComputedField } from "../../../../fields/ScriptField"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { Docs } from "../../../documents/Documents"; diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 3c6841f08..d05e8f1ea 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -1,10 +1,10 @@ import { IReactionDisposer, observable, runInAction, computed, action } from "mobx"; -import { Doc, DocListCast, Field } from "../../../../new_fields/Doc"; -import { List } from "../../../../new_fields/List"; -import { listSpec } from "../../../../new_fields/Schema"; -import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField"; -import { ComputedField } from "../../../../new_fields/ScriptField"; -import { Cast, StrCast } from "../../../../new_fields/Types"; +import { Doc, DocListCast, Field } from "../../../../fields/Doc"; +import { List } from "../../../../fields/List"; +import { listSpec } from "../../../../fields/Schema"; +import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; +import { ComputedField } from "../../../../fields/ScriptField"; +import { Cast, StrCast } from "../../../../fields/Types"; import { DocServer } from "../../../DocServer"; import { CollectionViewType } from "../../collections/CollectionView"; import { FormattedTextBox } from "./FormattedTextBox"; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 206c3db8c..5e33e7e70 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -12,17 +12,17 @@ import { Fragment, Mark, Node, Slice } from "prosemirror-model"; import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "prosemirror-state"; import { ReplaceStep } from 'prosemirror-transform'; import { EditorView } from "prosemirror-view"; -import { DateField } from '../../../../new_fields/DateField'; -import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc"; -import { documentSchema } from '../../../../new_fields/documentSchemas'; -import { Id } from '../../../../new_fields/FieldSymbols'; -import { InkTool } from '../../../../new_fields/InkField'; -import { PrefetchProxy } from '../../../../new_fields/Proxy'; -import { RichTextField } from "../../../../new_fields/RichTextField"; -import { RichTextUtils } from '../../../../new_fields/RichTextUtils'; -import { createSchema, makeInterface } from "../../../../new_fields/Schema"; -import { Cast, DateCast, NumCast, StrCast } from "../../../../new_fields/Types"; -import { TraceMobx } from '../../../../new_fields/util'; +import { DateField } from '../../../../fields/DateField'; +import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../../../fields/Doc"; +import { documentSchema } from '../../../../fields/documentSchemas'; +import { Id } from '../../../../fields/FieldSymbols'; +import { InkTool } from '../../../../fields/InkField'; +import { PrefetchProxy } from '../../../../fields/Proxy'; +import { RichTextField } from "../../../../fields/RichTextField"; +import { RichTextUtils } from '../../../../fields/RichTextUtils'; +import { createSchema, makeInterface } from "../../../../fields/Schema"; +import { Cast, DateCast, NumCast, StrCast } from "../../../../fields/Types"; +import { TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnOne, returnZero, Utils, setupMoveUpEvents } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; import { DocServer } from "../../../DocServer"; @@ -58,7 +58,7 @@ import { FieldView, FieldViewProps } from "../FieldView"; import "./FormattedTextBox.scss"; import { FormattedTextBoxComment, formattedTextBoxCommentPlugin } from './FormattedTextBoxComment'; import React = require("react"); -import { ScriptField } from '../../../../new_fields/ScriptField'; +import { ScriptField } from '../../../../fields/ScriptField'; import GoogleAuthenticationManager from '../../../apis/GoogleAuthenticationManager'; library.add(faEdit); diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index 07aecf148..d47ae63af 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -2,8 +2,8 @@ import { Mark, ResolvedPos } from "prosemirror-model"; import { EditorState, Plugin } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import * as ReactDOM from 'react-dom'; -import { Doc, DocCastAsync } from "../../../../new_fields/Doc"; -import { Cast, FieldValue, NumCast } from "../../../../new_fields/Types"; +import { Doc, DocCastAsync } from "../../../../fields/Doc"; +import { Cast, FieldValue, NumCast } from "../../../../fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, Utils, emptyPath, returnZero, returnOne } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { DocumentManager } from "../../../util/DocumentManager"; diff --git a/src/client/views/nodes/formattedText/ImageResizeView.tsx b/src/client/views/nodes/formattedText/ImageResizeView.tsx index 8f98da0fd..401ecd7e6 100644 --- a/src/client/views/nodes/formattedText/ImageResizeView.tsx +++ b/src/client/views/nodes/formattedText/ImageResizeView.tsx @@ -1,5 +1,5 @@ import { NodeSelection } from "prosemirror-state"; -import { Doc } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../fields/Doc"; import { DocServer } from "../../../DocServer"; import { DocumentManager } from "../../../util/DocumentManager"; import React = require("react"); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index a0b02880e..2f7d23021 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,10 +7,10 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "../../../util/SelectionManager"; import { Docs } from "../../../documents/Documents"; -import { NumCast, BoolCast, Cast, StrCast } from "../../../../new_fields/Types"; -import { Doc } from "../../../../new_fields/Doc"; +import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; +import { Doc } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; -import { Id } from "../../../../new_fields/FieldSymbols"; +import { Id } from "../../../../fields/FieldSymbols"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 170a39801..fd1b26208 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -11,14 +11,14 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faBold, faItalic, faChevronLeft, faUnderline, faStrikethrough, faSubscript, faSuperscript, faIndent, faEyeDropper, faCaretDown, faPalette, faHighlighter, faLink, faPaintRoller, faSleigh } from "@fortawesome/free-solid-svg-icons"; import { updateBullets } from "./ProsemirrorExampleTransfer"; import { FieldViewProps } from "../FieldView"; -import { Cast, StrCast } from "../../../../new_fields/Types"; +import { Cast, StrCast } from "../../../../fields/Types"; import { FormattedTextBoxProps } from "./FormattedTextBox"; import { unimplementedFunction, Utils } from "../../../../Utils"; import { wrapInList } from "prosemirror-schema-list"; -import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../new_fields/SchemaHeaderField'; +import { PastelSchemaPalette, DarkPastelSchemaPalette } from '../../../../fields/SchemaHeaderField'; import "./RichTextMenu.scss"; import { DocServer } from "../../../DocServer"; -import { Doc } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../fields/Doc"; import { SelectionManager } from "../../../util/SelectionManager"; import { LinkManager } from "../../../util/LinkManager"; const { toggleMark, setBlockType } = require("prosemirror-commands"); diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 0ba591fec..fbd6c87bb 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -1,9 +1,9 @@ import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from "prosemirror-inputrules"; import { NodeSelection, TextSelection } from "prosemirror-state"; -import { DataSym, Doc } from "../../../../new_fields/Doc"; -import { Id } from "../../../../new_fields/FieldSymbols"; -import { ComputedField } from "../../../../new_fields/ScriptField"; -import { Cast, NumCast } from "../../../../new_fields/Types"; +import { DataSym, Doc } from "../../../../fields/Doc"; +import { Id } from "../../../../fields/FieldSymbols"; +import { ComputedField } from "../../../../fields/ScriptField"; +import { Cast, NumCast } from "../../../../fields/Types"; import { returnFalse, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from "../../../documents/Documents"; @@ -11,7 +11,7 @@ import { FormattedTextBox } from "./FormattedTextBox"; import { wrappingInputRule } from "./prosemirrorPatches"; import RichTextMenu from "./RichTextMenu"; import { schema } from "./schema_rts"; -import { List } from "../../../../new_fields/List"; +import { List } from "../../../../fields/List"; export class RichTextRules { public Document: Doc; diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx index 7edd191cf..91280dea4 100644 --- a/src/client/views/nodes/formattedText/RichTextSchema.tsx +++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx @@ -8,14 +8,14 @@ import { EditorState, NodeSelection, Plugin, TextSelection } from "prosemirror-s import { StepMap } from "prosemirror-transform"; import { EditorView } from "prosemirror-view"; import * as ReactDOM from 'react-dom'; -import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../new_fields/Doc"; -import { Id } from "../../../../new_fields/FieldSymbols"; -import { List } from "../../../../new_fields/List"; -import { ObjectField } from "../../../../new_fields/ObjectField"; -import { listSpec } from "../../../../new_fields/Schema"; -import { SchemaHeaderField } from "../../../../new_fields/SchemaHeaderField"; -import { ComputedField } from "../../../../new_fields/ScriptField"; -import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../../new_fields/Types"; +import { Doc, DocListCast, Field, HeightSym, WidthSym } from "../../../../fields/Doc"; +import { Id } from "../../../../fields/FieldSymbols"; +import { List } from "../../../../fields/List"; +import { ObjectField } from "../../../../fields/ObjectField"; +import { listSpec } from "../../../../fields/Schema"; +import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; +import { ComputedField } from "../../../../fields/ScriptField"; +import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../../fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { Docs } from "../../../documents/Documents"; diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 46bf481fb..ebaa23e99 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -1,6 +1,6 @@ import React = require("react"); import { DOMOutputSpecArray, Fragment, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model"; -import { Doc } from "../../../../new_fields/Doc"; +import { Doc } from "../../../../fields/Doc"; const emDOM: DOMOutputSpecArray = ["em", 0]; diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 672d3adb8..cb6a15f36 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -1,10 +1,10 @@ import React = require("react"); import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; -import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; +import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; +import { List } from "../../../fields/List"; +import { Cast, FieldValue, NumCast, StrCast } from "../../../fields/Types"; import { DocumentManager } from "../../util/DocumentManager"; import PDFMenu from "./PDFMenu"; import "./Annotation.scss"; diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2a6eff7ff..ff328068b 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -5,7 +5,7 @@ import { observer } from "mobx-react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { unimplementedFunction, returnFalse } from "../../../Utils"; import AntimodeMenu from "../AntimodeMenu"; -import { Doc, Opt } from "../../../new_fields/Doc"; +import { Doc, Opt } from "../../../fields/Doc"; @observer export default class PDFMenu extends AntimodeMenu { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index acaa4363e..c50969493 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -4,16 +4,16 @@ import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import * as rp from "request-promise"; import { Dictionary } from "typescript-collections"; -import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; -import { documentSchema } from "../../../new_fields/documentSchemas"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { InkTool } from "../../../new_fields/InkField"; -import { List } from "../../../new_fields/List"; -import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { Cast, NumCast } from "../../../new_fields/Types"; -import { PdfField } from "../../../new_fields/URLField"; -import { TraceMobx } from "../../../new_fields/util"; +import { Doc, DocListCast, FieldResult, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { documentSchema } from "../../../fields/documentSchemas"; +import { Id } from "../../../fields/FieldSymbols"; +import { InkTool } from "../../../fields/InkField"; +import { List } from "../../../fields/List"; +import { createSchema, makeInterface } from "../../../fields/Schema"; +import { ScriptField } from "../../../fields/ScriptField"; +import { Cast, NumCast } from "../../../fields/Types"; +import { PdfField } from "../../../fields/URLField"; +import { TraceMobx } from "../../../fields/util"; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, emptyPath, intersectRect, returnZero, smoothScroll, Utils } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index e13a5f2f7..280ba9093 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -1,11 +1,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DataSym, DocListCast } from "../../../new_fields/Doc"; -import { documentSchema } from '../../../new_fields/documentSchemas'; -import { Id } from "../../../new_fields/FieldSymbols"; -import { createSchema, makeInterface } from '../../../new_fields/Schema'; -import { Cast, NumCast, BoolCast, ScriptCast } from "../../../new_fields/Types"; +import { Doc, DataSym, DocListCast } from "../../../fields/Doc"; +import { documentSchema } from '../../../fields/documentSchemas'; +import { Id } from "../../../fields/FieldSymbols"; +import { createSchema, makeInterface } from '../../../fields/Schema'; +import { Cast, NumCast, BoolCast, ScriptCast } from "../../../fields/Types"; import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, returnZero } from "../../../Utils"; import { Transform } from "../../util/Transform"; import { CollectionViewType } from '../collections/CollectionView'; diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index 662b37d77..4b53963a5 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -4,10 +4,10 @@ import { observable, action } from 'mobx'; import "./SearchBox.scss"; import { faTimes, faCheckCircle, faObjectGroup } from '@fortawesome/free-solid-svg-icons'; import { library } from '@fortawesome/fontawesome-svg-core'; -import { Doc } from '../../../new_fields/Doc'; -import { Id } from '../../../new_fields/FieldSymbols'; +import { Doc } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; import { DocumentType } from "../../documents/DocumentTypes"; -import { Cast, StrCast } from '../../../new_fields/Types'; +import { Cast, StrCast } from '../../../fields/Types'; import * as _ from "lodash"; import { IconBar } from './IconBar'; import { FieldFilters } from './FieldFilters'; diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index e41b725b1..eea7b528b 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -5,9 +5,9 @@ import { action, computed, observable, runInAction, IReactionDisposer, reaction import { observer } from 'mobx-react'; import * as React from 'react'; import * as rp from 'request-promise'; -import { Doc } from '../../../new_fields/Doc'; -import { Id } from '../../../new_fields/FieldSymbols'; -import { Cast, NumCast, StrCast } from '../../../new_fields/Types'; +import { Doc } from '../../../fields/Doc'; +import { Id } from '../../../fields/FieldSymbols'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { SetupDrag } from '../../util/DragManager'; @@ -19,7 +19,7 @@ import { FieldView } from '../nodes/FieldView'; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentView } from '../nodes/DocumentView'; import { SelectionManager } from '../../util/SelectionManager'; -import { listSpec } from '../../../new_fields/Schema'; +import { listSpec } from '../../../fields/Schema'; library.add(faTimes); diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 96f43e931..24d6e9d6f 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -4,9 +4,9 @@ import { faCaretUp, faChartBar, faFile, faFilePdf, faFilm, faFingerprint, faGlob import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Doc } from "../../../fields/Doc"; +import { Id } from "../../../fields/FieldSymbols"; +import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { emptyFunction, emptyPath, returnFalse, Utils, returnTrue, returnOne, returnZero } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { DocumentManager } from "../../util/DocumentManager"; diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx index fd6b47ff0..d541c8009 100644 --- a/src/debug/Repl.tsx +++ b/src/debug/Repl.tsx @@ -3,9 +3,9 @@ import * as ReactDOM from 'react-dom'; import { observer } from 'mobx-react'; import { observable, computed } from 'mobx'; import { CompileScript } from '../client/util/Scripting'; -import { makeInterface } from '../new_fields/Schema'; -import { ObjectField } from '../new_fields/ObjectField'; -import { RefField } from '../new_fields/RefField'; +import { makeInterface } from '../fields/Schema'; +import { ObjectField } from '../fields/ObjectField'; +import { RefField } from '../fields/RefField'; import { DocServer } from '../client/DocServer'; @observer diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx index 3baedce4b..17d3db8fd 100644 --- a/src/debug/Test.tsx +++ b/src/debug/Test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { DocServer } from '../client/DocServer'; -import { Doc } from '../new_fields/Doc'; +import { Doc } from '../fields/Doc'; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import { Utils } from '../Utils'; diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index a26d2e06a..6ce39d533 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -3,17 +3,17 @@ import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { observer } from 'mobx-react'; -import { Doc, Field, FieldResult, Opt } from '../new_fields/Doc'; +import { Doc, Field, FieldResult, Opt } from '../fields/Doc'; import { DocServer } from '../client/DocServer'; -import { Id } from '../new_fields/FieldSymbols'; -import { List } from '../new_fields/List'; -import { URLField } from '../new_fields/URLField'; +import { Id } from '../fields/FieldSymbols'; +import { List } from '../fields/List'; +import { URLField } from '../fields/URLField'; import { EditableView } from '../client/views/EditableView'; import { CompileScript } from '../client/util/Scripting'; -import { RichTextField } from '../new_fields/RichTextField'; -import { DateField } from '../new_fields/DateField'; -import { ScriptField } from '../new_fields/ScriptField'; -import CursorField from '../new_fields/CursorField'; +import { RichTextField } from '../fields/RichTextField'; +import { DateField } from '../fields/DateField'; +import { ScriptField } from '../fields/ScriptField'; +import CursorField from '../fields/CursorField'; DateField; URLField; diff --git a/src/fields/CursorField.ts b/src/fields/CursorField.ts new file mode 100644 index 000000000..28467377b --- /dev/null +++ b/src/fields/CursorField.ts @@ -0,0 +1,66 @@ +import { ObjectField } from "./ObjectField"; +import { observable } from "mobx"; +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, createSimpleSchema, object, date } from "serializr"; +import { OnUpdate, ToScriptString, ToString, Copy } from "./FieldSymbols"; + +export type CursorPosition = { + x: number, + y: number +}; + +export type CursorMetadata = { + id: string, + identifier: string, + timestamp: number +}; + +export type CursorData = { + metadata: CursorMetadata, + position: CursorPosition +}; + +const PositionSchema = createSimpleSchema({ + x: true, + y: true +}); + +const MetadataSchema = createSimpleSchema({ + id: true, + identifier: true, + timestamp: true +}); + +const CursorSchema = createSimpleSchema({ + metadata: object(MetadataSchema), + position: object(PositionSchema) +}); + +@Deserializable("cursor") +export default class CursorField extends ObjectField { + + @serializable(object(CursorSchema)) + readonly data: CursorData; + + constructor(data: CursorData) { + super(); + this.data = data; + } + + setPosition(position: CursorPosition) { + this.data.position = position; + this.data.metadata.timestamp = Date.now(); + this[OnUpdate](); + } + + [Copy]() { + return new CursorField(this.data); + } + + [ToScriptString]() { + return "invalid"; + } + [ToString]() { + return "invalid"; + } +} \ No newline at end of file diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts new file mode 100644 index 000000000..a925148c2 --- /dev/null +++ b/src/fields/DateField.ts @@ -0,0 +1,36 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, date } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString, ToString } from "./FieldSymbols"; +import { scriptingGlobal, Scripting } from "../client/util/Scripting"; + +@scriptingGlobal +@Deserializable("date") +export class DateField extends ObjectField { + @serializable(date()) + readonly date: Date; + + constructor(date: Date = new Date()) { + super(); + this.date = date; + } + + [Copy]() { + return new DateField(this.date); + } + + toString() { + return `${this.date.toISOString()}`; + } + + [ToScriptString]() { + return `new DateField(new Date(${this.date.toISOString()}))`; + } + [ToString]() { + return this.date.toISOString(); + } +} + +Scripting.addGlobal(function d(...dateArgs: any[]) { + return new DateField(new (Date as any)(...dateArgs)); +}); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts new file mode 100644 index 000000000..a1e1e11b1 --- /dev/null +++ b/src/fields/Doc.ts @@ -0,0 +1,1109 @@ +import { action, computed, observable, ObservableMap, runInAction } from "mobx"; +import { computedFn } from "mobx-utils"; +import { alias, map, serializable } from "serializr"; +import { DocServer } from "../client/DocServer"; +import { DocumentType } from "../client/documents/DocumentTypes"; +import { Scripting, scriptingGlobal } from "../client/util/Scripting"; +import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper"; +import { UndoManager } from "../client/util/UndoManager"; +import { intersectRect, Utils } from "../Utils"; +import { HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update, Copy } from "./FieldSymbols"; +import { List } from "./List"; +import { ObjectField } from "./ObjectField"; +import { PrefetchProxy, ProxyField } from "./Proxy"; +import { FieldId, RefField } from "./RefField"; +import { RichTextField } from "./RichTextField"; +import { listSpec } from "./Schema"; +import { ComputedField, ScriptField } from "./ScriptField"; +import { Cast, FieldValue, NumCast, StrCast, ToConstructor, ScriptCast } from "./Types"; +import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util"; +import { Docs, DocumentOptions } from "../client/documents/Documents"; +import { PdfField, VideoField, AudioField, ImageField } from "./URLField"; +import { LinkManager } from "../client/util/LinkManager"; + +export namespace Field { + export function toKeyValueString(doc: Doc, key: string): string { + const onDelegate = Object.keys(doc).includes(key); + + const field = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + if (Field.IsField(field)) { + return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field)); + } + return ""; + } + export function toScriptString(field: Field): string { + if (typeof field === "string") { + return `"${field}"`; + } else if (typeof field === "number" || typeof field === "boolean") { + return String(field); + } else { + return field[ToScriptString](); + } + } + export function toString(field: Field): string { + if (typeof field === "string") { + return field; + } else if (typeof field === "number" || typeof field === "boolean") { + return String(field); + } else if (field instanceof ObjectField) { + return field[ToString](); + } else if (field instanceof RefField) { + return field[ToString](); + } + return ""; + } + export function IsField(field: any): field is Field; + export function IsField(field: any, includeUndefined: true): field is Field | undefined; + export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined { + return (typeof field === "string") + || (typeof field === "number") + || (typeof field === "boolean") + || (field instanceof ObjectField) + || (field instanceof RefField) + || (includeUndefined && field === undefined); + } +} +export type Field = number | string | boolean | ObjectField | RefField; +export type Opt = T | undefined; +export type FieldWaiting = T extends undefined ? never : Promise; +export type FieldResult = Opt | FieldWaiting>; + +/** + * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs. + * If a default value is given, that will be returned instead of undefined. + * If a default value is given, the returned value should not be modified as it might be a temporary value. + * If no default value is given, and the returned value is not undefined, it can be safely modified. + */ +export function DocListCastAsync(field: FieldResult): Promise; +export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise; +export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { + const list = Cast(field, listSpec(Doc)); + return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue); +} + +export async function DocCastAsync(field: FieldResult): Promise> { + return Cast(field, Doc); +} + +export function DocListCast(field: FieldResult): Doc[] { + return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; +} + +export const WidthSym = Symbol("Width"); +export const HeightSym = Symbol("Height"); +export const DataSym = Symbol("Data"); +export const LayoutSym = Symbol("Layout"); +export const UpdatingFromServer = Symbol("UpdatingFromServer"); +const CachedUpdates = Symbol("Cached updates"); + + +function fetchProto(doc: Doc) { + const proto = doc.proto; + if (proto instanceof Promise) { + return proto; + } +} + +@scriptingGlobal +@Deserializable("Doc", fetchProto).withFields(["id"]) +export class Doc extends RefField { + constructor(id?: FieldId, forceSave?: boolean) { + super(id); + const doc = new Proxy(this, { + set: setter, + get: getter, + // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter + has: (target, key) => key in target.__fields, + ownKeys: target => { + const obj = {} as any; + Object.assign(obj, target.___fields); + runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__); + return Object.keys(obj); + }, + getOwnPropertyDescriptor: (target, prop) => { + if (prop.toString() === "__LAYOUT__") { + return Reflect.getOwnPropertyDescriptor(target, prop); + } + if (prop in target.__fields) { + return { + configurable: true,//TODO Should configurable be true? + enumerable: true, + value: target.__fields[prop] + }; + } + return Reflect.getOwnPropertyDescriptor(target, prop); + }, + deleteProperty: deleteProperty, + defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, + }); + this[SelfProxy] = doc; + if (!id || forceSave) { + DocServer.CreateField(doc); + } + return doc; + } + + proto: Opt; + [key: string]: FieldResult; + + @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize }))) + private get __fields() { return this.___fields; } + private set __fields(value) { + this.___fields = value; + for (const key in value) { + const field = value[key]; + if (!(field instanceof ObjectField)) continue; + field[Parent] = this[Self]; + field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); + } + } + + @observable + //{ [key: string]: Field | FieldWaiting | undefined } + private ___fields: any = {}; + + private [UpdatingFromServer]: boolean = false; + + private [Update] = (diff: any) => { + !this[UpdatingFromServer] && DocServer.UpdateField(this[Id], diff); + } + + private [Self] = this; + private [SelfProxy]: any; + public [WidthSym] = () => NumCast(this[SelfProxy]._width); + public [HeightSym] = () => NumCast(this[SelfProxy]._height); + public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } + public get [DataSym]() { + const self = this[SelfProxy]; + return self.resolvedDataDoc && !self.isTemplateForField ? self : + Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self); + } + @computed get __LAYOUT__() { + const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null); + if (templateLayoutDoc) { + let renderFieldKey: any; + const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, "layout")]; + if (typeof layoutField === "string") { + renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0];//layoutField.split("'")[1]; + } else { + return Cast(layoutField, Doc, null); + } + return Cast(this[SelfProxy][renderFieldKey + "-layout[" + templateLayoutDoc[Id] + "]"], Doc, null) || templateLayoutDoc; + } + return undefined; + } + + [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } + [ToString]() { return `Doc(${this.title})`; } + + private [CachedUpdates]: { [key: string]: () => void | Promise } = {}; + public static CurrentUserEmail: string = ""; + public async [HandleUpdate](diff: any) { + const set = diff.$set; + const sameAuthor = this.author === Doc.CurrentUserEmail; + if (set) { + for (const key in set) { + if (!key.startsWith("fields.")) { + continue; + } + const fKey = key.substring(7); + const fn = async () => { + const value = await SerializationHelper.Deserialize(set[key]); + this[UpdatingFromServer] = true; + this[fKey] = value; + this[UpdatingFromServer] = false; + }; + if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { + delete this[CachedUpdates][fKey]; + await fn(); + } else { + this[CachedUpdates][fKey] = fn; + } + } + } + const unset = diff.$unset; + if (unset) { + for (const key in unset) { + if (!key.startsWith("fields.")) { + continue; + } + const fKey = key.substring(7); + const fn = () => { + this[UpdatingFromServer] = true; + delete this[fKey]; + this[UpdatingFromServer] = false; + }; + if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { + delete this[CachedUpdates][fKey]; + await fn(); + } else { + this[CachedUpdates][fKey] = fn; + } + } + } + } +} + +export namespace Doc { + // export function GetAsync(doc: Doc, key: string, ignoreProto: boolean = false): Promise { + // const self = doc[Self]; + // return new Promise(res => getField(self, key, ignoreProto, res)); + // } + // export function GetTAsync(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): Promise { + // return new Promise(async res => { + // const field = await GetAsync(doc, key, ignoreProto); + // return Cast(field, ctor); + // }); + // } + + export function RunCachedUpdate(doc: Doc, field: string) { + const update = doc[CachedUpdates][field]; + if (update) { + update(); + delete doc[CachedUpdates][field]; + } + } + export function AddCachedUpdate(doc: Doc, field: string, oldValue: any) { + const val = oldValue; + doc[CachedUpdates][field] = () => { + doc[UpdatingFromServer] = true; + doc[field] = val; + doc[UpdatingFromServer] = false; + }; + } + export function MakeReadOnly(): { end(): void } { + makeReadOnly(); + return { + end() { + makeEditable(); + } + }; + } + + export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult { + try { + return getField(doc[Self], key, ignoreProto); + } catch { + return doc; + } + } + export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): FieldResult { + return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult; + } + export function IsPrototype(doc: Doc) { + return GetT(doc, "isPrototype", "boolean", true); + } + export function IsBaseProto(doc: Doc) { + return GetT(doc, "baseProto", "boolean", true); + } + export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) { + const hasProto = doc.proto instanceof Doc; + const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1; + const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1; + if (onDeleg || !hasProto || (!onProto && !defaultProto)) { + doc[key] = value; + } else doc.proto![key] = value; + } + export async function SetOnPrototype(doc: Doc, key: string, value: Field) { + const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc; + + if (proto) { + proto[key] = value; + } + } + export function GetAllPrototypes(doc: Doc): Doc[] { + const protos: Doc[] = []; + let d: Opt = doc; + while (d) { + protos.push(d); + d = FieldValue(d.proto); + } + return protos; + } + + /** + * This function is intended to model Object.assign({}, {}) [https://mzl.la/1Mo3l21], which copies + * the values of the properties of a source object into the target. + * + * This is just a specific, Dash-authored version that serves the same role for our + * Doc class. + * + * @param doc the target document into which you'd like to insert the new fields + * @param fields the fields to project onto the target. Its type signature defines a mapping from some string key + * to a potentially undefined field, where each entry in this mapping is optional. + */ + export function assign(doc: Doc, fields: Partial>>, skipUndefineds: boolean = false) { + for (const key in fields) { + if (fields.hasOwnProperty(key)) { + const value = fields[key]; + if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds? + doc[key] = value; + } + } + } + return doc; + } + + // compare whether documents or their protos match + export function AreProtosEqual(doc?: Doc, other?: Doc) { + if (!doc || !other) return false; + const r = (doc === other); + const r2 = (Doc.GetProto(doc) === other); + const r3 = (Doc.GetProto(other) === doc); + const r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined); + return r || r2 || r3 || r4; + } + + // Gets the data document for the document. Note: this is mis-named -- it does not specifically + // return the doc's proto, but rather recursively searches through the proto inheritance chain + // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype'). + export function GetProto(doc: Doc): Doc { + if (doc instanceof Promise) { + console.log("GetProto: error: got Promise insead of Doc"); + } + const proto = doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc)); + return proto === doc ? proto : Doc.GetProto(proto); + } + export function GetDataDoc(doc: Doc): Doc { + const proto = Doc.GetProto(doc); + return proto === doc ? proto : Doc.GetDataDoc(proto); + } + + export function allKeys(doc: Doc): string[] { + const results: Set = new Set; + + let proto: Doc | undefined = doc; + while (proto) { + Object.keys(proto).forEach(key => results.add(key)); + proto = proto.proto; + } + + return Array.from(results); + } + + export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) { + let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1); + index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1); + return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); + } + export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc) { + const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc); + if (listDoc[key] === undefined) { + Doc.GetProto(listDoc)[key] = new List(); + } + const list = Cast(listDoc[key], listSpec(Doc)); + if (list) { + const ind = list.indexOf(doc); + if (ind !== -1) { + list.splice(ind, 1); + return true; + } + } + return false; + } + export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) { + const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc); + if (listDoc[key] === undefined) { + Doc.GetProto(listDoc)[key] = new List(); + } + const list = Cast(listDoc[key], listSpec(Doc)); + if (list) { + if (allowDuplicates !== true) { + const pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1); + if (pind !== -1) { + list.splice(pind, 1); + } + } + if (first) { + list.splice(0, 0, doc); + } + else { + const ind = relativeTo ? list.indexOf(relativeTo) : -1; + if (ind === -1) { + if (reversed) list.splice(0, 0, doc); + else list.push(doc); + } + else { + if (reversed) list.splice(before ? (list.length - ind) + 1 : list.length - ind, 0, doc); + else list.splice(before ? ind : ind + 1, 0, doc); + } + } + return true; + } + return false; + } + + // + // Computes the bounds of the contents of a set of documents. + // + export function ComputeContentBounds(docList: Doc[]) { + const bounds = docList.reduce((bounds, doc) => { + const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; + const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; + return { + x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), + r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) + }; + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }); + return bounds; + } + + export function MakeAlias(doc: Doc, id?: string) { + const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); + const layout = Doc.LayoutField(alias); + if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) { + Doc.SetLayout(alias, Doc.MakeAlias(layout)); + } + alias.aliasOf = doc; + alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`); + return alias; + } + + // + // Determines whether the layout needs to be expanded (as a template). + // template expansion is rquired when the layout is a template doc/field and there's a datadoc which isn't equal to the layout template + // + export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) { + return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc; + } + + const _pendingMap: Map = new Map(); + // + // Returns an expanded template layout for a target data document if there is a template relationship + // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties + // of the original layout while allowing for individual layout properties to be overridden in the expanded layout. + // templateArgs should be equivalent to the layout key that generates the template since that's where the template parameters are stored in ()'s at the end of the key. + // NOTE: the template will have references to "@params" -- the template arguments will be assigned to the '@params' field + // so that when the @params key is accessed, it will be rewritten as the key that is stored in the 'params' field and + // the derefence will then occur on the rootDocument (the original document). + // in the future, field references could be written as @ and then arguments would be passed in the layout key as: + // layout_mytemplate(somparam=somearg). + // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument + export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) { + const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", "") || StrCast(templateLayoutDoc.PARAMS); + if (!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc) || !targetDoc) return templateLayoutDoc; + + const templateField = StrCast(templateLayoutDoc.isTemplateForField); // the field that the template renders + // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc + // using the template layout doc's id as the field key. + // If it doesn't find the expanded layout, then it makes a delegate of the template layout and + // saves it on the data doc indexed by the template layout's id. + // + const params = args.split("=").length > 1 ? args.split("=")[0] : "PARAMS"; + const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc); + const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]"; + let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey]; + + if (templateLayoutDoc.resolvedDataDoc instanceof Promise) { + expandedTemplateLayout = undefined; + _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true); + } + else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) { + if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) { + expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params + } else { + _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true); + templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype + setTimeout(action(() => { + if (!targetDoc[expandedLayoutFieldKey]) { + const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]"); + // the template's arguments are stored in params which is derefenced to find + // the actual field key where the parameterized template data is stored. + newLayoutDoc[params] = args !== "..." ? args : ""; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params. it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances + newLayoutDoc.rootDocument = targetDoc; + targetDoc[expandedLayoutFieldKey] = newLayoutDoc; + const dataDoc = Doc.GetProto(targetDoc); + newLayoutDoc.resolvedDataDoc = dataDoc; + if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) { + dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc }); + } + _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args); + } + }), 0); + } + } + return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplateLayout is pending. + } + + // if the childDoc is a template for a field, then this will return the expanded layout with its data doc. + // otherwise, it just returns the childDoc + export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt, childDoc: Doc) { + if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) { + console.log("No, no, no!"); + return { layout: childDoc, data: childDoc }; + } + const resolvedDataDoc = (Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc); + return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, "(" + StrCast(containerDoc.PARAMS) + ")"), data: resolvedDataDoc }; + } + + export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc { + Object.keys(doc).forEach(key => { + const field = ProxyField.WithoutProxy(() => doc[key]); + if (key === "proto" && copyProto) { + if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) { + overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto); + } + } else { + if (field instanceof RefField) { + overwrite[key] = field; + } else if (field instanceof ObjectField) { + overwrite[key] = ObjectField.MakeCopy(field); + } else if (field instanceof Promise) { + debugger; //This shouldn't happend... + } else { + overwrite[key] = field; + } + } + }); + + return overwrite; + } + + export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc { + const copy = new Doc(copyProtoId, true); + const exclude = Cast(doc.excludeFields, listSpec("string"), []); + Object.keys(doc).forEach(key => { + if (exclude.includes(key)) return; + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + const field = ProxyField.WithoutProxy(() => doc[key]); + if (key === "proto" && copyProto) { + if (doc[key] instanceof Doc) { + copy[key] = Doc.MakeCopy(doc[key]!, false); + } + } else { + if (field instanceof RefField) { + copy[key] = field; + } else if (cfield instanceof ComputedField) { + copy[key] = ComputedField.MakeFunction(cfield.script.originalScript); + } else if (field instanceof ObjectField) { + copy[key] = doc[key] instanceof Doc ? + key.includes("layout[") ? Doc.MakeCopy(doc[key] as Doc, false) : doc[key] : // reference documents except copy documents that are expanded teplate fields + ObjectField.MakeCopy(field); + } else if (field instanceof Promise) { + debugger; //This shouldn't happend... + } else { + copy[key] = field; + } + } + }); + + return copy; + } + + export function MakeClone(doc: Doc): Doc { + const cloneMap = new Map(); + const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = []; + const copy = Doc.makeClone(doc, cloneMap, rtfMap); + rtfMap.map(({ copy, key, field }) => { + const replacer = (match: any, attr: string, id: string, offset: any, string: any) => { + const mapped = cloneMap.get(id); + return attr + "\"" + (mapped ? mapped[Id] : id) + "\""; + }; + const replacer2 = (match: any, href: string, id: string, offset: any, string: any) => { + const mapped = cloneMap.get(id); + return href + (mapped ? mapped[Id] : id); + }; + const regex = `(${Utils.prepend("/doc/")})([^"]*)`; + const re = new RegExp(regex, "g"); + copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text); + }); + return copy; + } + + export function makeClone(doc: Doc, cloneMap: Map, rtfs: { copy: Doc, key: string, field: RichTextField }[]): Doc { + if (Doc.IsBaseProto(doc)) return doc; + if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!; + const copy = new Doc(undefined, true); + cloneMap.set(doc[Id], copy); + if (LinkManager.Instance.getAllLinks().includes(doc) && LinkManager.Instance.getAllLinks().indexOf(copy) === -1) LinkManager.Instance.addLink(copy); + const exclude = Cast(doc.excludeFields, listSpec("string"), []); + Object.keys(doc).forEach(key => { + if (exclude.includes(key)) return; + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + const field = ProxyField.WithoutProxy(() => doc[key]); + const copyObjectField = (field: ObjectField) => { + const list = Cast(doc[key], listSpec(Doc)); + if (list !== undefined && !(list instanceof Promise)) { + copy[key] = new List(list.filter(d => d instanceof Doc).map(d => Doc.makeClone(d as Doc, cloneMap, rtfs))); + } else if (doc[key] instanceof Doc) { + copy[key] = key.includes("layout[") ? undefined : Doc.makeClone(doc[key] as Doc, cloneMap, rtfs); // reference documents except copy documents that are expanded teplate fields + } else { + copy[key] = ObjectField.MakeCopy(field); + if (field instanceof RichTextField) { + if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) { + rtfs.push({ copy, key, field }); + } + } + } + }; + if (key === "proto") { + if (doc[key] instanceof Doc) { + copy[key] = Doc.makeClone(doc[key]!, cloneMap, rtfs); + } + } else { + if (field instanceof RefField) { + copy[key] = field; + } else if (cfield instanceof ComputedField) { + copy[key] = ComputedField.MakeFunction(cfield.script.originalScript); + (key === "links" && field instanceof ObjectField) && copyObjectField(field); + } else if (field instanceof ObjectField) { + copyObjectField(field); + } else if (field instanceof Promise) { + debugger; //This shouldn't happend... + } else { + copy[key] = field; + } + } + }); + Doc.SetInPlace(copy, "title", "CLONE: " + doc.title, true); + copy.cloneOf = doc; + cloneMap.set(doc[Id], copy); + return copy; + } + + export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc; + export function MakeDelegate(doc: Opt, id?: string, title?: string): Opt; + export function MakeDelegate(doc: Opt, id?: string, title?: string): Opt { + if (doc) { + const delegate = new Doc(id, true); + delegate.proto = doc; + title && (delegate.title = title); + return delegate; + } + return undefined; + } + + let _applyCount: number = 0; + export function ApplyTemplate(templateDoc: Doc) { + if (templateDoc) { + const target = Doc.MakeDelegate(new Doc()); + const targetKey = StrCast(templateDoc.layoutKey, "layout"); + const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")"); + target.layoutKey = targetKey; + applied && (Doc.GetProto(applied).type = templateDoc.type); + return applied; + } + return undefined; + } + export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) { + if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) { + if (target.resolvedDataDoc) { + target[targetKey] = new PrefetchProxy(templateDoc); + } else { + titleTarget && (Doc.GetProto(target).title = titleTarget); + Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc); + } + } + return target; + } + + // + // This function converts a generic field layout display into a field layout that displays a specific + // metadata field indicated by the title of the template field (not the default field that it was rendering) + // + export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt): boolean { + + // find the metadata field key that this template field doc will display (indicated by its title) + const metadataFieldKey = StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, ""); + + // update the original template to mark it as a template + templateField.isTemplateForField = metadataFieldKey; + templateField.title = metadataFieldKey; + + const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)]; + const templateCaptionValue = templateField.caption; + // move any data that the template field had been rendering over to the template doc so that things will still be rendered + // when the template field is adjusted to point to the new metadatafield key. + // note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well. + // note 2: this will not overwrite any field that already exists on the template doc at the field key + if (!templateDoc?.[metadataFieldKey] && templateFieldValue instanceof ObjectField) { + Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc)); + (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue)); + } + // copy the textTemplates from 'this' (not 'self') because the layout contains the template info, not the original doc + if (templateCaptionValue instanceof RichTextField && !templateCaptionValue.Empty()) { + templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`); + } + if (templateFieldValue instanceof RichTextField && !templateFieldValue.Empty()) { + templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`); + } + + // get the layout string that the template uses to specify its layout + const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField))); + + // change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document. + Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`); + + // assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino) + Doc.Layout(templateField)[metadataFieldKey + "_ext"] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + "_ext"] as Doc); + + return true; + } + + export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) { + const doc2Layout = Doc.Layout(doc2); + const doc1Layout = Doc.Layout(doc1); + const x2 = NumCast(doc2.x) - clusterDistance; + const y2 = NumCast(doc2.y) - clusterDistance; + const w2 = NumCast(doc2Layout._width) + clusterDistance; + const h2 = NumCast(doc2Layout._height) + clusterDistance; + const x = NumCast(doc1.x) - clusterDistance; + const y = NumCast(doc1.y) - clusterDistance; + const w = NumCast(doc1Layout._width) + clusterDistance; + const h = NumCast(doc1Layout._height) + clusterDistance; + return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 }); + } + + export function isBrushedHighlightedDegree(doc: Doc) { + if (Doc.IsHighlighted(doc)) { + return 6; + } + else { + return Doc.IsBrushedDegree(doc); + } + } + + export class DocBrush { + BrushedDoc: ObservableMap = new ObservableMap(); + } + const brushManager = new DocBrush(); + + export class DocData { + @observable _user_doc: Doc = undefined!; + @observable _searchQuery: string = ""; + } + + // the document containing the view layout information - will be the Document itself unless the Document has + // a layout field or 'layout' is given. + export function Layout(doc: Doc, layout?: Doc): Doc { + const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, "data")}-layout[` + layout[Id] + "]"], Doc, null); + return overrideLayout || doc[LayoutSym] || doc; + } + export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; } + export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; } + export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; } + const manager = new DocData(); + export function SearchQuery(): string { return manager._searchQuery; } + export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); } + export function UserDoc(): Doc { return manager._user_doc; } + export function SetUserDoc(doc: Doc) { manager._user_doc = doc; } + export function IsBrushed(doc: Doc) { + return computedFn(function IsBrushed(doc: Doc) { + return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc)); + })(doc); + } + // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message) + export function IsBrushedDegreeUnmemoized(doc: Doc) { + return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0; + } + export function IsBrushedDegree(doc: Doc) { + return computedFn(function IsBrushDegree(doc: Doc) { + return Doc.IsBrushedDegreeUnmemoized(doc); + })(doc); + } + export function BrushDoc(doc: Doc) { + brushManager.BrushedDoc.set(doc, true); + brushManager.BrushedDoc.set(Doc.GetProto(doc), true); + return doc; + } + export function UnBrushDoc(doc: Doc) { + brushManager.BrushedDoc.delete(doc); + brushManager.BrushedDoc.delete(Doc.GetProto(doc)); + return doc; + } + + + export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; } + export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; } + + export function linkFollowUnhighlight() { + Doc.UnhighlightAll(); + document.removeEventListener("pointerdown", linkFollowUnhighlight); + } + + let _lastDate = 0; + export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) { + linkFollowUnhighlight(); + Doc.HighlightDoc(destDoc, dataAndDisplayDocs); + document.removeEventListener("pointerdown", linkFollowUnhighlight); + document.addEventListener("pointerdown", linkFollowUnhighlight); + const lastDate = _lastDate = Date.now(); + window.setTimeout(() => _lastDate === lastDate && linkFollowUnhighlight(), 5000); + } + + export class HighlightBrush { + @observable HighlightedDoc: Map = new Map(); + } + const highlightManager = new HighlightBrush(); + export function IsHighlighted(doc: Doc) { + return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc)); + } + export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) { + runInAction(() => { + highlightManager.HighlightedDoc.set(doc, true); + dataAndDisplayDocs && highlightManager.HighlightedDoc.set(Doc.GetProto(doc), true); + }); + } + export function UnHighlightDoc(doc: Doc) { + runInAction(() => { + highlightManager.HighlightedDoc.set(doc, false); + highlightManager.HighlightedDoc.set(Doc.GetProto(doc), false); + }); + } + export function UnhighlightAll() { + const mapEntries = highlightManager.HighlightedDoc.keys(); + let docEntry: IteratorResult; + while (!(docEntry = mapEntries.next()).done) { + const targetDoc = docEntry.value; + targetDoc && Doc.UnHighlightDoc(targetDoc); + } + + } + export function UnBrushAllDocs() { + brushManager.BrushedDoc.clear(); + } + + export function getDocTemplate(doc?: Doc) { + return doc?.isTemplateDoc ? doc : + Cast(doc?.dragFactory, Doc, null)?.isTemplateDoc ? doc?.dragFactory : + Cast(doc?.layout, Doc, null)?.isTemplateDoc ? doc?.layout : undefined; + } + + export function matchFieldValue(doc: Doc, key: string, value: any): boolean { + const fieldVal = doc[key]; + if (Cast(fieldVal, listSpec("string"), []).length) { + const vals = Cast(fieldVal, listSpec("string"), []); + return vals.some(v => v === value); + } + const fieldStr = Field.toString(fieldVal as Field); + return fieldStr === value; + } + + export function deiconifyView(doc: any) { + StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc); + } + + export function setNativeView(doc: any) { + const prevLayout = StrCast(doc.layoutKey).split("_")[1]; + const deiconify = prevLayout === "icon" && StrCast(doc.deiconifyLayout) ? "layout_" + StrCast(doc.deiconifyLayout) : ""; + prevLayout === "icon" && (doc.deiconifyLayout = undefined); + doc.layoutKey = deiconify || "layout"; + } + export function setDocFilterRange(target: Doc, key: string, range?: number[]) { + const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []); + for (let i = 0; i < docRangeFilters.length; i += 3) { + if (docRangeFilters[i] === key) { + docRangeFilters.splice(i, 3); + break; + } + } + if (range !== undefined) { + docRangeFilters.push(key); + docRangeFilters.push(range[0].toString()); + docRangeFilters.push(range[1].toString()); + target._docRangeFilters = new List(docRangeFilters); + } + } + + export function aliasDocs(field: any) { + return new List(field.map((d: any) => Doc.MakeAlias(d))); + } + + // filters document in a container collection: + // all documents with the specified value for the specified key are included/excluded + // based on the modifiers :"check", "x", undefined + export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { + const docFilters = Cast(container._docFilters, listSpec("string"), []); + for (let i = 0; i < docFilters.length; i += 3) { + if (docFilters[i] === key && docFilters[i + 1] === value) { + docFilters.splice(i, 3); + break; + } + } + if (typeof modifiers === "string") { + docFilters.push(key); + docFilters.push(value); + docFilters.push(modifiers); + container._docFilters = new List(docFilters); + } + } + export function readDocRangeFilter(doc: Doc, key: string) { + const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []); + for (let i = 0; i < docRangeFilters.length; i += 3) { + if (docRangeFilters[i] === key) { + return [Number(docRangeFilters[i + 1]), Number(docRangeFilters[i + 2])]; + } + } + } + export function assignDocToField(doc: Doc, field: string, id: string) { + DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout)); + return id; + } + + export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) { + runInAction(() => { + if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) { + layoutDoc.scale = NumCast(layoutDoc.scale, 1) * contentScale; + layoutDoc._nativeWidth = undefined; + layoutDoc._nativeHeight = undefined; + } + else { + layoutDoc._autoHeight = false; + if (!layoutDoc._nativeWidth) { + layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth); + layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight); + } + } + }); + } + + export function isDocPinned(doc: Doc) { + //add this new doc to props.Document + const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; + if (curPres) { + return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; + } + return false; + } + + // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) + export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { + const batch = UndoManager.StartBatch("makeCustomViewClicked"); + runInAction(() => { + doc.layoutKey = "layout_" + templateSignature; + if (doc[doc.layoutKey] === undefined) { + createCustomView(doc, creator, templateSignature, docLayoutTemplate); + } + }); + batch.end(); + } + export function findTemplate(templateName: string, type: string, signature: string) { + let docLayoutTemplate: Opt; + const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data); + const templBtns = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data); + const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data); + const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc); + // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized + // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on + !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc)); + !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); + return docLayoutTemplate; + } + export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { + const templateName = templateSignature.replace(/\(.*\)/, ""); + docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.type), templateSignature); + + const customName = "layout_" + templateSignature; + const _width = NumCast(doc._width); + const _height = NumCast(doc._height); + const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; + + let fieldTemplate: Opt; + if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { + fieldTemplate = Docs.Create.TextDocument("", options); + } else if (doc.data instanceof PdfField) { + fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options); + } else if (doc.data instanceof VideoField) { + fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options); + } else if (doc.data instanceof AudioField) { + fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options); + } else if (doc.data instanceof ImageField) { + fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); + } + const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); + + fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); + docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); + } + export function makeCustomView(doc: Doc, custom: boolean, layout: string) { + Doc.setNativeView(doc); + if (custom) { + makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined); + } + } + export function iconify(doc: Doc) { + const layoutKey = Cast(doc.layoutKey, "string", null); + Doc.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined); + if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", ""); + } + + export function pileup(selected: Doc[], x: number, y: number) { + const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true }); + let w = 0, h = 0; + selected.forEach((d, i) => { + Doc.iconify(d); + w = Math.max(d[WidthSym](), w); + h = Math.max(d[HeightSym](), h); + }); + h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away. so this is a fallback hack to try to do something reasonable + selected.forEach((d, i) => { + d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2; + d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2; + d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + }); + newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; + newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; + newCollection._width = newCollection._height = 110; + //newCollection.borderRounding = "40px"; + newCollection._jitterRotation = 10; + newCollection._backgroundColor = "gray"; + newCollection._overflow = "visible"; + return newCollection; + } + + + export async function addFieldEnumerations(doc: Opt, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) { + let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey); + if (!(optionsCollection instanceof Doc)) { + optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey); + Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc); + } + const options = optionsCollection as Doc; + const targetDoc = doc && Doc.GetProto(Cast(doc.rootDocument, Doc, null) || doc); + const docFind = `options.data.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`; + targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(docFind + `._backgroundColor || "white"`, undefined, { options })); + targetDoc && (targetDoc.color = ComputedField.MakeFunction(docFind + `.color || "black"`, undefined, { options })); + targetDoc && (targetDoc.borderRounding = ComputedField.MakeFunction(docFind + `.borderRounding`, undefined, { options })); + enumerations.map(enumeration => { + const found = DocListCast(options.data).find(d => d.title === enumeration.title); + if (found) { + found._backgroundColor = enumeration._backgroundColor || found._backgroundColor; + found._color = enumeration.color || found._color; + } else { + Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, enumeration)); + } + }); + return optionsCollection; + } +} + +Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; }); +Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); +Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); }); +Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); +Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); +Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); }); +Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(field); }); +Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); +Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); +Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); +Scripting.addGlobal(function deiconifyView(doc: any) { Doc.deiconifyView(doc); }); +Scripting.addGlobal(function undo() { return UndoManager.Undo(); }); +Scripting.addGlobal(function redo() { return UndoManager.Redo(); }); +Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; }); +Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); }); +Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); }); +Scripting.addGlobal(function activePresentationItem() { + const curPres = Doc.UserDoc().activePresentation as Doc; + return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)]; +}); +Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().activeSelection = new List([doc]); }); +Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { + const docs = DocListCast(Doc.UserDoc().activeSelection). + filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && d.type !== DocumentType.KVP && + (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); + return docs.length ? new List(docs) : prevValue; +}); +Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); +Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file diff --git a/src/fields/FieldSymbols.ts b/src/fields/FieldSymbols.ts new file mode 100644 index 000000000..8d040f493 --- /dev/null +++ b/src/fields/FieldSymbols.ts @@ -0,0 +1,12 @@ + +export const Update = Symbol("Update"); +export const Self = Symbol("Self"); +export const SelfProxy = Symbol("SelfProxy"); +export const HandleUpdate = Symbol("HandleUpdate"); +export const Id = Symbol("Id"); +export const OnUpdate = Symbol("OnUpdate"); +export const Parent = Symbol("Parent"); +export const Copy = Symbol("Copy"); +export const ToScriptString = Symbol("ToScriptString"); +export const ToPlainText = Symbol("ToPlainText"); +export const ToString = Symbol("ToString"); diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts new file mode 100644 index 000000000..6e8bba977 --- /dev/null +++ b/src/fields/HtmlField.ts @@ -0,0 +1,26 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString, ToString} from "./FieldSymbols"; + +@Deserializable("html") +export class HtmlField extends ObjectField { + @serializable(primitive()) + readonly html: string; + + constructor(html: string) { + super(); + this.html = html; + } + + [Copy]() { + return new HtmlField(this.html); + } + + [ToScriptString]() { + return "invalid"; + } + [ToString]() { + return this.html; + } +} diff --git a/src/fields/IconField.ts b/src/fields/IconField.ts new file mode 100644 index 000000000..76c4ddf1b --- /dev/null +++ b/src/fields/IconField.ts @@ -0,0 +1,26 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString, ToString } from "./FieldSymbols"; + +@Deserializable("icon") +export class IconField extends ObjectField { + @serializable(primitive()) + readonly icon: string; + + constructor(icon: string) { + super(); + this.icon = icon; + } + + [Copy]() { + return new IconField(this.icon); + } + + [ToScriptString]() { + return "invalid"; + } + [ToString]() { + return "ICONfield"; + } +} diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts new file mode 100644 index 000000000..bb93de5ac --- /dev/null +++ b/src/fields/InkField.ts @@ -0,0 +1,50 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString, ToString } from "./FieldSymbols"; + +export enum InkTool { + None, + Pen, + Highlighter, + Eraser, + Stamp +} + +export interface PointData { + X: number; + Y: number; +} + +export type InkData = Array; + +const pointSchema = createSimpleSchema({ + X: true, Y: true +}); + +const strokeDataSchema = createSimpleSchema({ + pathData: list(object(pointSchema)), + "*": true +}); + +@Deserializable("ink") +export class InkField extends ObjectField { + @serializable(list(object(strokeDataSchema))) + readonly inkData: InkData; + + constructor(data: InkData) { + super(); + this.inkData = data; + } + + [Copy]() { + return new InkField(this.inkData); + } + + [ToScriptString]() { + return "invalid"; + } + [ToString]() { + return "InkField"; + } +} diff --git a/src/fields/List.ts b/src/fields/List.ts new file mode 100644 index 000000000..fdabea365 --- /dev/null +++ b/src/fields/List.ts @@ -0,0 +1,330 @@ +import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper"; +import { Field } from "./Doc"; +import { setter, getter, deleteProperty, updateFunction } from "./util"; +import { serializable, alias, list } from "serializr"; +import { observable, action, runInAction } from "mobx"; +import { ObjectField } from "./ObjectField"; +import { RefField } from "./RefField"; +import { ProxyField } from "./Proxy"; +import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols"; +import { Scripting } from "../client/util/Scripting"; +import { DocServer } from "../client/DocServer"; + +const listHandlers: any = { + /// Mutator methods + copyWithin() { + throw new Error("copyWithin not supported yet"); + }, + fill(value: any, start?: number, end?: number) { + if (value instanceof RefField) { + throw new Error("fill with RefFields not supported yet"); + } + const res = this[Self].__fields.fill(value, start, end); + this[Update](); + return res; + }, + pop(): any { + const field = toRealField(this[Self].__fields.pop()); + this[Update](); + return field; + }, + push: action(function (this: any, ...items: any[]) { + items = items.map(toObjectField); + const list = this[Self]; + const length = list.__fields.length; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + //TODO Error checking to make sure parent doesn't already exist + if (item instanceof ObjectField) { + item[Parent] = list; + item[OnUpdate] = updateFunction(list, i + length, item, this); + } + } + const res = list.__fields.push(...items); + this[Update](); + return res; + }), + reverse() { + const res = this[Self].__fields.reverse(); + this[Update](); + return res; + }, + shift() { + const res = toRealField(this[Self].__fields.shift()); + this[Update](); + return res; + }, + sort(cmpFunc: any) { + this[Self].__realFields(); // coerce retrieving entire array + const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined); + this[Update](); + return res; + }, + splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) { + this[Self].__realFields(); // coerce retrieving entire array + items = items.map(toObjectField); + const list = this[Self]; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + //TODO Error checking to make sure parent doesn't already exist + //TODO Need to change indices of other fields in array + if (item instanceof ObjectField) { + item[Parent] = list; + item[OnUpdate] = updateFunction(list, i + start, item, this); + } + } + const res = list.__fields.splice(start, deleteCount, ...items); + this[Update](); + return res.map(toRealField); + }), + unshift(...items: any[]) { + items = items.map(toObjectField); + const list = this[Self]; + const length = list.__fields.length; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + //TODO Error checking to make sure parent doesn't already exist + //TODO Need to change indices of other fields in array + if (item instanceof ObjectField) { + item[Parent] = list; + item[OnUpdate] = updateFunction(list, i, item, this); + } + } + const res = this[Self].__fields.unshift(...items); + this[Update](); + return res; + + }, + /// Accessor methods + concat: action(function (this: any, ...items: any[]) { + this[Self].__realFields(); + return this[Self].__fields.map(toRealField).concat(...items); + }), + includes(valueToFind: any, fromIndex: number) { + if (valueToFind instanceof RefField) { + return this[Self].__realFields().includes(valueToFind, fromIndex); + } else { + return this[Self].__fields.includes(valueToFind, fromIndex); + } + }, + indexOf(valueToFind: any, fromIndex: number) { + if (valueToFind instanceof RefField) { + return this[Self].__realFields().indexOf(valueToFind, fromIndex); + } else { + return this[Self].__fields.indexOf(valueToFind, fromIndex); + } + }, + join(separator: any) { + this[Self].__realFields(); + return this[Self].__fields.map(toRealField).join(separator); + }, + lastIndexOf(valueToFind: any, fromIndex: number) { + if (valueToFind instanceof RefField) { + return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex); + } else { + return this[Self].__fields.lastIndexOf(valueToFind, fromIndex); + } + }, + slice(begin: number, end: number) { + this[Self].__realFields(); + return this[Self].__fields.slice(begin, end).map(toRealField); + }, + + /// Iteration methods + entries() { + return this[Self].__realFields().entries(); + }, + every(callback: any, thisArg: any) { + return this[Self].__realFields().every(callback, thisArg); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + }, + filter(callback: any, thisArg: any) { + return this[Self].__realFields().filter(callback, thisArg); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + }, + find(callback: any, thisArg: any) { + return this[Self].__realFields().find(callback, thisArg); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + }, + findIndex(callback: any, thisArg: any) { + return this[Self].__realFields().findIndex(callback, thisArg); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + }, + forEach(callback: any, thisArg: any) { + return this[Self].__realFields().forEach(callback, thisArg); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + }, + map(callback: any, thisArg: any) { + return this[Self].__realFields().map(callback, thisArg); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + }, + reduce(callback: any, initialValue: any) { + return this[Self].__realFields().reduce(callback, initialValue); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); + }, + reduceRight(callback: any, initialValue: any) { + return this[Self].__realFields().reduceRight(callback, initialValue); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); + }, + some(callback: any, thisArg: any) { + return this[Self].__realFields().some(callback, thisArg); + // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. + // If we don't want to support the array parameter, we should use this version instead + // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); + }, + values() { + return this[Self].__realFields().values(); + }, + [Symbol.iterator]() { + return this[Self].__realFields().values(); + } +}; + +function toObjectField(field: Field) { + return field instanceof RefField ? new ProxyField(field) : field; +} + +function toRealField(field: Field) { + return field instanceof ProxyField ? field.value() : field; +} + +function listGetter(target: any, prop: string | number | symbol, receiver: any): any { + if (listHandlers.hasOwnProperty(prop)) { + return listHandlers[prop]; + } + return getter(target, prop, receiver); +} + +interface ListSpliceUpdate { + type: "splice"; + index: number; + added: T[]; + removedCount: number; +} + +interface ListIndexUpdate { + type: "update"; + index: number; + newValue: T; +} + +type ListUpdate = ListSpliceUpdate | ListIndexUpdate; + +type StoredType = T extends RefField ? ProxyField : T; + +@Deserializable("list") +class ListImpl extends ObjectField { + constructor(fields?: T[]) { + super(); + const list = new Proxy(this, { + set: setter, + get: listGetter, + ownKeys: target => Object.keys(target.__fields), + getOwnPropertyDescriptor: (target, prop) => { + if (prop in target.__fields) { + return { + configurable: true,//TODO Should configurable be true? + enumerable: true, + }; + } + return Reflect.getOwnPropertyDescriptor(target, prop); + }, + deleteProperty: deleteProperty, + defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, + }); + this[SelfProxy] = list; + if (fields) { + (list as any).push(...fields); + } + return list; + } + + [key: number]: T | (T extends RefField ? Promise : never); + + // this requests all ProxyFields at the same time to avoid the overhead + // of separate network requests and separate updates to the React dom. + private __realFields() { + const waiting = this.__fields.filter(f => f instanceof ProxyField && f.promisedValue()); + const promised = waiting.map(f => f instanceof ProxyField ? f.promisedValue() : ""); + // if we find any ProxyFields that don't have a current value, then + // start the server request for all of them + if (promised.length) { + const promise = DocServer.GetRefFields(promised); + // as soon as we get the fields from the server, set all the list values in one + // action to generate one React dom update. + promise.then(fields => runInAction(() => { + waiting.map((w, i) => w instanceof ProxyField && w.setValue(fields[promised[i]])); + })); + // we also have to mark all lists items with this promise so that any calls to them + // will await the batch request. + // This counts on the handler for 'promise' in the call above being invoked before the + // handler for 'promise' in the lines below. + waiting.map((w, i) => { + w instanceof ProxyField && w.setPromise(promise.then(fields => fields[promised[i]])); + }); + } + return this.__fields.map(toRealField); + } + + @serializable(alias("fields", list(autoObject(), { afterDeserialize: afterDocDeserialize }))) + private get __fields() { + return this.___fields; + } + + private set __fields(value) { + this.___fields = value; + for (const key in value) { + const field = value[key]; + if (!(field instanceof ObjectField)) continue; + (field as ObjectField)[Parent] = this[Self]; + (field as ObjectField)[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); + } + } + + [Copy]() { + const copiedData = this[Self].__fields.map(f => f instanceof ObjectField ? f[Copy]() : f); + const deepCopy = new ListImpl(copiedData as any); + return deepCopy; + } + + // @serializable(alias("fields", list(autoObject()))) + @observable + private ___fields: StoredType[] = []; + + private [Update] = (diff: any) => { + // console.log(diff); + const update = this[OnUpdate]; + // update && update(diff); + update?.(); + } + + private [Self] = this; + private [SelfProxy]: any; + + [ToScriptString]() { + return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`; + } + [ToString]() { + return "List"; + } +} +export type List = ListImpl & (T | (T extends RefField ? Promise : never))[]; +export const List: { new (fields?: T[]): List } = ListImpl as any; + +Scripting.addGlobal("List", List); \ No newline at end of file diff --git a/src/fields/ListSpec.ts b/src/fields/ListSpec.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts new file mode 100644 index 000000000..9aa1c9b04 --- /dev/null +++ b/src/fields/ObjectField.ts @@ -0,0 +1,20 @@ +import { RefField } from "./RefField"; +import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols"; +import { Scripting } from "../client/util/Scripting"; + +export abstract class ObjectField { + protected [OnUpdate](diff?: any) { } + private [Parent]?: RefField | ObjectField; + abstract [Copy](): ObjectField; + + abstract [ToScriptString](): string; + abstract [ToString](): string; +} + +export namespace ObjectField { + export function MakeCopy(field: T) { + return field?.[Copy](); + } +} + +Scripting.addGlobal(ObjectField); \ No newline at end of file diff --git a/src/fields/PresField.ts b/src/fields/PresField.ts new file mode 100644 index 000000000..f236a04fd --- /dev/null +++ b/src/fields/PresField.ts @@ -0,0 +1,6 @@ +//insert code here +import { ObjectField } from "./ObjectField"; + +export abstract class PresField extends ObjectField { + +} \ No newline at end of file diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts new file mode 100644 index 000000000..555faaad0 --- /dev/null +++ b/src/fields/Proxy.ts @@ -0,0 +1,126 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { FieldWaiting } from "./Doc"; +import { primitive, serializable } from "serializr"; +import { observable, action, runInAction } from "mobx"; +import { DocServer } from "../client/DocServer"; +import { RefField } from "./RefField"; +import { ObjectField } from "./ObjectField"; +import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols"; +import { scriptingGlobal } from "../client/util/Scripting"; +import { Plugins } from "./util"; + +@Deserializable("proxy") +export class ProxyField extends ObjectField { + constructor(); + constructor(value: T); + constructor(fieldId: string); + constructor(value?: T | string) { + super(); + if (typeof value === "string") { + this.fieldId = value; + } else if (value) { + this.cache = value; + this.fieldId = value[Id]; + } + } + + [Copy]() { + if (this.cache) return new ProxyField(this.cache); + return new ProxyField(this.fieldId); + } + + [ToScriptString]() { + return "invalid"; + } + [ToString]() { + return "ProxyField"; + } + + @serializable(primitive()) + readonly fieldId: string = ""; + + // This getter/setter and nested object thing is + // because mobx doesn't play well with observable proxies + @observable.ref + private _cache: { readonly field: T | undefined } = { field: undefined }; + private get cache(): T | undefined { + return this._cache.field; + } + private set cache(field: T | undefined) { + this._cache = { field }; + } + + private failed = false; + private promise?: Promise; + + value(): T | undefined | FieldWaiting { + if (this.cache) { + return this.cache; + } + if (this.failed) { + return undefined; + } + if (!this.promise) { + const cached = DocServer.GetCachedRefField(this.fieldId); + if (cached !== undefined) { + runInAction(() => this.cache = cached as any); + return cached as any; + } + this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => { + this.promise = undefined; + this.cache = field; + if (field === undefined) this.failed = true; + return field; + })); + } + return this.promise as any; + } + promisedValue(): string { return !this.cache && !this.failed && !this.promise ? this.fieldId : ""; } + setPromise(promise: any) { + this.promise = promise; + } + @action + setValue(field: any) { + this.promise = undefined; + this.cache = field; + if (field === undefined) this.failed = true; + return field; + } +} + +export namespace ProxyField { + let useProxy = true; + export function DisableProxyFields() { + useProxy = false; + } + + export function EnableProxyFields() { + useProxy = true; + } + + export function WithoutProxy(fn: () => T) { + DisableProxyFields(); + try { + return fn(); + } finally { + EnableProxyFields(); + } + } + + export function initPlugin() { + Plugins.addGetterPlugin((doc, _, value) => { + if (useProxy && value instanceof ProxyField) { + return { value: value.value() }; + } + }); + } +} + +function prefetchValue(proxy: PrefetchProxy) { + return proxy.value() as any; +} + +@scriptingGlobal +@Deserializable("prefetch_proxy", prefetchValue) +export class PrefetchProxy extends ProxyField { +} diff --git a/src/fields/RefField.ts b/src/fields/RefField.ts new file mode 100644 index 000000000..b6ef69750 --- /dev/null +++ b/src/fields/RefField.ts @@ -0,0 +1,21 @@ +import { serializable, primitive, alias } from "serializr"; +import { Utils } from "../Utils"; +import { Id, HandleUpdate, ToScriptString, ToString } from "./FieldSymbols"; +import { afterDocDeserialize } from "../client/util/SerializationHelper"; + +export type FieldId = string; +export abstract class RefField { + @serializable(alias("id", primitive({ afterDeserialize: afterDocDeserialize }))) + private __id: FieldId; + readonly [Id]: FieldId; + + constructor(id?: FieldId) { + this.__id = id || Utils.GenerateGuid(); + this[Id] = this.__id; + } + + protected [HandleUpdate]?(diff: any): void | Promise; + + abstract [ToScriptString](): string; + abstract [ToString](): string; +} diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts new file mode 100644 index 000000000..5cf0e0cc3 --- /dev/null +++ b/src/fields/RichTextField.ts @@ -0,0 +1,41 @@ +import { ObjectField } from "./ObjectField"; +import { serializable } from "serializr"; +import { Deserializable } from "../client/util/SerializationHelper"; +import { Copy, ToScriptString, ToPlainText, ToString } from "./FieldSymbols"; +import { scriptingGlobal } from "../client/util/Scripting"; + +@scriptingGlobal +@Deserializable("RichTextField") +export class RichTextField extends ObjectField { + @serializable(true) + readonly Data: string; + + @serializable(true) + readonly Text: string; + + constructor(data: string, text: string = "") { + super(); + this.Data = data; + this.Text = text; + } + + Empty() { + return !(this.Text || this.Data.toString().includes("dashField")); + } + + [Copy]() { + return new RichTextField(this.Data, this.Text); + } + + [ToScriptString]() { + return `new RichTextField("${this.Data}", "${this.Text}")`; + } + [ToString]() { + return this.Text; + } + + public static DashField(fieldKey: string) { + return new RichTextField(`{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"${fieldKey}","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`, ""); + } + +} \ No newline at end of file diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts new file mode 100644 index 000000000..c475d0d73 --- /dev/null +++ b/src/fields/RichTextUtils.ts @@ -0,0 +1,519 @@ +import { AssertionError } from "assert"; +import { docs_v1 } from "googleapis"; +import { Fragment, Mark, Node } from "prosemirror-model"; +import { sinkListItem } from "prosemirror-schema-list"; +import { Utils } from "../Utils"; +import { Docs } from "../client/documents/Documents"; +import { schema } from "../client/views/nodes/formattedText/schema_rts"; +import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils"; +import { DocServer } from "../client/DocServer"; +import { Networking } from "../client/Network"; +import { FormattedTextBox } from "../client/views/nodes/formattedText/FormattedTextBox"; +import { Doc, Opt } from "./Doc"; +import { Id } from "./FieldSymbols"; +import { RichTextField } from "./RichTextField"; +import { Cast, StrCast } from "./Types"; +import Color = require('color'); +import { EditorState, TextSelection, Transaction } from "prosemirror-state"; +import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils"; + +export namespace RichTextUtils { + + const delimiter = "\n"; + const joiner = ""; + + + export const Initialize = (initial?: string) => { + const content: any[] = []; + const state = { + doc: { + type: "doc", + content, + }, + selection: { + type: "text", + anchor: 0, + head: 0 + } + }; + if (initial && initial.length) { + content.push({ + type: "paragraph", + content: { + type: "text", + text: initial + } + }); + state.selection.anchor = state.selection.head = initial.length + 1; + } + return JSON.stringify(state); + }; + + export const Synthesize = (plainText: string, oldState?: RichTextField) => { + return new RichTextField(ToProsemirrorState(plainText, oldState), plainText); + }; + + export const ToPlainText = (state: EditorState) => { + // Because we're working with plain text, just concatenate all paragraphs + const content = state.doc.content; + const paragraphs: Node[] = []; + content.forEach(node => node.type.name === "paragraph" && paragraphs.push(node)); + + // Functions to flatten ProseMirror paragraph objects (and their components) to plain text + // Concatentate paragraphs and string the result together + const textParagraphs: string[] = paragraphs.map(paragraph => { + const text: string[] = []; + paragraph.content.forEach(node => node.text && text.push(node.text)); + return text.join(joiner) + delimiter; + }); + const plainText = textParagraphs.join(joiner); + return plainText.substring(0, plainText.length - 1); + }; + + export const ToProsemirrorState = (plainText: string, oldState?: RichTextField) => { + // Remap the text, creating blocks split on newlines + const elements = plainText.split(delimiter); + + // Google Docs adds in an extra carriage return automatically, so this counteracts it + !elements[elements.length - 1].length && elements.pop(); + + // Preserve the current state, but re-write the content to be the blocks + const parsed = JSON.parse(oldState ? oldState.Data : Initialize()); + parsed.doc.content = elements.map(text => { + const paragraph: any = { type: "paragraph" }; + text.length && (paragraph.content = [{ type: "text", marks: [], text }]); // An empty paragraph gets treated as a line break + return paragraph; + }); + + // If the new content is shorter than the previous content and selection is unchanged, may throw an out of bounds exception, so we reset it + parsed.selection = { type: "text", anchor: 1, head: 1 }; + + // Export the ProseMirror-compatible state object we've just built + return JSON.stringify(parsed); + }; + + export namespace GoogleDocs { + + export const Export = async (state: EditorState): Promise => { + const nodes: (Node | null)[] = []; + const text = ToPlainText(state); + state.doc.content.forEach(node => { + if (!node.childCount) { + nodes.push(null); + } else { + node.content.forEach(child => nodes.push(child)); + } + }); + const requests = await marksToStyle(nodes); + return { text, requests }; + }; + + interface ImageTemplate { + width: number; + title: string; + url: string; + agnostic: string; + } + + const parseInlineObjects = async (document: docs_v1.Schema$Document): Promise> => { + const inlineObjectMap = new Map(); + const inlineObjects = document.inlineObjects; + + if (inlineObjects) { + const objects = Object.keys(inlineObjects).map(objectId => inlineObjects[objectId]); + const mediaItems: MediaItem[] = objects.map(object => { + const embeddedObject = object.inlineObjectProperties!.embeddedObject!; + return { baseUrl: embeddedObject.imageProperties!.contentUri! }; + }); + + const uploads = await Networking.PostToServer("/googlePhotosMediaGet", { mediaItems }); + + if (uploads.length !== mediaItems.length) { + throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" }); + } + + for (let i = 0; i < objects.length; i++) { + const object = objects[i]; + const { accessPaths } = uploads[i]; + const { agnostic, _m } = accessPaths; + const embeddedObject = object.inlineObjectProperties!.embeddedObject!; + const size = embeddedObject.size!; + const width = size.width!.magnitude!; + + inlineObjectMap.set(object.objectId!, { + title: embeddedObject.title || `Imported Image from ${document.title}`, + width, + url: Utils.prepend(_m.client), + agnostic: Utils.prepend(agnostic.client) + }); + } + } + return inlineObjectMap; + }; + + type BulletPosition = { value: number, sinks: number }; + + interface MediaItem { + baseUrl: string; + } + + export const Import = async (documentId: GoogleApiClientUtils.Docs.DocumentId, textNote: Doc): Promise> => { + const document = await GoogleApiClientUtils.Docs.retrieve({ documentId }); + if (!document) { + return undefined; + } + const inlineObjectMap = await parseInlineObjects(document); + const title = document.title!; + const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document); + let state = FormattedTextBox.blankState(); + const structured = parseLists(paragraphs); + + let position = 3; + const lists: ListGroup[] = []; + const indentMap = new Map(); + let globalOffset = 0; + const nodes: Node[] = []; + for (const element of structured) { + if (Array.isArray(element)) { + lists.push(element); + const positions: BulletPosition[] = []; + const items = element.map(paragraph => { + const item = listItem(state.schema, paragraph.contents); + const sinks = paragraph.bullet!; + positions.push({ + value: position + globalOffset, + sinks + }); + position += item.nodeSize; + globalOffset += 2 * sinks; + return item; + }); + indentMap.set(element, positions); + nodes.push(list(state.schema, items)); + } else { + if (element.contents.some(child => "inlineObjectId" in child)) { + const group = element.contents; + group.forEach((child, i) => { + let node: Opt>; + if ("inlineObjectId" in child) { + node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote); + } else if ("content" in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) { + node = paragraphNode(state.schema, [child]); + } + if (node) { + position += node.nodeSize; + nodes.push(node); + } + }); + } else { + const paragraph = paragraphNode(state.schema, element.contents); + nodes.push(paragraph); + position += paragraph.nodeSize; + } + } + } + state = state.apply(state.tr.replaceWith(0, 2, nodes)); + + const sink = sinkListItem(state.schema.nodes.list_item); + const dispatcher = (tr: Transaction) => state = state.apply(tr); + for (const list of lists) { + for (const pos of indentMap.get(list)!) { + const resolved = state.doc.resolve(pos.value); + state = state.apply(state.tr.setSelection(new TextSelection(resolved))); + for (let i = 0; i < pos.sinks; i++) { + sink(state, dispatcher); + } + } + } + + return { title, text, state }; + }; + + type Paragraph = GoogleApiClientUtils.Docs.Utils.DeconstructedParagraph; + type ListGroup = Paragraph[]; + type PreparedParagraphs = (ListGroup | Paragraph)[]; + + const parseLists = (paragraphs: ListGroup) => { + const groups: PreparedParagraphs = []; + let group: ListGroup = []; + for (const paragraph of paragraphs) { + if (paragraph.bullet !== undefined) { + group.push(paragraph); + } else { + if (group.length) { + groups.push(group); + group = []; + } + groups.push(paragraph); + } + } + group.length && groups.push(group); + return groups; + }; + + const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => { + return schema.node("list_item", null, paragraphNode(schema, runs)); + }; + + const list = (schema: any, items: Node[]): Node => { + return schema.node("bullet_list", null, items); + }; + + const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => { + const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined); + const fragment = children.length ? Fragment.from(children) : undefined; + return schema.node("paragraph", null, fragment); + }; + + const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => { + const { url: src, width, agnostic } = image; + let docid: string; + const guid = Utils.GenerateDeterministicGuid(agnostic); + const backingDocId = StrCast(textNote[guid]); + if (!backingDocId) { + const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 }); + Doc.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument); + docid = backingDoc[Id]; + textNote[guid] = docid; + } else { + docid = backingDocId; + } + return schema.node("image", { src, agnostic, width, docid, float: null, location: "onRight" }); + }; + + const textNode = (schema: any, run: docs_v1.Schema$TextRun) => { + const text = run.content!.removeTrailingNewlines(); + return text.length ? schema.text(text, styleToMarks(schema, run.textStyle)) : undefined; + }; + + const StyleToMark = new Map([ + ["bold", "strong"], + ["italic", "em"], + ["foregroundColor", "pFontColor"], + ["fontSize", "pFontSize"] + ]); + + const styleToMarks = (schema: any, textStyle?: docs_v1.Schema$TextStyle) => { + if (!textStyle) { + return undefined; + } + const marks: Mark[] = []; + Object.keys(textStyle).forEach(key => { + let value: any; + const targeted = key as keyof docs_v1.Schema$TextStyle; + if (value = textStyle[targeted]) { + const attributes: any = {}; + let converted = StyleToMark.get(targeted) || targeted; + + value.url && (attributes.href = value.url); + if (value.color) { + const object = value.color.rgbColor; + attributes.color = Color.rgb(["red", "green", "blue"].map(color => object[color] * 255 || 0)).hex(); + } + if (value.magnitude) { + attributes.fontSize = value.magnitude; + } + + if (converted === "weightedFontFamily") { + converted = ImportFontFamilyMapping.get(value.fontFamily) || "timesNewRoman"; + } + + const mapped = schema.marks[converted]; + if (!mapped) { + alert(`No mapping found for ${converted}!`); + return; + } + + const mark = schema.mark(mapped, attributes); + mark && marks.push(mark); + } + }); + return marks; + }; + + const MarkToStyle = new Map([ + ["strong", "bold"], + ["em", "italic"], + ["pFontColor", "foregroundColor"], + ["pFontSize", "fontSize"], + ["timesNewRoman", "weightedFontFamily"], + ["georgia", "weightedFontFamily"], + ["comicSans", "weightedFontFamily"], + ["tahoma", "weightedFontFamily"], + ["impact", "weightedFontFamily"] + ]); + + const ExportFontFamilyMapping = new Map([ + ["timesNewRoman", "Times New Roman"], + ["arial", "Arial"], + ["georgia", "Georgia"], + ["comicSans", "Comic Sans MS"], + ["tahoma", "Tahoma"], + ["impact", "Impact"] + ]); + + const ImportFontFamilyMapping = new Map([ + ["Times New Roman", "timesNewRoman"], + ["Arial", "arial"], + ["Georgia", "georgia"], + ["Comic Sans MS", "comicSans"], + ["Tahoma", "tahoma"], + ["Impact", "impact"] + ]); + + const ignored = ["user_mark"]; + + const marksToStyle = async (nodes: (Node | null)[]): Promise => { + const requests: docs_v1.Schema$Request[] = []; + let position = 1; + for (const node of nodes) { + if (node === null) { + position += 2; + continue; + } + const { marks, attrs, nodeSize } = node; + const textStyle: docs_v1.Schema$TextStyle = {}; + const information: LinkInformation = { + startIndex: position, + endIndex: position + nodeSize, + textStyle + }; + let mark: Mark; + const markMap = BuildMarkMap(marks); + for (const markName of Object.keys(schema.marks)) { + if (ignored.includes(markName) || !(mark = markMap[markName])) { + continue; + } + let converted = MarkToStyle.get(markName) || markName as keyof docs_v1.Schema$TextStyle; + let value: any = true; + if (!converted) { + continue; + } + const { attrs } = mark; + switch (converted) { + case "link": + let url = attrs.href; + const delimiter = "/doc/"; + const alreadyShared = "?sharing=true"; + if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) { + const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]); + if (linkDoc instanceof Doc) { + let exported = (await Cast(linkDoc.anchor2, Doc))!; + if (!exported.customLayout) { + exported = Doc.MakeAlias(exported); + Doc.makeCustomViewClicked(exported, Docs.Create.FreeformDocument); + linkDoc.anchor2 = exported; + } + url = Utils.shareUrl(exported[Id]); + } + } + value = { url }; + textStyle.foregroundColor = fromRgb.blue; + textStyle.bold = true; + break; + case "fontSize": + value = { magnitude: attrs.fontSize, unit: "PT" }; + break; + case "foregroundColor": + value = fromHex(attrs.color); + break; + case "weightedFontFamily": + value = { fontFamily: ExportFontFamilyMapping.get(markName) }; + } + let matches: RegExpExecArray | null; + if ((matches = /p(\d+)/g.exec(markName)) !== null) { + converted = "fontSize"; + value = { magnitude: parseInt(matches[1].replace("px", "")), unit: "PT" }; + } + textStyle[converted] = value; + } + if (Object.keys(textStyle).length) { + requests.push(EncodeStyleUpdate(information)); + } + if (node.type.name === "image") { + const width = attrs.width; + requests.push(await EncodeImage({ + startIndex: position + nodeSize - 1, + uri: attrs.agnostic, + width: Number(typeof width === "string" ? width.replace("px", "") : width) + })); + } + position += nodeSize; + } + return requests; + }; + + const BuildMarkMap = (marks: Mark[]) => { + const markMap: { [type: string]: Mark } = {}; + marks.forEach(mark => markMap[mark.type.name] = mark); + return markMap; + }; + + interface LinkInformation { + startIndex: number; + endIndex: number; + textStyle: docs_v1.Schema$TextStyle; + } + + interface ImageInformation { + startIndex: number; + width: number; + uri: string; + } + + namespace fromRgb { + + export const convert = (red: number, green: number, blue: number): docs_v1.Schema$OptionalColor => { + return { + color: { + rgbColor: { + red: red / 255, + green: green / 255, + blue: blue / 255 + } + } + }; + }; + + export const red = convert(255, 0, 0); + export const green = convert(0, 255, 0); + export const blue = convert(0, 0, 255); + + } + + const fromHex = (color: string): docs_v1.Schema$OptionalColor => { + const c = Color(color); + return fromRgb.convert(c.red(), c.green(), c.blue()); + }; + + const EncodeStyleUpdate = (information: LinkInformation): docs_v1.Schema$Request => { + const { startIndex, endIndex, textStyle } = information; + return { + updateTextStyle: { + fields: "*", + range: { startIndex, endIndex }, + textStyle + } as docs_v1.Schema$UpdateTextStyleRequest + }; + }; + + const EncodeImage = async ({ uri, width, startIndex }: ImageInformation) => { + if (!uri) { + return {}; + } + const source = [Docs.Create.ImageDocument(uri)]; + const baseUrls = await GooglePhotos.Transactions.UploadThenFetch(source); + if (baseUrls) { + return { + insertInlineImage: { + uri: baseUrls[0], + objectSize: { width: { magnitude: width, unit: "PT" } }, + location: { index: startIndex } + } + }; + } + return {}; + }; + } + +} \ No newline at end of file diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts new file mode 100644 index 000000000..72bce283d --- /dev/null +++ b/src/fields/Schema.ts @@ -0,0 +1,120 @@ +import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from "./Types"; +import { Doc, Field } from "./Doc"; +import { ObjectField } from "./ObjectField"; +import { RefField } from "./RefField"; +import { SelfProxy } from "./FieldSymbols"; + +type AllToInterface = { + 1: ToInterface> & AllToInterface>, + 0: ToInterface> +}[HasTail extends true ? 1 : 0]; + +export const emptySchema = createSchema({}); +export const Document = makeInterface(emptySchema); +export type Document = makeInterface<[typeof emptySchema]>; + +export interface InterfaceFunc { + (docs: Doc[]): makeInterface[]; + (): makeInterface; + (doc: Doc): makeInterface; +} + +export type makeInterface = AllToInterface & Doc & { proto: Doc | undefined }; +// export function makeInterface(schemas: T): (doc: U) => All; +// export function makeInterface(schema: T): (doc: U) => makeInterface; +export function makeInterface(...schemas: T): InterfaceFunc { + const schema: Interface = {}; + for (const s of schemas) { + for (const key in s) { + schema[key] = s[key]; + } + } + const proto = new Proxy({}, { + get(target: any, prop, receiver) { + const field = receiver.doc[prop]; + if (prop in schema) { + const desc = prop === "proto" ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should? + if (typeof desc === "object" && "defaultVal" in desc && "type" in desc) {//defaultSpec + return Cast(field, desc.type, desc.defaultVal); + } else if (typeof desc === "function" && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) { + const doc = Cast(field, Doc); + if (doc === undefined) { + return undefined; + } else if (doc instanceof Doc) { + return desc(doc); + } else { + return doc.then(doc => doc && desc(doc)); + } + } else { + return Cast(field, desc); + } + } + return field; + }, + set(target: any, prop, value, receiver) { + receiver.doc[prop] = value; + return true; + } + }); + const fn = (doc: Doc) => { + doc = doc[SelfProxy]; + // if (!(doc instanceof Doc)) { + // throw new Error("Currently wrapping a schema in another schema isn't supported"); + // } + const obj = Object.create(proto, { doc: { value: doc, writable: false } }); + return obj; + }; + return function (doc?: Doc | Doc[]) { + doc = doc || new Doc; + if (doc instanceof Doc) { + return fn(doc); + } else { + return doc.map(fn); + } + }; +} + +export type makeStrictInterface = Partial>; +export function makeStrictInterface(schema: T): (doc: Doc) => makeStrictInterface { + const proto = {}; + for (const key in schema) { + const type = schema[key]; + Object.defineProperty(proto, key, { + get() { + return Cast(this.__doc[key], type as any); + }, + set(value) { + value = Cast(value, type as any); + if (value !== undefined) { + this.__doc[key] = value; + return; + } + throw new TypeError("Expected type " + type); + } + }); + } + return function (doc: any) { + if (!(doc instanceof Doc)) { + throw new Error("Currently wrapping a schema in another schema isn't supported"); + } + const obj = Object.create(proto); + obj.__doc = doc; + return obj; + }; +} + +export function createSchema(schema: T): T & { proto: ToConstructor } { + (schema as any).proto = Doc; + return schema as any; +} + +export function listSpec>(type: U): ListSpec> { + return { List: type as any };//TODO Types +} + +export function defaultSpec>(type: T, defaultVal: ToType): DefaultFieldConstructor> { + return { + type: type as any, + defaultVal + }; +} \ No newline at end of file diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts new file mode 100644 index 000000000..07c90f5a2 --- /dev/null +++ b/src/fields/SchemaHeaderField.ts @@ -0,0 +1,122 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, primitive } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { Copy, ToScriptString, ToString, OnUpdate } from "./FieldSymbols"; +import { scriptingGlobal } from "../client/util/Scripting"; +import { ColumnType } from "../client/views/collections/CollectionSchemaView"; + +export const PastelSchemaPalette = new Map([ + // ["pink1", "#FFB4E8"], + ["pink2", "#ff9cee"], + ["pink3", "#ffccf9"], + ["pink4", "#fcc2ff"], + ["pink5", "#f6a6ff"], + ["purple1", "#b28dff"], + ["purple2", "#c5a3ff"], + ["purple3", "#d5aaff"], + ["purple4", "#ecd4ff"], + // ["purple5", "#fb34ff"], + ["purple6", "#dcd3ff"], + ["purple7", "#a79aff"], + ["purple8", "#b5b9ff"], + ["purple9", "#97a2ff"], + ["bluegreen1", "#afcbff"], + ["bluegreen2", "#aff8db"], + ["bluegreen3", "#c4faf8"], + ["bluegreen4", "#85e3ff"], + ["bluegreen5", "#ace7ff"], + // ["bluegreen6", "#6eb5ff"], + ["bluegreen7", "#bffcc6"], + ["bluegreen8", "#dbffd6"], + ["yellow1", "#f3ffe3"], + ["yellow2", "#e7ffac"], + ["yellow3", "#ffffd1"], + ["yellow4", "#fff5ba"], + // ["red1", "#ffc9de"], + ["red2", "#ffabab"], + ["red3", "#ffbebc"], + ["red4", "#ffcbc1"], + ["orange1", "#ffd5b3"], +]); + +export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)]; + +export const DarkPastelSchemaPalette = new Map([ + ["pink2", "#c932b0"], + ["purple4", "#913ad6"], + ["bluegreen1", "#3978ed"], + ["bluegreen7", "#2adb3e"], + ["bluegreen5", "#21b0eb"], + ["yellow4", "#edcc0c"], + ["red2", "#eb3636"], + ["orange1", "#f2740f"], +]); + +@scriptingGlobal +@Deserializable("schemaheader") +export class SchemaHeaderField extends ObjectField { + @serializable(primitive()) + heading: string; + @serializable(primitive()) + color: string; + @serializable(primitive()) + type: number; + @serializable(primitive()) + width: number; + @serializable(primitive()) + collapsed: boolean | undefined; + @serializable(primitive()) + desc: boolean | undefined; // boolean determines sort order, undefined when no sort + + constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) { + super(); + + this.heading = heading; + this.color = color; + this.type = type ? type : 0; + this.width = width ? width : -1; + this.desc = desc; + this.collapsed = collapsed; + } + + setHeading(heading: string) { + this.heading = heading; + this[OnUpdate](); + } + + setColor(color: string) { + this.color = color; + this[OnUpdate](); + } + + setType(type: ColumnType) { + this.type = type; + this[OnUpdate](); + } + + setWidth(width: number) { + this.width = width; + this[OnUpdate](); + } + + setDesc(desc: boolean | undefined) { + this.desc = desc; + this[OnUpdate](); + } + + setCollapsed(collapsed: boolean | undefined) { + this.collapsed = collapsed; + this[OnUpdate](); + } + + [Copy]() { + return new SchemaHeaderField(this.heading, this.color, this.type); + } + + [ToScriptString]() { + return `invalid`; + } + [ToString]() { + return `SchemaHeaderField`; + } +} \ No newline at end of file diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts new file mode 100644 index 000000000..f05f431ac --- /dev/null +++ b/src/fields/ScriptField.ts @@ -0,0 +1,193 @@ +import { ObjectField } from "./ObjectField"; +import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions, CompileError, CompileResult } from "../client/util/Scripting"; +import { Copy, ToScriptString, ToString, Parent, SelfProxy } from "./FieldSymbols"; +import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; +import { Deserializable, autoObject } from "../client/util/SerializationHelper"; +import { Doc, Field } from "./Doc"; +import { Plugins, setter } from "./util"; +import { computedFn } from "mobx-utils"; +import { ProxyField } from "./Proxy"; +import { Cast } from "./Types"; + +function optional(propSchema: PropSchema) { + return custom(value => { + if (value !== undefined) { + return propSchema.serializer(value); + } + return SKIP; + }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => { + if (jsonValue !== undefined) { + return propSchema.deserializer(jsonValue, callback, context, oldValue); + } + return SKIP; + }); +} + +const optionsSchema = createSimpleSchema({ + requiredType: true, + addReturn: true, + typecheck: true, + editable: true, + readonly: true, + params: optional(map(primitive())) +}); + +const scriptSchema = createSimpleSchema({ + options: object(optionsSchema), + originalScript: true +}); + +async function deserializeScript(script: ScriptField) { + const captures: ProxyField = (script as any).captures; + if (captures) { + const doc = (await captures.value())!; + const captured: any = {}; + const keys = Object.keys(doc); + const vals = await Promise.all(keys.map(key => doc[key]) as any); + keys.forEach((key, i) => captured[key] = vals[i]); + (script.script.options as any).capturedVariables = captured; + } + const comp = CompileScript(script.script.originalScript, script.script.options); + if (!comp.compiled) { + throw new Error("Couldn't compile loaded script"); + } + (script as any).script = comp; +} + +@scriptingGlobal +@Deserializable("script", deserializeScript) +export class ScriptField extends ObjectField { + @serializable(object(scriptSchema)) + readonly script: CompiledScript; + @serializable(object(scriptSchema)) + readonly setterscript: CompiledScript | undefined; + + @serializable(autoObject()) + private captures?: ProxyField; + + constructor(script: CompiledScript, setterscript?: CompileResult) { + super(); + + if (script?.options.capturedVariables) { + const doc = Doc.assign(new Doc, script.options.capturedVariables); + this.captures = new ProxyField(doc); + } + this.setterscript = setterscript?.compiled ? setterscript : undefined; + this.script = script; + } + + // init(callback: (res: Field) => any) { + // const options = this.options!; + // const keys = Object.keys(options.options.capturedIds); + // Server.GetFields(keys).then(fields => { + // let captured: { [name: string]: Field } = {}; + // keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); + // const opts: ScriptOptions = { + // addReturn: options.options.addReturn, + // params: options.options.params, + // requiredType: options.options.requiredType, + // capturedVariables: captured + // }; + // const script = CompileScript(options.script, opts); + // if (!script.compiled) { + // throw new Error("Can't compile script"); + // } + // this._script = script; + // callback(this); + // }); + // } + + [Copy](): ObjectField { + return new ScriptField(this.script); + } + toString() { + return `${this.script.originalScript}`; + } + + [ToScriptString]() { + return "script field"; + } + [ToString]() { + return this.script.originalScript; + } + public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) { + const compiled = CompileScript(script, { + params: { this: Doc.name, self: Doc.name, _last_: "any", ...params }, + typecheck: false, + editable: true, + addReturn: addReturn, + capturedVariables + }); + return compiled; + } + public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) { + const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); + return compiled.compiled ? new ScriptField(compiled) : undefined; + } + + public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) { + const compiled = ScriptField.CompileScript(script, params, false, capturedVariables); + return compiled.compiled ? new ScriptField(compiled) : undefined; + } +} + +@scriptingGlobal +@Deserializable("computed", deserializeScript) +export class ComputedField extends ScriptField { + _lastComputedResult: any; + //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc + value = computedFn((doc: Doc) => this._valueOutsideReaction(doc)); + _valueOutsideReaction = (doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult }, console.log).result; + + + constructor(script: CompiledScript, setterscript?: CompiledScript) { + super(script, + !setterscript && script?.originalScript.includes("self.timecode") ? + ScriptField.CompileScript("self['x' + self.timecode] = value", { value: "any" }, true) : setterscript); + } + + public static MakeScript(script: string, params: object = {}) { + const compiled = ScriptField.CompileScript(script, params, false); + return compiled.compiled ? new ComputedField(compiled) : undefined; + } + public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }, setterScript?: string) { + const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); + const setCompiled = setterScript ? ScriptField.CompileScript(setterScript, params, true, capturedVariables) : undefined; + return compiled.compiled ? new ComputedField(compiled, setCompiled?.compiled ? setCompiled : undefined) : undefined; + } + public static MakeInterpolated(fieldKey: string, interpolatorKey: string) { + const getField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}]`, {}, true, {}); + const setField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}] = value`, { value: "any" }, true, {}); + return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; + } +} + +export namespace ComputedField { + let useComputed = true; + export function DisableComputedFields() { + useComputed = false; + } + + export function EnableComputedFields() { + useComputed = true; + } + + export const undefined = "__undefined"; + + export function WithoutComputed(fn: () => T) { + DisableComputedFields(); + try { + return fn(); + } finally { + EnableComputedFields(); + } + } + + export function initPlugin() { + Plugins.addGetterPlugin((doc, _, value) => { + if (useComputed && value instanceof ComputedField) { + return { value: value._valueOutsideReaction(doc), shouldReturn: true }; + } + }); + } +} \ No newline at end of file diff --git a/src/fields/Types.ts b/src/fields/Types.ts new file mode 100644 index 000000000..3d784448d --- /dev/null +++ b/src/fields/Types.ts @@ -0,0 +1,108 @@ +import { Field, Opt, FieldResult, Doc } from "./Doc"; +import { List } from "./List"; +import { RefField } from "./RefField"; +import { DateField } from "./DateField"; +import { ScriptField } from "./ScriptField"; + +export type ToType = + T extends "string" ? string : + T extends "number" ? number : + T extends "boolean" ? boolean : + T extends ListSpec ? List : + // T extends { new(...args: any[]): infer R } ? (R | Promise) : never; + T extends DefaultFieldConstructor ? never : + T extends { new(...args: any[]): List } ? never : + T extends { new(...args: any[]): infer R } ? R : + T extends (doc?: Doc) => infer R ? R : never; + +export type ToConstructor = + T extends string ? "string" : + T extends number ? "number" : + T extends boolean ? "boolean" : + T extends List ? ListSpec : + new (...args: any[]) => T; + +export type ToInterface = { + [P in Exclude]: T[P] extends DefaultFieldConstructor ? Exclude, undefined> : FieldResult>; +}; + +// type ListSpec = { List: ToContructor> | ListSpec> }; +export type ListSpec = { List: ToConstructor }; + +export type DefaultFieldConstructor = { + type: ToConstructor, + defaultVal: T +}; + +// type ListType = { 0: List>>, 1: ToType> }[HasTail extends true ? 0 : 1]; + +export type Head = T extends [any, ...any[]] ? T[0] : never; +export type Tail = + ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []; +export type HasTail = T extends ([] | [any]) ? false : true; + +export type InterfaceValue = ToConstructor | ListSpec | DefaultFieldConstructor | ((doc?: Doc) => any); +//TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial +export interface Interface { + [key: string]: InterfaceValue; + // [key: string]: ToConstructor | ListSpec; +} +export type WithoutRefField = T extends RefField ? never : T; + +export type CastCtor = ToConstructor | ListSpec; + +export function Cast(field: FieldResult, ctor: T): FieldResult>; +export function Cast(field: FieldResult, ctor: T, defaultVal: WithoutList>> | null): WithoutList>; +export function Cast(field: FieldResult, ctor: T, defaultVal?: ToType | null): FieldResult> | undefined { + if (field instanceof Promise) { + return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal; + } + if (field !== undefined && !(field instanceof Promise)) { + if (typeof ctor === "string") { + if (typeof field === ctor) { + return field as ToType; + } + } else if (typeof ctor === "object") { + if (field instanceof List) { + return field as any; + } + } else if (field instanceof (ctor as any)) { + return field as ToType; + } + } + return defaultVal === null ? undefined : defaultVal; +} + +export function NumCast(field: FieldResult, defaultVal: number | null = 0) { + return Cast(field, "number", defaultVal); +} + +export function StrCast(field: FieldResult, defaultVal: string | null = "") { + return Cast(field, "string", defaultVal); +} + +export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) { + return Cast(field, "boolean", defaultVal); +} +export function DateCast(field: FieldResult) { + return Cast(field, DateField, null); +} + +export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) { + return Cast(field, ScriptField, defaultVal); +} + +type WithoutList = T extends List ? (R extends RefField ? (R | Promise)[] : R[]) : T; + +export function FieldValue>(field: FieldResult, defaultValue: U): WithoutList; +export function FieldValue(field: FieldResult): Opt; +export function FieldValue(field: FieldResult, defaultValue?: T): Opt { + return (field instanceof Promise || field === undefined) ? defaultValue : field; +} + +export interface PromiseLike { + then(callback: (field: Opt) => void): void; +} +export function PromiseValue(field: FieldResult): PromiseLike> { + return field instanceof Promise ? field : { then(cb: ((field: Opt) => void)) { return cb(field); } }; +} \ No newline at end of file diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts new file mode 100644 index 000000000..fb71160ca --- /dev/null +++ b/src/fields/URLField.ts @@ -0,0 +1,53 @@ +import { Deserializable } from "../client/util/SerializationHelper"; +import { serializable, custom } from "serializr"; +import { ObjectField } from "./ObjectField"; +import { ToScriptString, ToString, Copy } from "./FieldSymbols"; +import { Scripting, scriptingGlobal } from "../client/util/Scripting"; + +function url() { + return custom( + function (value: URL) { + return value.href; + }, + function (jsonValue: string) { + return new URL(jsonValue); + } + ); +} + +export abstract class URLField extends ObjectField { + @serializable(url()) + readonly url: URL; + + constructor(url: string); + constructor(url: URL); + constructor(url: URL | string) { + super(); + if (typeof url === "string") { + url = new URL(url); + } + this.url = url; + } + + [ToScriptString]() { + return `new ${this.constructor.name}("${this.url.href}")`; + } + [ToString]() { + return this.url.href; + } + + [Copy](): this { + return new (this.constructor as any)(this.url); + } +} + +export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short.ogg"; + +@scriptingGlobal @Deserializable("audio") export class AudioField extends URLField { } +@scriptingGlobal @Deserializable("image") export class ImageField extends URLField { } +@scriptingGlobal @Deserializable("video") export class VideoField extends URLField { } +@scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { } +@scriptingGlobal @Deserializable("web") export class WebField extends URLField { } +@scriptingGlobal @Deserializable("youtube") export class YoutubeField extends URLField { } +@scriptingGlobal @Deserializable("webcam") export class WebCamField extends URLField { } + diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts new file mode 100644 index 000000000..cacba43b6 --- /dev/null +++ b/src/fields/documentSchemas.ts @@ -0,0 +1,100 @@ +import { makeInterface, createSchema, listSpec } from "./Schema"; +import { ScriptField } from "./ScriptField"; +import { Doc } from "./Doc"; +import { DateField } from "./DateField"; + +export const documentSchema = createSchema({ + // content properties + type: "string", // enumerated type of document -- should be template-specific (ie, start with an '_') + title: "string", // document title (can be on either data document or layout) + isTemplateForField: "string",// if specified, it indicates the document is a template that renders the specified field + creationDate: DateField, // when the document was created + links: listSpec(Doc), // computed (readonly) list of links associated with this document + + // "Location" properties in a very general sense + currentTimecode: "number", // current play back time of a temporal document (video / audio) + displayTimecode: "number", // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) + inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently + x: "number", // x coordinate when in a freeform view + y: "number", // y coordinate when in a freeform view + z: "number", // z "coordinate" - non-zero specifies the overlay layer of a freeformview + zIndex: "number", // zIndex of a document in a freeform view + scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) + scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) + + // appearance properties on the layout + _autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents + _nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set + _nativeHeight: "number", // " + _width: "number", // width of document in its container's coordinate system + _height: "number", // " + _xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set + _yPadding: "number", // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set + _xMargin: "number", // margin added on left/right of most documents to add separation from their container + _yMargin: "number", // margin added on top/bottom of most documents to add separation from their container + _overflow: "string", // sets overflow behvavior for CollectionFreeForm views + _showCaption: "string", // whether editable caption text is overlayed at the bottom of the document + _showTitle: "string", // the fieldkey whose contents should be displayed at the top of the document + _showTitleHover: "string", // the showTitle should be shown only on hover + _showAudio: "boolean", // whether to show the audio record icon on documents + _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents + _LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews + _pivotField: "string", // specifies which field key should be used as the timeline/pivot axis + _replacedChrome: "string", // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's. + _chromeStatus: "string", // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed' + _fontSize: "number", + _fontFamily: "string", + _sidebarWidthPercent: "string", // percent of text window width taken up by sidebar + + // appearance properties on the data document + backgroundColor: "string", // background color of document + borderRounding: "string", // border radius rounding of document + boxShadow: "string", // the amount of shadow around the perimeter of a document + color: "string", // foreground color of document + fitToBox: "boolean", // whether freeform view contents should be zoomed/panned to fill the area of the document view + fontSize: "string", + layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below + layoutKey: "string", // holds the field key for the field that actually holds the current lyoat + letterSpacing: "string", + opacity: "number", // opacity of document + strokeWidth: "number", + textTransform: "string", + treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden + treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree + treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) + + // interaction and linking properties + ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) + onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. + followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) + isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations + isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked + isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) + lockedPosition: "boolean", // whether the document can be moved (dragged) + lockedTransform: "boolean", // whether the document can be panned/zoomed + + // drag drop properties + dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. + dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move") + targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move' + childDropAction: "string", // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy") + removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped +}); + + +export const collectionSchema = createSchema({ + childLayoutTemplateName: "string", // the name of a template to use to override the layoutKey when rendering a document -- ONLY used in DocHolderBox + childLayoutTemplate: Doc, // layout template to use to render children of a collecion + childLayoutString: "string", //layout string to use to render children of a collection + childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template) + dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc. + onChildClick: ScriptField, // script to run for each child when its clicked + onChildDoubleClick: ScriptField, // script to run for each child when its clicked + onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view +}); + +export type Document = makeInterface<[typeof documentSchema]>; +export const Document = makeInterface(documentSchema); diff --git a/src/fields/util.ts b/src/fields/util.ts new file mode 100644 index 000000000..a287b0210 --- /dev/null +++ b/src/fields/util.ts @@ -0,0 +1,199 @@ +import { UndoManager } from "../client/util/UndoManager"; +import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym } from "./Doc"; +import { SerializationHelper } from "../client/util/SerializationHelper"; +import { ProxyField, PrefetchProxy } from "./Proxy"; +import { RefField } from "./RefField"; +import { ObjectField } from "./ObjectField"; +import { action, trace } from "mobx"; +import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; +import { DocServer } from "../client/DocServer"; +import { ComputedField } from "./ScriptField"; +import { ScriptCast } from "./Types"; + +function _readOnlySetter(): never { + throw new Error("Documents can't be modified in read-only mode"); +} + +const tracing = false; +export function TraceMobx() { + tracing && trace(); +} + +export interface GetterResult { + value: FieldResult; + shouldReturn?: boolean; +} +export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined; +const getterPlugins: GetterPlugin[] = []; + +export namespace Plugins { + export function addGetterPlugin(plugin: GetterPlugin) { + getterPlugins.push(plugin); + } +} + +const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { + //console.log("-set " + target[SelfProxy].title + "(" + target[SelfProxy][prop] + ")." + prop.toString() + " = " + value); + if (SerializationHelper.IsSerializing()) { + target[prop] = value; + return true; + } + + if (typeof prop === "symbol") { + target[prop] = value; + return true; + } + if (value !== undefined) { + value = value[SelfProxy] || value; + } + const curValue = target.__fields[prop]; + if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) { + // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically + // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way + return true; + } + if (value instanceof RefField) { + value = new ProxyField(value); + } + if (value instanceof ObjectField) { + if (value[Parent] && value[Parent] !== receiver && !(value instanceof PrefetchProxy)) { + throw new Error("Can't put the same object in multiple documents at the same time"); + } + value[Parent] = receiver; + value[OnUpdate] = updateFunction(target, prop, value, receiver); + } + if (curValue instanceof ObjectField) { + delete curValue[Parent]; + delete curValue[OnUpdate]; + } + const writeMode = DocServer.getFieldWriteMode(prop as string); + const fromServer = target[UpdatingFromServer]; + const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail); + const writeToDoc = sameAuthor || (writeMode !== DocServer.WriteMode.LiveReadonly); + const writeToServer = sameAuthor || (writeMode === DocServer.WriteMode.Default); + if (writeToDoc) { + if (value === undefined) { + delete target.__fields[prop]; + } else { + target.__fields[prop] = value; + } + if (typeof value === "object" && !(value instanceof ObjectField)) debugger; + if (writeToServer) { + if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } }); + else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } }); + } else { + DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); + } + UndoManager.AddEvent({ + redo: () => receiver[prop] = value, + undo: () => receiver[prop] = curValue + }); + } + return true; +}); + +let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl; + +export function makeReadOnly() { + _setter = _readOnlySetter; +} + +export function makeEditable() { + _setter = _setterImpl; +} + +const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", + "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; +export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { + let prop = in_prop; + if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { + if (!prop.startsWith("_")) { + console.log(prop + " is deprecated - switch to _" + prop); + prop = "_" + prop; + } + if (target.__LAYOUT__) { + target.__LAYOUT__[prop] = value; + return true; + } + } + if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript) { + return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; + } + return _setter(target, prop, value, receiver); +} + +export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { + let prop = in_prop; + if (prop === LayoutSym) { + return target.__LAYOUT__; + } + if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { + if (!prop.startsWith("_")) { + console.log(prop + " is deprecated - switch to _" + prop); + prop = "_" + prop; + } + if (target.__LAYOUT__) return target.__LAYOUT__[prop]; + } + if (prop === "then") {//If we're being awaited + return undefined; + } + if (typeof prop === "symbol") { + return target.__fields[prop] || target[prop]; + } + if (SerializationHelper.IsSerializing()) { + return target[prop]; + } + return getFieldImpl(target, prop, receiver); +} + +function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any { + receiver = receiver || target[SelfProxy]; + let field = target.__fields[prop]; + for (const plugin of getterPlugins) { + const res = plugin(receiver, prop, field); + if (res === undefined) continue; + if (res.shouldReturn) { + return res.value; + } else { + field = res.value; + } + } + if (field === undefined && !ignoreProto && prop !== "proto") { + const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters + if (proto instanceof Doc) { + return getFieldImpl(proto[Self], prop, receiver, ignoreProto); + } + return undefined; + } + return field; + +} +export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any { + return getFieldImpl(target, prop, undefined, ignoreProto); +} + +export function deleteProperty(target: any, prop: string | number | symbol) { + if (typeof prop === "symbol") { + delete target[prop]; + return true; + } + target[SelfProxy][prop] = undefined; + return true; +} + +export function updateFunction(target: any, prop: any, value: any, receiver: any) { + let current = ObjectField.MakeCopy(value); + return (diff?: any) => { + if (true || !diff) { + diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; + const oldValue = current; + const newValue = ObjectField.MakeCopy(value); + current = newValue; + UndoManager.AddEvent({ + redo() { receiver[prop] = newValue; }, + undo() { receiver[prop] = oldValue; } + }); + } + target[Update](diff); + }; +} \ No newline at end of file diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx index f30e9869a..231870531 100644 --- a/src/mobile/ImageUpload.tsx +++ b/src/mobile/ImageUpload.tsx @@ -4,10 +4,10 @@ import { Docs } from '../client/documents/Documents'; import "./ImageUpload.scss"; import React = require('react'); import { DocServer } from '../client/DocServer'; -import { Opt, Doc } from '../new_fields/Doc'; -import { Cast } from '../new_fields/Types'; -import { listSpec } from '../new_fields/Schema'; -import { List } from '../new_fields/List'; +import { Opt, Doc } from '../fields/Doc'; +import { Cast } from '../fields/Types'; +import { listSpec } from '../fields/Schema'; +import { List } from '../fields/List'; import { observer } from 'mobx-react'; import { observable } from 'mobx'; import { Utils } from '../Utils'; diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx index 1537ae034..973931615 100644 --- a/src/mobile/MobileInkOverlay.tsx +++ b/src/mobile/MobileInkOverlay.tsx @@ -4,11 +4,11 @@ import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPosition import { observable, action } from "mobx"; import { GestureUtils } from "../pen-gestures/GestureUtils"; import "./MobileInkOverlay.scss"; -import { StrCast, Cast } from '../new_fields/Types'; +import { StrCast, Cast } from '../fields/Types'; import { DragManager } from "../client/util/DragManager"; import { DocServer } from '../client/DocServer'; -import { Doc, DocListCastAsync } from '../new_fields/Doc'; -import { listSpec } from '../new_fields/Schema'; +import { Doc, DocListCastAsync } from '../fields/Doc'; +import { listSpec } from '../fields/Schema'; @observer diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index 9d4d58ad1..6c2e797d6 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -16,12 +16,12 @@ import { InkingControl } from '../client/views/InkingControl'; import { DocumentView } from '../client/views/nodes/DocumentView'; import { RadialMenu } from '../client/views/nodes/RadialMenu'; import { PreviewCursor } from '../client/views/PreviewCursor'; -import { Doc, DocListCast, FieldResult } from '../new_fields/Doc'; -import { Id } from '../new_fields/FieldSymbols'; -import { InkTool } from '../new_fields/InkField'; -import { listSpec } from '../new_fields/Schema'; -import { Cast, FieldValue } from '../new_fields/Types'; -import { WebField } from "../new_fields/URLField"; +import { Doc, DocListCast, FieldResult } from '../fields/Doc'; +import { Id } from '../fields/FieldSymbols'; +import { InkTool } from '../fields/InkField'; +import { listSpec } from '../fields/Schema'; +import { Cast, FieldValue } from '../fields/Types'; +import { WebField } from "../fields/URLField"; import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; import { emptyFunction, emptyPath, returnEmptyString, returnFalse, returnOne, returnTrue, returnZero } from '../Utils'; import "./MobileInterface.scss"; diff --git a/src/new_fields/CursorField.ts b/src/new_fields/CursorField.ts deleted file mode 100644 index 28467377b..000000000 --- a/src/new_fields/CursorField.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ObjectField } from "./ObjectField"; -import { observable } from "mobx"; -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, createSimpleSchema, object, date } from "serializr"; -import { OnUpdate, ToScriptString, ToString, Copy } from "./FieldSymbols"; - -export type CursorPosition = { - x: number, - y: number -}; - -export type CursorMetadata = { - id: string, - identifier: string, - timestamp: number -}; - -export type CursorData = { - metadata: CursorMetadata, - position: CursorPosition -}; - -const PositionSchema = createSimpleSchema({ - x: true, - y: true -}); - -const MetadataSchema = createSimpleSchema({ - id: true, - identifier: true, - timestamp: true -}); - -const CursorSchema = createSimpleSchema({ - metadata: object(MetadataSchema), - position: object(PositionSchema) -}); - -@Deserializable("cursor") -export default class CursorField extends ObjectField { - - @serializable(object(CursorSchema)) - readonly data: CursorData; - - constructor(data: CursorData) { - super(); - this.data = data; - } - - setPosition(position: CursorPosition) { - this.data.position = position; - this.data.metadata.timestamp = Date.now(); - this[OnUpdate](); - } - - [Copy]() { - return new CursorField(this.data); - } - - [ToScriptString]() { - return "invalid"; - } - [ToString]() { - return "invalid"; - } -} \ No newline at end of file diff --git a/src/new_fields/DateField.ts b/src/new_fields/DateField.ts deleted file mode 100644 index a925148c2..000000000 --- a/src/new_fields/DateField.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, date } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, ToString } from "./FieldSymbols"; -import { scriptingGlobal, Scripting } from "../client/util/Scripting"; - -@scriptingGlobal -@Deserializable("date") -export class DateField extends ObjectField { - @serializable(date()) - readonly date: Date; - - constructor(date: Date = new Date()) { - super(); - this.date = date; - } - - [Copy]() { - return new DateField(this.date); - } - - toString() { - return `${this.date.toISOString()}`; - } - - [ToScriptString]() { - return `new DateField(new Date(${this.date.toISOString()}))`; - } - [ToString]() { - return this.date.toISOString(); - } -} - -Scripting.addGlobal(function d(...dateArgs: any[]) { - return new DateField(new (Date as any)(...dateArgs)); -}); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts deleted file mode 100644 index a1e1e11b1..000000000 --- a/src/new_fields/Doc.ts +++ /dev/null @@ -1,1109 +0,0 @@ -import { action, computed, observable, ObservableMap, runInAction } from "mobx"; -import { computedFn } from "mobx-utils"; -import { alias, map, serializable } from "serializr"; -import { DocServer } from "../client/DocServer"; -import { DocumentType } from "../client/documents/DocumentTypes"; -import { Scripting, scriptingGlobal } from "../client/util/Scripting"; -import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper"; -import { UndoManager } from "../client/util/UndoManager"; -import { intersectRect, Utils } from "../Utils"; -import { HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update, Copy } from "./FieldSymbols"; -import { List } from "./List"; -import { ObjectField } from "./ObjectField"; -import { PrefetchProxy, ProxyField } from "./Proxy"; -import { FieldId, RefField } from "./RefField"; -import { RichTextField } from "./RichTextField"; -import { listSpec } from "./Schema"; -import { ComputedField, ScriptField } from "./ScriptField"; -import { Cast, FieldValue, NumCast, StrCast, ToConstructor, ScriptCast } from "./Types"; -import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util"; -import { Docs, DocumentOptions } from "../client/documents/Documents"; -import { PdfField, VideoField, AudioField, ImageField } from "./URLField"; -import { LinkManager } from "../client/util/LinkManager"; - -export namespace Field { - export function toKeyValueString(doc: Doc, key: string): string { - const onDelegate = Object.keys(doc).includes(key); - - const field = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - if (Field.IsField(field)) { - return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field)); - } - return ""; - } - export function toScriptString(field: Field): string { - if (typeof field === "string") { - return `"${field}"`; - } else if (typeof field === "number" || typeof field === "boolean") { - return String(field); - } else { - return field[ToScriptString](); - } - } - export function toString(field: Field): string { - if (typeof field === "string") { - return field; - } else if (typeof field === "number" || typeof field === "boolean") { - return String(field); - } else if (field instanceof ObjectField) { - return field[ToString](); - } else if (field instanceof RefField) { - return field[ToString](); - } - return ""; - } - export function IsField(field: any): field is Field; - export function IsField(field: any, includeUndefined: true): field is Field | undefined; - export function IsField(field: any, includeUndefined: boolean = false): field is Field | undefined { - return (typeof field === "string") - || (typeof field === "number") - || (typeof field === "boolean") - || (field instanceof ObjectField) - || (field instanceof RefField) - || (includeUndefined && field === undefined); - } -} -export type Field = number | string | boolean | ObjectField | RefField; -export type Opt = T | undefined; -export type FieldWaiting = T extends undefined ? never : Promise; -export type FieldResult = Opt | FieldWaiting>; - -/** - * Cast any field to either a List of Docs or undefined if the given field isn't a List of Docs. - * If a default value is given, that will be returned instead of undefined. - * If a default value is given, the returned value should not be modified as it might be a temporary value. - * If no default value is given, and the returned value is not undefined, it can be safely modified. - */ -export function DocListCastAsync(field: FieldResult): Promise; -export function DocListCastAsync(field: FieldResult, defaultValue: Doc[]): Promise; -export function DocListCastAsync(field: FieldResult, defaultValue?: Doc[]) { - const list = Cast(field, listSpec(Doc)); - return list ? Promise.all(list).then(() => list) : Promise.resolve(defaultValue); -} - -export async function DocCastAsync(field: FieldResult): Promise> { - return Cast(field, Doc); -} - -export function DocListCast(field: FieldResult): Doc[] { - return Cast(field, listSpec(Doc), []).filter(d => d instanceof Doc) as Doc[]; -} - -export const WidthSym = Symbol("Width"); -export const HeightSym = Symbol("Height"); -export const DataSym = Symbol("Data"); -export const LayoutSym = Symbol("Layout"); -export const UpdatingFromServer = Symbol("UpdatingFromServer"); -const CachedUpdates = Symbol("Cached updates"); - - -function fetchProto(doc: Doc) { - const proto = doc.proto; - if (proto instanceof Promise) { - return proto; - } -} - -@scriptingGlobal -@Deserializable("Doc", fetchProto).withFields(["id"]) -export class Doc extends RefField { - constructor(id?: FieldId, forceSave?: boolean) { - super(id); - const doc = new Proxy(this, { - set: setter, - get: getter, - // getPrototypeOf: (target) => Cast(target[SelfProxy].proto, Doc) || null, // TODO this might be able to replace the proto logic in getter - has: (target, key) => key in target.__fields, - ownKeys: target => { - const obj = {} as any; - Object.assign(obj, target.___fields); - runInAction(() => obj.__LAYOUT__ = target.__LAYOUT__); - return Object.keys(obj); - }, - getOwnPropertyDescriptor: (target, prop) => { - if (prop.toString() === "__LAYOUT__") { - return Reflect.getOwnPropertyDescriptor(target, prop); - } - if (prop in target.__fields) { - return { - configurable: true,//TODO Should configurable be true? - enumerable: true, - value: target.__fields[prop] - }; - } - return Reflect.getOwnPropertyDescriptor(target, prop); - }, - deleteProperty: deleteProperty, - defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, - }); - this[SelfProxy] = doc; - if (!id || forceSave) { - DocServer.CreateField(doc); - } - return doc; - } - - proto: Opt; - [key: string]: FieldResult; - - @serializable(alias("fields", map(autoObject(), { afterDeserialize: afterDocDeserialize }))) - private get __fields() { return this.___fields; } - private set __fields(value) { - this.___fields = value; - for (const key in value) { - const field = value[key]; - if (!(field instanceof ObjectField)) continue; - field[Parent] = this[Self]; - field[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); - } - } - - @observable - //{ [key: string]: Field | FieldWaiting | undefined } - private ___fields: any = {}; - - private [UpdatingFromServer]: boolean = false; - - private [Update] = (diff: any) => { - !this[UpdatingFromServer] && DocServer.UpdateField(this[Id], diff); - } - - private [Self] = this; - private [SelfProxy]: any; - public [WidthSym] = () => NumCast(this[SelfProxy]._width); - public [HeightSym] = () => NumCast(this[SelfProxy]._height); - public get [LayoutSym]() { return this[SelfProxy].__LAYOUT__; } - public get [DataSym]() { - const self = this[SelfProxy]; - return self.resolvedDataDoc && !self.isTemplateForField ? self : - Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self); - } - @computed get __LAYOUT__() { - const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null); - if (templateLayoutDoc) { - let renderFieldKey: any; - const layoutField = templateLayoutDoc[StrCast(templateLayoutDoc.layoutKey, "layout")]; - if (typeof layoutField === "string") { - renderFieldKey = layoutField.split("fieldKey={'")[1].split("'")[0];//layoutField.split("'")[1]; - } else { - return Cast(layoutField, Doc, null); - } - return Cast(this[SelfProxy][renderFieldKey + "-layout[" + templateLayoutDoc[Id] + "]"], Doc, null) || templateLayoutDoc; - } - return undefined; - } - - [ToScriptString]() { return `DOC-"${this[Self][Id]}"-`; } - [ToString]() { return `Doc(${this.title})`; } - - private [CachedUpdates]: { [key: string]: () => void | Promise } = {}; - public static CurrentUserEmail: string = ""; - public async [HandleUpdate](diff: any) { - const set = diff.$set; - const sameAuthor = this.author === Doc.CurrentUserEmail; - if (set) { - for (const key in set) { - if (!key.startsWith("fields.")) { - continue; - } - const fKey = key.substring(7); - const fn = async () => { - const value = await SerializationHelper.Deserialize(set[key]); - this[UpdatingFromServer] = true; - this[fKey] = value; - this[UpdatingFromServer] = false; - }; - if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { - delete this[CachedUpdates][fKey]; - await fn(); - } else { - this[CachedUpdates][fKey] = fn; - } - } - } - const unset = diff.$unset; - if (unset) { - for (const key in unset) { - if (!key.startsWith("fields.")) { - continue; - } - const fKey = key.substring(7); - const fn = () => { - this[UpdatingFromServer] = true; - delete this[fKey]; - this[UpdatingFromServer] = false; - }; - if (sameAuthor || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { - delete this[CachedUpdates][fKey]; - await fn(); - } else { - this[CachedUpdates][fKey] = fn; - } - } - } - } -} - -export namespace Doc { - // export function GetAsync(doc: Doc, key: string, ignoreProto: boolean = false): Promise { - // const self = doc[Self]; - // return new Promise(res => getField(self, key, ignoreProto, res)); - // } - // export function GetTAsync(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): Promise { - // return new Promise(async res => { - // const field = await GetAsync(doc, key, ignoreProto); - // return Cast(field, ctor); - // }); - // } - - export function RunCachedUpdate(doc: Doc, field: string) { - const update = doc[CachedUpdates][field]; - if (update) { - update(); - delete doc[CachedUpdates][field]; - } - } - export function AddCachedUpdate(doc: Doc, field: string, oldValue: any) { - const val = oldValue; - doc[CachedUpdates][field] = () => { - doc[UpdatingFromServer] = true; - doc[field] = val; - doc[UpdatingFromServer] = false; - }; - } - export function MakeReadOnly(): { end(): void } { - makeReadOnly(); - return { - end() { - makeEditable(); - } - }; - } - - export function Get(doc: Doc, key: string, ignoreProto: boolean = false): FieldResult { - try { - return getField(doc[Self], key, ignoreProto); - } catch { - return doc; - } - } - export function GetT(doc: Doc, key: string, ctor: ToConstructor, ignoreProto: boolean = false): FieldResult { - return Cast(Get(doc, key, ignoreProto), ctor) as FieldResult; - } - export function IsPrototype(doc: Doc) { - return GetT(doc, "isPrototype", "boolean", true); - } - export function IsBaseProto(doc: Doc) { - return GetT(doc, "baseProto", "boolean", true); - } - export async function SetInPlace(doc: Doc, key: string, value: Field | undefined, defaultProto: boolean) { - const hasProto = doc.proto instanceof Doc; - const onDeleg = Object.getOwnPropertyNames(doc).indexOf(key) !== -1; - const onProto = hasProto && Object.getOwnPropertyNames(doc.proto).indexOf(key) !== -1; - if (onDeleg || !hasProto || (!onProto && !defaultProto)) { - doc[key] = value; - } else doc.proto![key] = value; - } - export async function SetOnPrototype(doc: Doc, key: string, value: Field) { - const proto = Object.getOwnPropertyNames(doc).indexOf("isPrototype") === -1 ? doc.proto : doc; - - if (proto) { - proto[key] = value; - } - } - export function GetAllPrototypes(doc: Doc): Doc[] { - const protos: Doc[] = []; - let d: Opt = doc; - while (d) { - protos.push(d); - d = FieldValue(d.proto); - } - return protos; - } - - /** - * This function is intended to model Object.assign({}, {}) [https://mzl.la/1Mo3l21], which copies - * the values of the properties of a source object into the target. - * - * This is just a specific, Dash-authored version that serves the same role for our - * Doc class. - * - * @param doc the target document into which you'd like to insert the new fields - * @param fields the fields to project onto the target. Its type signature defines a mapping from some string key - * to a potentially undefined field, where each entry in this mapping is optional. - */ - export function assign(doc: Doc, fields: Partial>>, skipUndefineds: boolean = false) { - for (const key in fields) { - if (fields.hasOwnProperty(key)) { - const value = fields[key]; - if (!skipUndefineds || value !== undefined) { // Do we want to filter out undefineds? - doc[key] = value; - } - } - } - return doc; - } - - // compare whether documents or their protos match - export function AreProtosEqual(doc?: Doc, other?: Doc) { - if (!doc || !other) return false; - const r = (doc === other); - const r2 = (Doc.GetProto(doc) === other); - const r3 = (Doc.GetProto(other) === doc); - const r4 = (Doc.GetProto(doc) === Doc.GetProto(other) && Doc.GetProto(other) !== undefined); - return r || r2 || r3 || r4; - } - - // Gets the data document for the document. Note: this is mis-named -- it does not specifically - // return the doc's proto, but rather recursively searches through the proto inheritance chain - // and returns the document who's proto is undefined or whose proto is marked as a base prototype ('isPrototype'). - export function GetProto(doc: Doc): Doc { - if (doc instanceof Promise) { - console.log("GetProto: error: got Promise insead of Doc"); - } - const proto = doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc)); - return proto === doc ? proto : Doc.GetProto(proto); - } - export function GetDataDoc(doc: Doc): Doc { - const proto = Doc.GetProto(doc); - return proto === doc ? proto : Doc.GetDataDoc(proto); - } - - export function allKeys(doc: Doc): string[] { - const results: Set = new Set; - - let proto: Doc | undefined = doc; - while (proto) { - Object.keys(proto).forEach(key => results.add(key)); - proto = proto.proto; - } - - return Array.from(results); - } - - export function IndexOf(toFind: Doc, list: Doc[], allowProtos: boolean = true) { - let index = list.reduce((p, v, i) => (v instanceof Doc && v === toFind) ? i : p, -1); - index = allowProtos && index !== -1 ? index : list.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, toFind)) ? i : p, -1); - return index; // list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); - } - export function RemoveDocFromList(listDoc: Doc, fieldKey: string | undefined, doc: Doc) { - const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc); - if (listDoc[key] === undefined) { - Doc.GetProto(listDoc)[key] = new List(); - } - const list = Cast(listDoc[key], listSpec(Doc)); - if (list) { - const ind = list.indexOf(doc); - if (ind !== -1) { - list.splice(ind, 1); - return true; - } - } - return false; - } - export function AddDocToList(listDoc: Doc, fieldKey: string | undefined, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) { - const key = fieldKey ? fieldKey : Doc.LayoutFieldKey(listDoc); - if (listDoc[key] === undefined) { - Doc.GetProto(listDoc)[key] = new List(); - } - const list = Cast(listDoc[key], listSpec(Doc)); - if (list) { - if (allowDuplicates !== true) { - const pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1); - if (pind !== -1) { - list.splice(pind, 1); - } - } - if (first) { - list.splice(0, 0, doc); - } - else { - const ind = relativeTo ? list.indexOf(relativeTo) : -1; - if (ind === -1) { - if (reversed) list.splice(0, 0, doc); - else list.push(doc); - } - else { - if (reversed) list.splice(before ? (list.length - ind) + 1 : list.length - ind, 0, doc); - else list.splice(before ? ind : ind + 1, 0, doc); - } - } - return true; - } - return false; - } - - // - // Computes the bounds of the contents of a set of documents. - // - export function ComputeContentBounds(docList: Doc[]) { - const bounds = docList.reduce((bounds, doc) => { - const [sptX, sptY] = [NumCast(doc.x), NumCast(doc.y)]; - const [bptX, bptY] = [sptX + doc[WidthSym](), sptY + doc[HeightSym]()]; - return { - x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), - r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) - }; - }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }); - return bounds; - } - - export function MakeAlias(doc: Doc, id?: string) { - const alias = !GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeCopy(doc, undefined, id) : Doc.MakeDelegate(doc, id); - const layout = Doc.LayoutField(alias); - if (layout instanceof Doc && layout !== alias && layout === Doc.Layout(alias)) { - Doc.SetLayout(alias, Doc.MakeAlias(layout)); - } - alias.aliasOf = doc; - alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`); - return alias; - } - - // - // Determines whether the layout needs to be expanded (as a template). - // template expansion is rquired when the layout is a template doc/field and there's a datadoc which isn't equal to the layout template - // - export function WillExpandTemplateLayout(layoutDoc: Doc, dataDoc?: Doc) { - return (layoutDoc.isTemplateForField || layoutDoc.isTemplateDoc) && dataDoc && layoutDoc !== dataDoc; - } - - const _pendingMap: Map = new Map(); - // - // Returns an expanded template layout for a target data document if there is a template relationship - // between the two. If so, the layoutDoc is expanded into a new document that inherits the properties - // of the original layout while allowing for individual layout properties to be overridden in the expanded layout. - // templateArgs should be equivalent to the layout key that generates the template since that's where the template parameters are stored in ()'s at the end of the key. - // NOTE: the template will have references to "@params" -- the template arguments will be assigned to the '@params' field - // so that when the @params key is accessed, it will be rewritten as the key that is stored in the 'params' field and - // the derefence will then occur on the rootDocument (the original document). - // in the future, field references could be written as @ and then arguments would be passed in the layout key as: - // layout_mytemplate(somparam=somearg). - // then any references to @someparam would be rewritten as accesses to 'somearg' on the rootDocument - export function expandTemplateLayout(templateLayoutDoc: Doc, targetDoc?: Doc, templateArgs?: string) { - const args = templateArgs?.match(/\(([a-zA-Z0-9._\-]*)\)/)?.[1].replace("()", "") || StrCast(templateLayoutDoc.PARAMS); - if (!args && !WillExpandTemplateLayout(templateLayoutDoc, targetDoc) || !targetDoc) return templateLayoutDoc; - - const templateField = StrCast(templateLayoutDoc.isTemplateForField); // the field that the template renders - // First it checks if an expanded layout already exists -- if so it will be stored on the dataDoc - // using the template layout doc's id as the field key. - // If it doesn't find the expanded layout, then it makes a delegate of the template layout and - // saves it on the data doc indexed by the template layout's id. - // - const params = args.split("=").length > 1 ? args.split("=")[0] : "PARAMS"; - const layoutFielddKey = Doc.LayoutFieldKey(templateLayoutDoc); - const expandedLayoutFieldKey = (templateField || layoutFielddKey) + "-layout[" + templateLayoutDoc[Id] + (args ? `(${args})` : "") + "]"; - let expandedTemplateLayout = targetDoc?.[expandedLayoutFieldKey]; - - if (templateLayoutDoc.resolvedDataDoc instanceof Promise) { - expandedTemplateLayout = undefined; - _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey, true); - } - else if (expandedTemplateLayout === undefined && !_pendingMap.get(targetDoc[Id] + expandedLayoutFieldKey + args)) { - if (templateLayoutDoc.resolvedDataDoc === (targetDoc.rootDocument || Doc.GetProto(targetDoc)) && templateLayoutDoc.PARAMS === StrCast(targetDoc.PARAMS)) { - expandedTemplateLayout = templateLayoutDoc; // reuse an existing template layout if its for the same document with the same params - } else { - _pendingMap.set(targetDoc[Id] + expandedLayoutFieldKey + args, true); - templateLayoutDoc.resolvedDataDoc && (templateLayoutDoc = Cast(templateLayoutDoc.proto, Doc, null) || templateLayoutDoc); // if the template has already been applied (ie, a nested template), then use the template's prototype - setTimeout(action(() => { - if (!targetDoc[expandedLayoutFieldKey]) { - const newLayoutDoc = Doc.MakeDelegate(templateLayoutDoc, undefined, "[" + templateLayoutDoc.title + "]"); - // the template's arguments are stored in params which is derefenced to find - // the actual field key where the parameterized template data is stored. - newLayoutDoc[params] = args !== "..." ? args : ""; // ... signifies the layout has sub template(s) -- so we have to expand the layout for them so that they can get the correct 'rootDocument' field, but we don't need to reassign their params. it would be better if the 'rootDocument' field could be passed dynamically to avoid have to create instances - newLayoutDoc.rootDocument = targetDoc; - targetDoc[expandedLayoutFieldKey] = newLayoutDoc; - const dataDoc = Doc.GetProto(targetDoc); - newLayoutDoc.resolvedDataDoc = dataDoc; - if (dataDoc[templateField] === undefined && templateLayoutDoc[templateField] instanceof List) { - dataDoc[templateField] = ComputedField.MakeFunction(`ObjectField.MakeCopy(templateLayoutDoc["${templateField}"] as List)`, { templateLayoutDoc: Doc.name }, { templateLayoutDoc }); - } - _pendingMap.delete(targetDoc[Id] + expandedLayoutFieldKey + args); - } - }), 0); - } - } - return expandedTemplateLayout instanceof Doc ? expandedTemplateLayout : undefined; // layout is undefined if the expandedTemplateLayout is pending. - } - - // if the childDoc is a template for a field, then this will return the expanded layout with its data doc. - // otherwise, it just returns the childDoc - export function GetLayoutDataDocPair(containerDoc: Doc, containerDataDoc: Opt, childDoc: Doc) { - if (!childDoc || childDoc instanceof Promise || !Doc.GetProto(childDoc)) { - console.log("No, no, no!"); - return { layout: childDoc, data: childDoc }; - } - const resolvedDataDoc = (Doc.AreProtosEqual(containerDataDoc, containerDoc) || (!childDoc.isTemplateDoc && !childDoc.isTemplateForField && !childDoc.PARAMS) ? undefined : containerDataDoc); - return { layout: Doc.expandTemplateLayout(childDoc, resolvedDataDoc, "(" + StrCast(containerDoc.PARAMS) + ")"), data: resolvedDataDoc }; - } - - export function Overwrite(doc: Doc, overwrite: Doc, copyProto: boolean = false): Doc { - Object.keys(doc).forEach(key => { - const field = ProxyField.WithoutProxy(() => doc[key]); - if (key === "proto" && copyProto) { - if (doc.proto instanceof Doc && overwrite.proto instanceof Doc) { - overwrite[key] = Doc.Overwrite(doc[key]!, overwrite.proto); - } - } else { - if (field instanceof RefField) { - overwrite[key] = field; - } else if (field instanceof ObjectField) { - overwrite[key] = ObjectField.MakeCopy(field); - } else if (field instanceof Promise) { - debugger; //This shouldn't happend... - } else { - overwrite[key] = field; - } - } - }); - - return overwrite; - } - - export function MakeCopy(doc: Doc, copyProto: boolean = false, copyProtoId?: string): Doc { - const copy = new Doc(copyProtoId, true); - const exclude = Cast(doc.excludeFields, listSpec("string"), []); - Object.keys(doc).forEach(key => { - if (exclude.includes(key)) return; - const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - const field = ProxyField.WithoutProxy(() => doc[key]); - if (key === "proto" && copyProto) { - if (doc[key] instanceof Doc) { - copy[key] = Doc.MakeCopy(doc[key]!, false); - } - } else { - if (field instanceof RefField) { - copy[key] = field; - } else if (cfield instanceof ComputedField) { - copy[key] = ComputedField.MakeFunction(cfield.script.originalScript); - } else if (field instanceof ObjectField) { - copy[key] = doc[key] instanceof Doc ? - key.includes("layout[") ? Doc.MakeCopy(doc[key] as Doc, false) : doc[key] : // reference documents except copy documents that are expanded teplate fields - ObjectField.MakeCopy(field); - } else if (field instanceof Promise) { - debugger; //This shouldn't happend... - } else { - copy[key] = field; - } - } - }); - - return copy; - } - - export function MakeClone(doc: Doc): Doc { - const cloneMap = new Map(); - const rtfMap: { copy: Doc, key: string, field: RichTextField }[] = []; - const copy = Doc.makeClone(doc, cloneMap, rtfMap); - rtfMap.map(({ copy, key, field }) => { - const replacer = (match: any, attr: string, id: string, offset: any, string: any) => { - const mapped = cloneMap.get(id); - return attr + "\"" + (mapped ? mapped[Id] : id) + "\""; - }; - const replacer2 = (match: any, href: string, id: string, offset: any, string: any) => { - const mapped = cloneMap.get(id); - return href + (mapped ? mapped[Id] : id); - }; - const regex = `(${Utils.prepend("/doc/")})([^"]*)`; - const re = new RegExp(regex, "g"); - copy[key] = new RichTextField(field.Data.replace(/("docid":|"targetId":|"linkId":)"([^"]+)"/g, replacer).replace(re, replacer2), field.Text); - }); - return copy; - } - - export function makeClone(doc: Doc, cloneMap: Map, rtfs: { copy: Doc, key: string, field: RichTextField }[]): Doc { - if (Doc.IsBaseProto(doc)) return doc; - if (cloneMap.get(doc[Id])) return cloneMap.get(doc[Id])!; - const copy = new Doc(undefined, true); - cloneMap.set(doc[Id], copy); - if (LinkManager.Instance.getAllLinks().includes(doc) && LinkManager.Instance.getAllLinks().indexOf(copy) === -1) LinkManager.Instance.addLink(copy); - const exclude = Cast(doc.excludeFields, listSpec("string"), []); - Object.keys(doc).forEach(key => { - if (exclude.includes(key)) return; - const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - const field = ProxyField.WithoutProxy(() => doc[key]); - const copyObjectField = (field: ObjectField) => { - const list = Cast(doc[key], listSpec(Doc)); - if (list !== undefined && !(list instanceof Promise)) { - copy[key] = new List(list.filter(d => d instanceof Doc).map(d => Doc.makeClone(d as Doc, cloneMap, rtfs))); - } else if (doc[key] instanceof Doc) { - copy[key] = key.includes("layout[") ? undefined : Doc.makeClone(doc[key] as Doc, cloneMap, rtfs); // reference documents except copy documents that are expanded teplate fields - } else { - copy[key] = ObjectField.MakeCopy(field); - if (field instanceof RichTextField) { - if (field.Data.includes('"docid":') || field.Data.includes('"targetId":') || field.Data.includes('"linkId":')) { - rtfs.push({ copy, key, field }); - } - } - } - }; - if (key === "proto") { - if (doc[key] instanceof Doc) { - copy[key] = Doc.makeClone(doc[key]!, cloneMap, rtfs); - } - } else { - if (field instanceof RefField) { - copy[key] = field; - } else if (cfield instanceof ComputedField) { - copy[key] = ComputedField.MakeFunction(cfield.script.originalScript); - (key === "links" && field instanceof ObjectField) && copyObjectField(field); - } else if (field instanceof ObjectField) { - copyObjectField(field); - } else if (field instanceof Promise) { - debugger; //This shouldn't happend... - } else { - copy[key] = field; - } - } - }); - Doc.SetInPlace(copy, "title", "CLONE: " + doc.title, true); - copy.cloneOf = doc; - cloneMap.set(doc[Id], copy); - return copy; - } - - export function MakeDelegate(doc: Doc, id?: string, title?: string): Doc; - export function MakeDelegate(doc: Opt, id?: string, title?: string): Opt; - export function MakeDelegate(doc: Opt, id?: string, title?: string): Opt { - if (doc) { - const delegate = new Doc(id, true); - delegate.proto = doc; - title && (delegate.title = title); - return delegate; - } - return undefined; - } - - let _applyCount: number = 0; - export function ApplyTemplate(templateDoc: Doc) { - if (templateDoc) { - const target = Doc.MakeDelegate(new Doc()); - const targetKey = StrCast(templateDoc.layoutKey, "layout"); - const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")"); - target.layoutKey = targetKey; - applied && (Doc.GetProto(applied).type = templateDoc.type); - return applied; - } - return undefined; - } - export function ApplyTemplateTo(templateDoc: Doc, target: Doc, targetKey: string, titleTarget: string | undefined) { - if (!Doc.AreProtosEqual(target[targetKey] as Doc, templateDoc)) { - if (target.resolvedDataDoc) { - target[targetKey] = new PrefetchProxy(templateDoc); - } else { - titleTarget && (Doc.GetProto(target).title = titleTarget); - Doc.GetProto(target)[targetKey] = new PrefetchProxy(templateDoc); - } - } - return target; - } - - // - // This function converts a generic field layout display into a field layout that displays a specific - // metadata field indicated by the title of the template field (not the default field that it was rendering) - // - export function MakeMetadataFieldTemplate(templateField: Doc, templateDoc: Opt): boolean { - - // find the metadata field key that this template field doc will display (indicated by its title) - const metadataFieldKey = StrCast(templateField.isTemplateForField) || StrCast(templateField.title).replace(/^-/, ""); - - // update the original template to mark it as a template - templateField.isTemplateForField = metadataFieldKey; - templateField.title = metadataFieldKey; - - const templateFieldValue = templateField[metadataFieldKey] || templateField[Doc.LayoutFieldKey(templateField)]; - const templateCaptionValue = templateField.caption; - // move any data that the template field had been rendering over to the template doc so that things will still be rendered - // when the template field is adjusted to point to the new metadatafield key. - // note 1: if the template field contained a list of documents, each of those documents will be converted to templates as well. - // note 2: this will not overwrite any field that already exists on the template doc at the field key - if (!templateDoc?.[metadataFieldKey] && templateFieldValue instanceof ObjectField) { - Cast(templateFieldValue, listSpec(Doc), [])?.map(d => d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc)); - (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateFieldValue)); - } - // copy the textTemplates from 'this' (not 'self') because the layout contains the template info, not the original doc - if (templateCaptionValue instanceof RichTextField && !templateCaptionValue.Empty()) { - templateField["caption-textTemplate"] = ComputedField.MakeFunction(`copyField(this.caption)`); - } - if (templateFieldValue instanceof RichTextField && !templateFieldValue.Empty()) { - templateField[metadataFieldKey + "-textTemplate"] = ComputedField.MakeFunction(`copyField(this.${metadataFieldKey})`); - } - - // get the layout string that the template uses to specify its layout - const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField))); - - // change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document. - Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`); - - // assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino) - Doc.Layout(templateField)[metadataFieldKey + "_ext"] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + "_ext"] as Doc); - - return true; - } - - export function overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) { - const doc2Layout = Doc.Layout(doc2); - const doc1Layout = Doc.Layout(doc1); - const x2 = NumCast(doc2.x) - clusterDistance; - const y2 = NumCast(doc2.y) - clusterDistance; - const w2 = NumCast(doc2Layout._width) + clusterDistance; - const h2 = NumCast(doc2Layout._height) + clusterDistance; - const x = NumCast(doc1.x) - clusterDistance; - const y = NumCast(doc1.y) - clusterDistance; - const w = NumCast(doc1Layout._width) + clusterDistance; - const h = NumCast(doc1Layout._height) + clusterDistance; - return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 }); - } - - export function isBrushedHighlightedDegree(doc: Doc) { - if (Doc.IsHighlighted(doc)) { - return 6; - } - else { - return Doc.IsBrushedDegree(doc); - } - } - - export class DocBrush { - BrushedDoc: ObservableMap = new ObservableMap(); - } - const brushManager = new DocBrush(); - - export class DocData { - @observable _user_doc: Doc = undefined!; - @observable _searchQuery: string = ""; - } - - // the document containing the view layout information - will be the Document itself unless the Document has - // a layout field or 'layout' is given. - export function Layout(doc: Doc, layout?: Doc): Doc { - const overrideLayout = layout && Cast(doc[`${StrCast(layout.isTemplateForField, "data")}-layout[` + layout[Id] + "]"], Doc, null); - return overrideLayout || doc[LayoutSym] || doc; - } - export function SetLayout(doc: Doc, layout: Doc | string) { doc[StrCast(doc.layoutKey, "layout")] = layout; } - export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; } - export function LayoutFieldKey(doc: Doc): string { return StrCast(Doc.Layout(doc).layout).split("'")[1]; } - const manager = new DocData(); - export function SearchQuery(): string { return manager._searchQuery; } - export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); } - export function UserDoc(): Doc { return manager._user_doc; } - export function SetUserDoc(doc: Doc) { manager._user_doc = doc; } - export function IsBrushed(doc: Doc) { - return computedFn(function IsBrushed(doc: Doc) { - return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc)); - })(doc); - } - // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message) - export function IsBrushedDegreeUnmemoized(doc: Doc) { - return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? 1 : 0; - } - export function IsBrushedDegree(doc: Doc) { - return computedFn(function IsBrushDegree(doc: Doc) { - return Doc.IsBrushedDegreeUnmemoized(doc); - })(doc); - } - export function BrushDoc(doc: Doc) { - brushManager.BrushedDoc.set(doc, true); - brushManager.BrushedDoc.set(Doc.GetProto(doc), true); - return doc; - } - export function UnBrushDoc(doc: Doc) { - brushManager.BrushedDoc.delete(doc); - brushManager.BrushedDoc.delete(Doc.GetProto(doc)); - return doc; - } - - - export function LinkOtherAnchor(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? Cast(linkDoc.anchor2, Doc) as Doc : Cast(linkDoc.anchor1, Doc) as Doc; } - export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "1" : "2"; } - - export function linkFollowUnhighlight() { - Doc.UnhighlightAll(); - document.removeEventListener("pointerdown", linkFollowUnhighlight); - } - - let _lastDate = 0; - export function linkFollowHighlight(destDoc: Doc, dataAndDisplayDocs = true) { - linkFollowUnhighlight(); - Doc.HighlightDoc(destDoc, dataAndDisplayDocs); - document.removeEventListener("pointerdown", linkFollowUnhighlight); - document.addEventListener("pointerdown", linkFollowUnhighlight); - const lastDate = _lastDate = Date.now(); - window.setTimeout(() => _lastDate === lastDate && linkFollowUnhighlight(), 5000); - } - - export class HighlightBrush { - @observable HighlightedDoc: Map = new Map(); - } - const highlightManager = new HighlightBrush(); - export function IsHighlighted(doc: Doc) { - return highlightManager.HighlightedDoc.get(doc) || highlightManager.HighlightedDoc.get(Doc.GetProto(doc)); - } - export function HighlightDoc(doc: Doc, dataAndDisplayDocs = true) { - runInAction(() => { - highlightManager.HighlightedDoc.set(doc, true); - dataAndDisplayDocs && highlightManager.HighlightedDoc.set(Doc.GetProto(doc), true); - }); - } - export function UnHighlightDoc(doc: Doc) { - runInAction(() => { - highlightManager.HighlightedDoc.set(doc, false); - highlightManager.HighlightedDoc.set(Doc.GetProto(doc), false); - }); - } - export function UnhighlightAll() { - const mapEntries = highlightManager.HighlightedDoc.keys(); - let docEntry: IteratorResult; - while (!(docEntry = mapEntries.next()).done) { - const targetDoc = docEntry.value; - targetDoc && Doc.UnHighlightDoc(targetDoc); - } - - } - export function UnBrushAllDocs() { - brushManager.BrushedDoc.clear(); - } - - export function getDocTemplate(doc?: Doc) { - return doc?.isTemplateDoc ? doc : - Cast(doc?.dragFactory, Doc, null)?.isTemplateDoc ? doc?.dragFactory : - Cast(doc?.layout, Doc, null)?.isTemplateDoc ? doc?.layout : undefined; - } - - export function matchFieldValue(doc: Doc, key: string, value: any): boolean { - const fieldVal = doc[key]; - if (Cast(fieldVal, listSpec("string"), []).length) { - const vals = Cast(fieldVal, listSpec("string"), []); - return vals.some(v => v === value); - } - const fieldStr = Field.toString(fieldVal as Field); - return fieldStr === value; - } - - export function deiconifyView(doc: any) { - StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc); - } - - export function setNativeView(doc: any) { - const prevLayout = StrCast(doc.layoutKey).split("_")[1]; - const deiconify = prevLayout === "icon" && StrCast(doc.deiconifyLayout) ? "layout_" + StrCast(doc.deiconifyLayout) : ""; - prevLayout === "icon" && (doc.deiconifyLayout = undefined); - doc.layoutKey = deiconify || "layout"; - } - export function setDocFilterRange(target: Doc, key: string, range?: number[]) { - const docRangeFilters = Cast(target._docRangeFilters, listSpec("string"), []); - for (let i = 0; i < docRangeFilters.length; i += 3) { - if (docRangeFilters[i] === key) { - docRangeFilters.splice(i, 3); - break; - } - } - if (range !== undefined) { - docRangeFilters.push(key); - docRangeFilters.push(range[0].toString()); - docRangeFilters.push(range[1].toString()); - target._docRangeFilters = new List(docRangeFilters); - } - } - - export function aliasDocs(field: any) { - return new List(field.map((d: any) => Doc.MakeAlias(d))); - } - - // filters document in a container collection: - // all documents with the specified value for the specified key are included/excluded - // based on the modifiers :"check", "x", undefined - export function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { - const docFilters = Cast(container._docFilters, listSpec("string"), []); - for (let i = 0; i < docFilters.length; i += 3) { - if (docFilters[i] === key && docFilters[i + 1] === value) { - docFilters.splice(i, 3); - break; - } - } - if (typeof modifiers === "string") { - docFilters.push(key); - docFilters.push(value); - docFilters.push(modifiers); - container._docFilters = new List(docFilters); - } - } - export function readDocRangeFilter(doc: Doc, key: string) { - const docRangeFilters = Cast(doc._docRangeFilters, listSpec("string"), []); - for (let i = 0; i < docRangeFilters.length; i += 3) { - if (docRangeFilters[i] === key) { - return [Number(docRangeFilters[i + 1]), Number(docRangeFilters[i + 2])]; - } - } - } - export function assignDocToField(doc: Doc, field: string, id: string) { - DocServer.GetRefField(id).then(layout => layout instanceof Doc && (doc[field] = layout)); - return id; - } - - export function toggleNativeDimensions(layoutDoc: Doc, contentScale: number, panelWidth: number, panelHeight: number) { - runInAction(() => { - if (layoutDoc._nativeWidth || layoutDoc._nativeHeight) { - layoutDoc.scale = NumCast(layoutDoc.scale, 1) * contentScale; - layoutDoc._nativeWidth = undefined; - layoutDoc._nativeHeight = undefined; - } - else { - layoutDoc._autoHeight = false; - if (!layoutDoc._nativeWidth) { - layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth); - layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight); - } - } - }); - } - - export function isDocPinned(doc: Doc) { - //add this new doc to props.Document - const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc; - if (curPres) { - return DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1; - } - return false; - } - - // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) - export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { - const batch = UndoManager.StartBatch("makeCustomViewClicked"); - runInAction(() => { - doc.layoutKey = "layout_" + templateSignature; - if (doc[doc.layoutKey] === undefined) { - createCustomView(doc, creator, templateSignature, docLayoutTemplate); - } - }); - batch.end(); - } - export function findTemplate(templateName: string, type: string, signature: string) { - let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data); - const templBtns = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data); - const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data); - const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data); - const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc); - // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized - // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc)); - !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc)); - return docLayoutTemplate; - } - export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) { - const templateName = templateSignature.replace(/\(.*\)/, ""); - docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.type), templateSignature); - - const customName = "layout_" + templateSignature; - const _width = NumCast(doc._width); - const _height = NumCast(doc._height); - const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false }; - - let fieldTemplate: Opt; - if (doc.data instanceof RichTextField || typeof (doc.data) === "string") { - fieldTemplate = Docs.Create.TextDocument("", options); - } else if (doc.data instanceof PdfField) { - fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options); - } else if (doc.data instanceof VideoField) { - fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options); - } else if (doc.data instanceof AudioField) { - fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options); - } else if (doc.data instanceof ImageField) { - fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options); - } - const docTemplate = docLayoutTemplate || creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); - - fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); - docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); - } - export function makeCustomView(doc: Doc, custom: boolean, layout: string) { - Doc.setNativeView(doc); - if (custom) { - makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined); - } - } - export function iconify(doc: Doc) { - const layoutKey = Cast(doc.layoutKey, "string", null); - Doc.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined); - if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", ""); - } - - export function pileup(selected: Doc[], x: number, y: number) { - const newCollection = Docs.Create.PileDocument(selected, { title: "pileup", x: x - 55, y: y - 55, _width: 110, _height: 100, _LODdisable: true }); - let w = 0, h = 0; - selected.forEach((d, i) => { - Doc.iconify(d); - w = Math.max(d[WidthSym](), w); - h = Math.max(d[HeightSym](), h); - }); - h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away. so this is a fallback hack to try to do something reasonable - selected.forEach((d, i) => { - d.x = Math.cos(Math.PI * 2 * i / selected.length) * 10 - w / 2; - d.y = Math.sin(Math.PI * 2 * i / selected.length) * 10 - h / 2; - d.displayTimecode = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - }); - newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55; - newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55; - newCollection._width = newCollection._height = 110; - //newCollection.borderRounding = "40px"; - newCollection._jitterRotation = 10; - newCollection._backgroundColor = "gray"; - newCollection._overflow = "visible"; - return newCollection; - } - - - export async function addFieldEnumerations(doc: Opt, enumeratedFieldKey: string, enumerations: { title: string, _backgroundColor?: string, color?: string }[]) { - let optionsCollection = await DocServer.GetRefField(enumeratedFieldKey); - if (!(optionsCollection instanceof Doc)) { - optionsCollection = Docs.Create.StackingDocument([], { title: `${enumeratedFieldKey} field set` }, enumeratedFieldKey); - Doc.AddDocToList((Doc.UserDoc().fieldTypes as Doc), "data", optionsCollection as Doc); - } - const options = optionsCollection as Doc; - const targetDoc = doc && Doc.GetProto(Cast(doc.rootDocument, Doc, null) || doc); - const docFind = `options.data.find(doc => doc.title === (this.rootDocument||this)["${enumeratedFieldKey}"])?`; - targetDoc && (targetDoc.backgroundColor = ComputedField.MakeFunction(docFind + `._backgroundColor || "white"`, undefined, { options })); - targetDoc && (targetDoc.color = ComputedField.MakeFunction(docFind + `.color || "black"`, undefined, { options })); - targetDoc && (targetDoc.borderRounding = ComputedField.MakeFunction(docFind + `.borderRounding`, undefined, { options })); - enumerations.map(enumeration => { - const found = DocListCast(options.data).find(d => d.title === enumeration.title); - if (found) { - found._backgroundColor = enumeration._backgroundColor || found._backgroundColor; - found._color = enumeration.color || found._color; - } else { - Doc.AddDocToList(options, "data", Docs.Create.TextDocument(enumeration.title, enumeration)); - } - }); - return optionsCollection; - } -} - -Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; }); -Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); }); -Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); }); -Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); }); -Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); }); -Scripting.addGlobal(function copyField(field: any) { return ObjectField.MakeCopy(field); }); -Scripting.addGlobal(function aliasDocs(field: any) { return Doc.aliasDocs(field); }); -Scripting.addGlobal(function docList(field: any) { return DocListCast(field); }); -Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); }); -Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); }); -Scripting.addGlobal(function deiconifyView(doc: any) { Doc.deiconifyView(doc); }); -Scripting.addGlobal(function undo() { return UndoManager.Undo(); }); -Scripting.addGlobal(function redo() { return UndoManager.Redo(); }); -Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; }); -Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); }); -Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); }); -Scripting.addGlobal(function activePresentationItem() { - const curPres = Doc.UserDoc().activePresentation as Doc; - return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)]; -}); -Scripting.addGlobal(function selectDoc(doc: any) { Doc.UserDoc().activeSelection = new List([doc]); }); -Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { - const docs = DocListCast(Doc.UserDoc().activeSelection). - filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.DOCHOLDER && d.type !== DocumentType.KVP && - (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); - return docs.length ? new List(docs) : prevValue; -}); -Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); -Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); \ No newline at end of file diff --git a/src/new_fields/FieldSymbols.ts b/src/new_fields/FieldSymbols.ts deleted file mode 100644 index 8d040f493..000000000 --- a/src/new_fields/FieldSymbols.ts +++ /dev/null @@ -1,12 +0,0 @@ - -export const Update = Symbol("Update"); -export const Self = Symbol("Self"); -export const SelfProxy = Symbol("SelfProxy"); -export const HandleUpdate = Symbol("HandleUpdate"); -export const Id = Symbol("Id"); -export const OnUpdate = Symbol("OnUpdate"); -export const Parent = Symbol("Parent"); -export const Copy = Symbol("Copy"); -export const ToScriptString = Symbol("ToScriptString"); -export const ToPlainText = Symbol("ToPlainText"); -export const ToString = Symbol("ToString"); diff --git a/src/new_fields/HtmlField.ts b/src/new_fields/HtmlField.ts deleted file mode 100644 index 6e8bba977..000000000 --- a/src/new_fields/HtmlField.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, primitive } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, ToString} from "./FieldSymbols"; - -@Deserializable("html") -export class HtmlField extends ObjectField { - @serializable(primitive()) - readonly html: string; - - constructor(html: string) { - super(); - this.html = html; - } - - [Copy]() { - return new HtmlField(this.html); - } - - [ToScriptString]() { - return "invalid"; - } - [ToString]() { - return this.html; - } -} diff --git a/src/new_fields/IconField.ts b/src/new_fields/IconField.ts deleted file mode 100644 index 76c4ddf1b..000000000 --- a/src/new_fields/IconField.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, primitive } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, ToString } from "./FieldSymbols"; - -@Deserializable("icon") -export class IconField extends ObjectField { - @serializable(primitive()) - readonly icon: string; - - constructor(icon: string) { - super(); - this.icon = icon; - } - - [Copy]() { - return new IconField(this.icon); - } - - [ToScriptString]() { - return "invalid"; - } - [ToString]() { - return "ICONfield"; - } -} diff --git a/src/new_fields/InkField.ts b/src/new_fields/InkField.ts deleted file mode 100644 index bb93de5ac..000000000 --- a/src/new_fields/InkField.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, custom, createSimpleSchema, list, object, map } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, ToString } from "./FieldSymbols"; - -export enum InkTool { - None, - Pen, - Highlighter, - Eraser, - Stamp -} - -export interface PointData { - X: number; - Y: number; -} - -export type InkData = Array; - -const pointSchema = createSimpleSchema({ - X: true, Y: true -}); - -const strokeDataSchema = createSimpleSchema({ - pathData: list(object(pointSchema)), - "*": true -}); - -@Deserializable("ink") -export class InkField extends ObjectField { - @serializable(list(object(strokeDataSchema))) - readonly inkData: InkData; - - constructor(data: InkData) { - super(); - this.inkData = data; - } - - [Copy]() { - return new InkField(this.inkData); - } - - [ToScriptString]() { - return "invalid"; - } - [ToString]() { - return "InkField"; - } -} diff --git a/src/new_fields/List.ts b/src/new_fields/List.ts deleted file mode 100644 index fdabea365..000000000 --- a/src/new_fields/List.ts +++ /dev/null @@ -1,330 +0,0 @@ -import { Deserializable, autoObject, afterDocDeserialize } from "../client/util/SerializationHelper"; -import { Field } from "./Doc"; -import { setter, getter, deleteProperty, updateFunction } from "./util"; -import { serializable, alias, list } from "serializr"; -import { observable, action, runInAction } from "mobx"; -import { ObjectField } from "./ObjectField"; -import { RefField } from "./RefField"; -import { ProxyField } from "./Proxy"; -import { Self, Update, Parent, OnUpdate, SelfProxy, ToScriptString, ToString, Copy, Id } from "./FieldSymbols"; -import { Scripting } from "../client/util/Scripting"; -import { DocServer } from "../client/DocServer"; - -const listHandlers: any = { - /// Mutator methods - copyWithin() { - throw new Error("copyWithin not supported yet"); - }, - fill(value: any, start?: number, end?: number) { - if (value instanceof RefField) { - throw new Error("fill with RefFields not supported yet"); - } - const res = this[Self].__fields.fill(value, start, end); - this[Update](); - return res; - }, - pop(): any { - const field = toRealField(this[Self].__fields.pop()); - this[Update](); - return field; - }, - push: action(function (this: any, ...items: any[]) { - items = items.map(toObjectField); - const list = this[Self]; - const length = list.__fields.length; - for (let i = 0; i < items.length; i++) { - const item = items[i]; - //TODO Error checking to make sure parent doesn't already exist - if (item instanceof ObjectField) { - item[Parent] = list; - item[OnUpdate] = updateFunction(list, i + length, item, this); - } - } - const res = list.__fields.push(...items); - this[Update](); - return res; - }), - reverse() { - const res = this[Self].__fields.reverse(); - this[Update](); - return res; - }, - shift() { - const res = toRealField(this[Self].__fields.shift()); - this[Update](); - return res; - }, - sort(cmpFunc: any) { - this[Self].__realFields(); // coerce retrieving entire array - const res = this[Self].__fields.sort(cmpFunc ? (first: any, second: any) => cmpFunc(toRealField(first), toRealField(second)) : undefined); - this[Update](); - return res; - }, - splice: action(function (this: any, start: number, deleteCount: number, ...items: any[]) { - this[Self].__realFields(); // coerce retrieving entire array - items = items.map(toObjectField); - const list = this[Self]; - for (let i = 0; i < items.length; i++) { - const item = items[i]; - //TODO Error checking to make sure parent doesn't already exist - //TODO Need to change indices of other fields in array - if (item instanceof ObjectField) { - item[Parent] = list; - item[OnUpdate] = updateFunction(list, i + start, item, this); - } - } - const res = list.__fields.splice(start, deleteCount, ...items); - this[Update](); - return res.map(toRealField); - }), - unshift(...items: any[]) { - items = items.map(toObjectField); - const list = this[Self]; - const length = list.__fields.length; - for (let i = 0; i < items.length; i++) { - const item = items[i]; - //TODO Error checking to make sure parent doesn't already exist - //TODO Need to change indices of other fields in array - if (item instanceof ObjectField) { - item[Parent] = list; - item[OnUpdate] = updateFunction(list, i, item, this); - } - } - const res = this[Self].__fields.unshift(...items); - this[Update](); - return res; - - }, - /// Accessor methods - concat: action(function (this: any, ...items: any[]) { - this[Self].__realFields(); - return this[Self].__fields.map(toRealField).concat(...items); - }), - includes(valueToFind: any, fromIndex: number) { - if (valueToFind instanceof RefField) { - return this[Self].__realFields().includes(valueToFind, fromIndex); - } else { - return this[Self].__fields.includes(valueToFind, fromIndex); - } - }, - indexOf(valueToFind: any, fromIndex: number) { - if (valueToFind instanceof RefField) { - return this[Self].__realFields().indexOf(valueToFind, fromIndex); - } else { - return this[Self].__fields.indexOf(valueToFind, fromIndex); - } - }, - join(separator: any) { - this[Self].__realFields(); - return this[Self].__fields.map(toRealField).join(separator); - }, - lastIndexOf(valueToFind: any, fromIndex: number) { - if (valueToFind instanceof RefField) { - return this[Self].__realFields().lastIndexOf(valueToFind, fromIndex); - } else { - return this[Self].__fields.lastIndexOf(valueToFind, fromIndex); - } - }, - slice(begin: number, end: number) { - this[Self].__realFields(); - return this[Self].__fields.slice(begin, end).map(toRealField); - }, - - /// Iteration methods - entries() { - return this[Self].__realFields().entries(); - }, - every(callback: any, thisArg: any) { - return this[Self].__realFields().every(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.every((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - filter(callback: any, thisArg: any) { - return this[Self].__realFields().filter(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.filter((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - find(callback: any, thisArg: any) { - return this[Self].__realFields().find(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.find((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - findIndex(callback: any, thisArg: any) { - return this[Self].__realFields().findIndex(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.findIndex((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - forEach(callback: any, thisArg: any) { - return this[Self].__realFields().forEach(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.forEach((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - map(callback: any, thisArg: any) { - return this[Self].__realFields().map(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.map((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - reduce(callback: any, initialValue: any) { - return this[Self].__realFields().reduce(callback, initialValue); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.reduce((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); - }, - reduceRight(callback: any, initialValue: any) { - return this[Self].__realFields().reduceRight(callback, initialValue); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.reduceRight((acc:any, element:any, index:number, array:any) => callback(acc, toRealField(element), index, array), initialValue); - }, - some(callback: any, thisArg: any) { - return this[Self].__realFields().some(callback, thisArg); - // TODO This is probably more efficient, but technically the callback can take the array, which would mean we would have to map the actual array anyway. - // If we don't want to support the array parameter, we should use this version instead - // return this[Self].__fields.some((element:any, index:number, array:any) => callback(toRealField(element), index, array), thisArg); - }, - values() { - return this[Self].__realFields().values(); - }, - [Symbol.iterator]() { - return this[Self].__realFields().values(); - } -}; - -function toObjectField(field: Field) { - return field instanceof RefField ? new ProxyField(field) : field; -} - -function toRealField(field: Field) { - return field instanceof ProxyField ? field.value() : field; -} - -function listGetter(target: any, prop: string | number | symbol, receiver: any): any { - if (listHandlers.hasOwnProperty(prop)) { - return listHandlers[prop]; - } - return getter(target, prop, receiver); -} - -interface ListSpliceUpdate { - type: "splice"; - index: number; - added: T[]; - removedCount: number; -} - -interface ListIndexUpdate { - type: "update"; - index: number; - newValue: T; -} - -type ListUpdate = ListSpliceUpdate | ListIndexUpdate; - -type StoredType = T extends RefField ? ProxyField : T; - -@Deserializable("list") -class ListImpl extends ObjectField { - constructor(fields?: T[]) { - super(); - const list = new Proxy(this, { - set: setter, - get: listGetter, - ownKeys: target => Object.keys(target.__fields), - getOwnPropertyDescriptor: (target, prop) => { - if (prop in target.__fields) { - return { - configurable: true,//TODO Should configurable be true? - enumerable: true, - }; - } - return Reflect.getOwnPropertyDescriptor(target, prop); - }, - deleteProperty: deleteProperty, - defineProperty: () => { throw new Error("Currently properties can't be defined on documents using Object.defineProperty"); }, - }); - this[SelfProxy] = list; - if (fields) { - (list as any).push(...fields); - } - return list; - } - - [key: number]: T | (T extends RefField ? Promise : never); - - // this requests all ProxyFields at the same time to avoid the overhead - // of separate network requests and separate updates to the React dom. - private __realFields() { - const waiting = this.__fields.filter(f => f instanceof ProxyField && f.promisedValue()); - const promised = waiting.map(f => f instanceof ProxyField ? f.promisedValue() : ""); - // if we find any ProxyFields that don't have a current value, then - // start the server request for all of them - if (promised.length) { - const promise = DocServer.GetRefFields(promised); - // as soon as we get the fields from the server, set all the list values in one - // action to generate one React dom update. - promise.then(fields => runInAction(() => { - waiting.map((w, i) => w instanceof ProxyField && w.setValue(fields[promised[i]])); - })); - // we also have to mark all lists items with this promise so that any calls to them - // will await the batch request. - // This counts on the handler for 'promise' in the call above being invoked before the - // handler for 'promise' in the lines below. - waiting.map((w, i) => { - w instanceof ProxyField && w.setPromise(promise.then(fields => fields[promised[i]])); - }); - } - return this.__fields.map(toRealField); - } - - @serializable(alias("fields", list(autoObject(), { afterDeserialize: afterDocDeserialize }))) - private get __fields() { - return this.___fields; - } - - private set __fields(value) { - this.___fields = value; - for (const key in value) { - const field = value[key]; - if (!(field instanceof ObjectField)) continue; - (field as ObjectField)[Parent] = this[Self]; - (field as ObjectField)[OnUpdate] = updateFunction(this[Self], key, field, this[SelfProxy]); - } - } - - [Copy]() { - const copiedData = this[Self].__fields.map(f => f instanceof ObjectField ? f[Copy]() : f); - const deepCopy = new ListImpl(copiedData as any); - return deepCopy; - } - - // @serializable(alias("fields", list(autoObject()))) - @observable - private ___fields: StoredType[] = []; - - private [Update] = (diff: any) => { - // console.log(diff); - const update = this[OnUpdate]; - // update && update(diff); - update?.(); - } - - private [Self] = this; - private [SelfProxy]: any; - - [ToScriptString]() { - return `new List([${(this as any).map((field: any) => Field.toScriptString(field))}])`; - } - [ToString]() { - return "List"; - } -} -export type List = ListImpl & (T | (T extends RefField ? Promise : never))[]; -export const List: { new (fields?: T[]): List } = ListImpl as any; - -Scripting.addGlobal("List", List); \ No newline at end of file diff --git a/src/new_fields/ListSpec.ts b/src/new_fields/ListSpec.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/new_fields/ObjectField.ts b/src/new_fields/ObjectField.ts deleted file mode 100644 index 9aa1c9b04..000000000 --- a/src/new_fields/ObjectField.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { RefField } from "./RefField"; -import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols"; -import { Scripting } from "../client/util/Scripting"; - -export abstract class ObjectField { - protected [OnUpdate](diff?: any) { } - private [Parent]?: RefField | ObjectField; - abstract [Copy](): ObjectField; - - abstract [ToScriptString](): string; - abstract [ToString](): string; -} - -export namespace ObjectField { - export function MakeCopy(field: T) { - return field?.[Copy](); - } -} - -Scripting.addGlobal(ObjectField); \ No newline at end of file diff --git a/src/new_fields/PresField.ts b/src/new_fields/PresField.ts deleted file mode 100644 index f236a04fd..000000000 --- a/src/new_fields/PresField.ts +++ /dev/null @@ -1,6 +0,0 @@ -//insert code here -import { ObjectField } from "./ObjectField"; - -export abstract class PresField extends ObjectField { - -} \ No newline at end of file diff --git a/src/new_fields/Proxy.ts b/src/new_fields/Proxy.ts deleted file mode 100644 index 555faaad0..000000000 --- a/src/new_fields/Proxy.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { FieldWaiting } from "./Doc"; -import { primitive, serializable } from "serializr"; -import { observable, action, runInAction } from "mobx"; -import { DocServer } from "../client/DocServer"; -import { RefField } from "./RefField"; -import { ObjectField } from "./ObjectField"; -import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols"; -import { scriptingGlobal } from "../client/util/Scripting"; -import { Plugins } from "./util"; - -@Deserializable("proxy") -export class ProxyField extends ObjectField { - constructor(); - constructor(value: T); - constructor(fieldId: string); - constructor(value?: T | string) { - super(); - if (typeof value === "string") { - this.fieldId = value; - } else if (value) { - this.cache = value; - this.fieldId = value[Id]; - } - } - - [Copy]() { - if (this.cache) return new ProxyField(this.cache); - return new ProxyField(this.fieldId); - } - - [ToScriptString]() { - return "invalid"; - } - [ToString]() { - return "ProxyField"; - } - - @serializable(primitive()) - readonly fieldId: string = ""; - - // This getter/setter and nested object thing is - // because mobx doesn't play well with observable proxies - @observable.ref - private _cache: { readonly field: T | undefined } = { field: undefined }; - private get cache(): T | undefined { - return this._cache.field; - } - private set cache(field: T | undefined) { - this._cache = { field }; - } - - private failed = false; - private promise?: Promise; - - value(): T | undefined | FieldWaiting { - if (this.cache) { - return this.cache; - } - if (this.failed) { - return undefined; - } - if (!this.promise) { - const cached = DocServer.GetCachedRefField(this.fieldId); - if (cached !== undefined) { - runInAction(() => this.cache = cached as any); - return cached as any; - } - this.promise = DocServer.GetRefField(this.fieldId).then(action((field: any) => { - this.promise = undefined; - this.cache = field; - if (field === undefined) this.failed = true; - return field; - })); - } - return this.promise as any; - } - promisedValue(): string { return !this.cache && !this.failed && !this.promise ? this.fieldId : ""; } - setPromise(promise: any) { - this.promise = promise; - } - @action - setValue(field: any) { - this.promise = undefined; - this.cache = field; - if (field === undefined) this.failed = true; - return field; - } -} - -export namespace ProxyField { - let useProxy = true; - export function DisableProxyFields() { - useProxy = false; - } - - export function EnableProxyFields() { - useProxy = true; - } - - export function WithoutProxy(fn: () => T) { - DisableProxyFields(); - try { - return fn(); - } finally { - EnableProxyFields(); - } - } - - export function initPlugin() { - Plugins.addGetterPlugin((doc, _, value) => { - if (useProxy && value instanceof ProxyField) { - return { value: value.value() }; - } - }); - } -} - -function prefetchValue(proxy: PrefetchProxy) { - return proxy.value() as any; -} - -@scriptingGlobal -@Deserializable("prefetch_proxy", prefetchValue) -export class PrefetchProxy extends ProxyField { -} diff --git a/src/new_fields/RefField.ts b/src/new_fields/RefField.ts deleted file mode 100644 index b6ef69750..000000000 --- a/src/new_fields/RefField.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { serializable, primitive, alias } from "serializr"; -import { Utils } from "../Utils"; -import { Id, HandleUpdate, ToScriptString, ToString } from "./FieldSymbols"; -import { afterDocDeserialize } from "../client/util/SerializationHelper"; - -export type FieldId = string; -export abstract class RefField { - @serializable(alias("id", primitive({ afterDeserialize: afterDocDeserialize }))) - private __id: FieldId; - readonly [Id]: FieldId; - - constructor(id?: FieldId) { - this.__id = id || Utils.GenerateGuid(); - this[Id] = this.__id; - } - - protected [HandleUpdate]?(diff: any): void | Promise; - - abstract [ToScriptString](): string; - abstract [ToString](): string; -} diff --git a/src/new_fields/RichTextField.ts b/src/new_fields/RichTextField.ts deleted file mode 100644 index 5cf0e0cc3..000000000 --- a/src/new_fields/RichTextField.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ObjectField } from "./ObjectField"; -import { serializable } from "serializr"; -import { Deserializable } from "../client/util/SerializationHelper"; -import { Copy, ToScriptString, ToPlainText, ToString } from "./FieldSymbols"; -import { scriptingGlobal } from "../client/util/Scripting"; - -@scriptingGlobal -@Deserializable("RichTextField") -export class RichTextField extends ObjectField { - @serializable(true) - readonly Data: string; - - @serializable(true) - readonly Text: string; - - constructor(data: string, text: string = "") { - super(); - this.Data = data; - this.Text = text; - } - - Empty() { - return !(this.Text || this.Data.toString().includes("dashField")); - } - - [Copy]() { - return new RichTextField(this.Data, this.Text); - } - - [ToScriptString]() { - return `new RichTextField("${this.Data}", "${this.Text}")`; - } - [ToString]() { - return this.Text; - } - - public static DashField(fieldKey: string) { - return new RichTextField(`{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"${fieldKey}","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}`, ""); - } - -} \ No newline at end of file diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts deleted file mode 100644 index c475d0d73..000000000 --- a/src/new_fields/RichTextUtils.ts +++ /dev/null @@ -1,519 +0,0 @@ -import { AssertionError } from "assert"; -import { docs_v1 } from "googleapis"; -import { Fragment, Mark, Node } from "prosemirror-model"; -import { sinkListItem } from "prosemirror-schema-list"; -import { Utils } from "../Utils"; -import { Docs } from "../client/documents/Documents"; -import { schema } from "../client/views/nodes/formattedText/schema_rts"; -import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils"; -import { DocServer } from "../client/DocServer"; -import { Networking } from "../client/Network"; -import { FormattedTextBox } from "../client/views/nodes/formattedText/FormattedTextBox"; -import { Doc, Opt } from "./Doc"; -import { Id } from "./FieldSymbols"; -import { RichTextField } from "./RichTextField"; -import { Cast, StrCast } from "./Types"; -import Color = require('color'); -import { EditorState, TextSelection, Transaction } from "prosemirror-state"; -import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClientUtils"; - -export namespace RichTextUtils { - - const delimiter = "\n"; - const joiner = ""; - - - export const Initialize = (initial?: string) => { - const content: any[] = []; - const state = { - doc: { - type: "doc", - content, - }, - selection: { - type: "text", - anchor: 0, - head: 0 - } - }; - if (initial && initial.length) { - content.push({ - type: "paragraph", - content: { - type: "text", - text: initial - } - }); - state.selection.anchor = state.selection.head = initial.length + 1; - } - return JSON.stringify(state); - }; - - export const Synthesize = (plainText: string, oldState?: RichTextField) => { - return new RichTextField(ToProsemirrorState(plainText, oldState), plainText); - }; - - export const ToPlainText = (state: EditorState) => { - // Because we're working with plain text, just concatenate all paragraphs - const content = state.doc.content; - const paragraphs: Node[] = []; - content.forEach(node => node.type.name === "paragraph" && paragraphs.push(node)); - - // Functions to flatten ProseMirror paragraph objects (and their components) to plain text - // Concatentate paragraphs and string the result together - const textParagraphs: string[] = paragraphs.map(paragraph => { - const text: string[] = []; - paragraph.content.forEach(node => node.text && text.push(node.text)); - return text.join(joiner) + delimiter; - }); - const plainText = textParagraphs.join(joiner); - return plainText.substring(0, plainText.length - 1); - }; - - export const ToProsemirrorState = (plainText: string, oldState?: RichTextField) => { - // Remap the text, creating blocks split on newlines - const elements = plainText.split(delimiter); - - // Google Docs adds in an extra carriage return automatically, so this counteracts it - !elements[elements.length - 1].length && elements.pop(); - - // Preserve the current state, but re-write the content to be the blocks - const parsed = JSON.parse(oldState ? oldState.Data : Initialize()); - parsed.doc.content = elements.map(text => { - const paragraph: any = { type: "paragraph" }; - text.length && (paragraph.content = [{ type: "text", marks: [], text }]); // An empty paragraph gets treated as a line break - return paragraph; - }); - - // If the new content is shorter than the previous content and selection is unchanged, may throw an out of bounds exception, so we reset it - parsed.selection = { type: "text", anchor: 1, head: 1 }; - - // Export the ProseMirror-compatible state object we've just built - return JSON.stringify(parsed); - }; - - export namespace GoogleDocs { - - export const Export = async (state: EditorState): Promise => { - const nodes: (Node | null)[] = []; - const text = ToPlainText(state); - state.doc.content.forEach(node => { - if (!node.childCount) { - nodes.push(null); - } else { - node.content.forEach(child => nodes.push(child)); - } - }); - const requests = await marksToStyle(nodes); - return { text, requests }; - }; - - interface ImageTemplate { - width: number; - title: string; - url: string; - agnostic: string; - } - - const parseInlineObjects = async (document: docs_v1.Schema$Document): Promise> => { - const inlineObjectMap = new Map(); - const inlineObjects = document.inlineObjects; - - if (inlineObjects) { - const objects = Object.keys(inlineObjects).map(objectId => inlineObjects[objectId]); - const mediaItems: MediaItem[] = objects.map(object => { - const embeddedObject = object.inlineObjectProperties!.embeddedObject!; - return { baseUrl: embeddedObject.imageProperties!.contentUri! }; - }); - - const uploads = await Networking.PostToServer("/googlePhotosMediaGet", { mediaItems }); - - if (uploads.length !== mediaItems.length) { - throw new AssertionError({ expected: mediaItems.length, actual: uploads.length, message: "Error with internally uploading inlineObjects!" }); - } - - for (let i = 0; i < objects.length; i++) { - const object = objects[i]; - const { accessPaths } = uploads[i]; - const { agnostic, _m } = accessPaths; - const embeddedObject = object.inlineObjectProperties!.embeddedObject!; - const size = embeddedObject.size!; - const width = size.width!.magnitude!; - - inlineObjectMap.set(object.objectId!, { - title: embeddedObject.title || `Imported Image from ${document.title}`, - width, - url: Utils.prepend(_m.client), - agnostic: Utils.prepend(agnostic.client) - }); - } - } - return inlineObjectMap; - }; - - type BulletPosition = { value: number, sinks: number }; - - interface MediaItem { - baseUrl: string; - } - - export const Import = async (documentId: GoogleApiClientUtils.Docs.DocumentId, textNote: Doc): Promise> => { - const document = await GoogleApiClientUtils.Docs.retrieve({ documentId }); - if (!document) { - return undefined; - } - const inlineObjectMap = await parseInlineObjects(document); - const title = document.title!; - const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document); - let state = FormattedTextBox.blankState(); - const structured = parseLists(paragraphs); - - let position = 3; - const lists: ListGroup[] = []; - const indentMap = new Map(); - let globalOffset = 0; - const nodes: Node[] = []; - for (const element of structured) { - if (Array.isArray(element)) { - lists.push(element); - const positions: BulletPosition[] = []; - const items = element.map(paragraph => { - const item = listItem(state.schema, paragraph.contents); - const sinks = paragraph.bullet!; - positions.push({ - value: position + globalOffset, - sinks - }); - position += item.nodeSize; - globalOffset += 2 * sinks; - return item; - }); - indentMap.set(element, positions); - nodes.push(list(state.schema, items)); - } else { - if (element.contents.some(child => "inlineObjectId" in child)) { - const group = element.contents; - group.forEach((child, i) => { - let node: Opt>; - if ("inlineObjectId" in child) { - node = imageNode(state.schema, inlineObjectMap.get(child.inlineObjectId!)!, textNote); - } else if ("content" in child && (i !== group.length - 1 || child.content!.removeTrailingNewlines().length)) { - node = paragraphNode(state.schema, [child]); - } - if (node) { - position += node.nodeSize; - nodes.push(node); - } - }); - } else { - const paragraph = paragraphNode(state.schema, element.contents); - nodes.push(paragraph); - position += paragraph.nodeSize; - } - } - } - state = state.apply(state.tr.replaceWith(0, 2, nodes)); - - const sink = sinkListItem(state.schema.nodes.list_item); - const dispatcher = (tr: Transaction) => state = state.apply(tr); - for (const list of lists) { - for (const pos of indentMap.get(list)!) { - const resolved = state.doc.resolve(pos.value); - state = state.apply(state.tr.setSelection(new TextSelection(resolved))); - for (let i = 0; i < pos.sinks; i++) { - sink(state, dispatcher); - } - } - } - - return { title, text, state }; - }; - - type Paragraph = GoogleApiClientUtils.Docs.Utils.DeconstructedParagraph; - type ListGroup = Paragraph[]; - type PreparedParagraphs = (ListGroup | Paragraph)[]; - - const parseLists = (paragraphs: ListGroup) => { - const groups: PreparedParagraphs = []; - let group: ListGroup = []; - for (const paragraph of paragraphs) { - if (paragraph.bullet !== undefined) { - group.push(paragraph); - } else { - if (group.length) { - groups.push(group); - group = []; - } - groups.push(paragraph); - } - } - group.length && groups.push(group); - return groups; - }; - - const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => { - return schema.node("list_item", null, paragraphNode(schema, runs)); - }; - - const list = (schema: any, items: Node[]): Node => { - return schema.node("bullet_list", null, items); - }; - - const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => { - const children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined); - const fragment = children.length ? Fragment.from(children) : undefined; - return schema.node("paragraph", null, fragment); - }; - - const imageNode = (schema: any, image: ImageTemplate, textNote: Doc) => { - const { url: src, width, agnostic } = image; - let docid: string; - const guid = Utils.GenerateDeterministicGuid(agnostic); - const backingDocId = StrCast(textNote[guid]); - if (!backingDocId) { - const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 }); - Doc.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument); - docid = backingDoc[Id]; - textNote[guid] = docid; - } else { - docid = backingDocId; - } - return schema.node("image", { src, agnostic, width, docid, float: null, location: "onRight" }); - }; - - const textNode = (schema: any, run: docs_v1.Schema$TextRun) => { - const text = run.content!.removeTrailingNewlines(); - return text.length ? schema.text(text, styleToMarks(schema, run.textStyle)) : undefined; - }; - - const StyleToMark = new Map([ - ["bold", "strong"], - ["italic", "em"], - ["foregroundColor", "pFontColor"], - ["fontSize", "pFontSize"] - ]); - - const styleToMarks = (schema: any, textStyle?: docs_v1.Schema$TextStyle) => { - if (!textStyle) { - return undefined; - } - const marks: Mark[] = []; - Object.keys(textStyle).forEach(key => { - let value: any; - const targeted = key as keyof docs_v1.Schema$TextStyle; - if (value = textStyle[targeted]) { - const attributes: any = {}; - let converted = StyleToMark.get(targeted) || targeted; - - value.url && (attributes.href = value.url); - if (value.color) { - const object = value.color.rgbColor; - attributes.color = Color.rgb(["red", "green", "blue"].map(color => object[color] * 255 || 0)).hex(); - } - if (value.magnitude) { - attributes.fontSize = value.magnitude; - } - - if (converted === "weightedFontFamily") { - converted = ImportFontFamilyMapping.get(value.fontFamily) || "timesNewRoman"; - } - - const mapped = schema.marks[converted]; - if (!mapped) { - alert(`No mapping found for ${converted}!`); - return; - } - - const mark = schema.mark(mapped, attributes); - mark && marks.push(mark); - } - }); - return marks; - }; - - const MarkToStyle = new Map([ - ["strong", "bold"], - ["em", "italic"], - ["pFontColor", "foregroundColor"], - ["pFontSize", "fontSize"], - ["timesNewRoman", "weightedFontFamily"], - ["georgia", "weightedFontFamily"], - ["comicSans", "weightedFontFamily"], - ["tahoma", "weightedFontFamily"], - ["impact", "weightedFontFamily"] - ]); - - const ExportFontFamilyMapping = new Map([ - ["timesNewRoman", "Times New Roman"], - ["arial", "Arial"], - ["georgia", "Georgia"], - ["comicSans", "Comic Sans MS"], - ["tahoma", "Tahoma"], - ["impact", "Impact"] - ]); - - const ImportFontFamilyMapping = new Map([ - ["Times New Roman", "timesNewRoman"], - ["Arial", "arial"], - ["Georgia", "georgia"], - ["Comic Sans MS", "comicSans"], - ["Tahoma", "tahoma"], - ["Impact", "impact"] - ]); - - const ignored = ["user_mark"]; - - const marksToStyle = async (nodes: (Node | null)[]): Promise => { - const requests: docs_v1.Schema$Request[] = []; - let position = 1; - for (const node of nodes) { - if (node === null) { - position += 2; - continue; - } - const { marks, attrs, nodeSize } = node; - const textStyle: docs_v1.Schema$TextStyle = {}; - const information: LinkInformation = { - startIndex: position, - endIndex: position + nodeSize, - textStyle - }; - let mark: Mark; - const markMap = BuildMarkMap(marks); - for (const markName of Object.keys(schema.marks)) { - if (ignored.includes(markName) || !(mark = markMap[markName])) { - continue; - } - let converted = MarkToStyle.get(markName) || markName as keyof docs_v1.Schema$TextStyle; - let value: any = true; - if (!converted) { - continue; - } - const { attrs } = mark; - switch (converted) { - case "link": - let url = attrs.href; - const delimiter = "/doc/"; - const alreadyShared = "?sharing=true"; - if (new RegExp(window.location.origin + delimiter).test(url) && !url.endsWith(alreadyShared)) { - const linkDoc = await DocServer.GetRefField(url.split(delimiter)[1]); - if (linkDoc instanceof Doc) { - let exported = (await Cast(linkDoc.anchor2, Doc))!; - if (!exported.customLayout) { - exported = Doc.MakeAlias(exported); - Doc.makeCustomViewClicked(exported, Docs.Create.FreeformDocument); - linkDoc.anchor2 = exported; - } - url = Utils.shareUrl(exported[Id]); - } - } - value = { url }; - textStyle.foregroundColor = fromRgb.blue; - textStyle.bold = true; - break; - case "fontSize": - value = { magnitude: attrs.fontSize, unit: "PT" }; - break; - case "foregroundColor": - value = fromHex(attrs.color); - break; - case "weightedFontFamily": - value = { fontFamily: ExportFontFamilyMapping.get(markName) }; - } - let matches: RegExpExecArray | null; - if ((matches = /p(\d+)/g.exec(markName)) !== null) { - converted = "fontSize"; - value = { magnitude: parseInt(matches[1].replace("px", "")), unit: "PT" }; - } - textStyle[converted] = value; - } - if (Object.keys(textStyle).length) { - requests.push(EncodeStyleUpdate(information)); - } - if (node.type.name === "image") { - const width = attrs.width; - requests.push(await EncodeImage({ - startIndex: position + nodeSize - 1, - uri: attrs.agnostic, - width: Number(typeof width === "string" ? width.replace("px", "") : width) - })); - } - position += nodeSize; - } - return requests; - }; - - const BuildMarkMap = (marks: Mark[]) => { - const markMap: { [type: string]: Mark } = {}; - marks.forEach(mark => markMap[mark.type.name] = mark); - return markMap; - }; - - interface LinkInformation { - startIndex: number; - endIndex: number; - textStyle: docs_v1.Schema$TextStyle; - } - - interface ImageInformation { - startIndex: number; - width: number; - uri: string; - } - - namespace fromRgb { - - export const convert = (red: number, green: number, blue: number): docs_v1.Schema$OptionalColor => { - return { - color: { - rgbColor: { - red: red / 255, - green: green / 255, - blue: blue / 255 - } - } - }; - }; - - export const red = convert(255, 0, 0); - export const green = convert(0, 255, 0); - export const blue = convert(0, 0, 255); - - } - - const fromHex = (color: string): docs_v1.Schema$OptionalColor => { - const c = Color(color); - return fromRgb.convert(c.red(), c.green(), c.blue()); - }; - - const EncodeStyleUpdate = (information: LinkInformation): docs_v1.Schema$Request => { - const { startIndex, endIndex, textStyle } = information; - return { - updateTextStyle: { - fields: "*", - range: { startIndex, endIndex }, - textStyle - } as docs_v1.Schema$UpdateTextStyleRequest - }; - }; - - const EncodeImage = async ({ uri, width, startIndex }: ImageInformation) => { - if (!uri) { - return {}; - } - const source = [Docs.Create.ImageDocument(uri)]; - const baseUrls = await GooglePhotos.Transactions.UploadThenFetch(source); - if (baseUrls) { - return { - insertInlineImage: { - uri: baseUrls[0], - objectSize: { width: { magnitude: width, unit: "PT" } }, - location: { index: startIndex } - } - }; - } - return {}; - }; - } - -} \ No newline at end of file diff --git a/src/new_fields/Schema.ts b/src/new_fields/Schema.ts deleted file mode 100644 index 72bce283d..000000000 --- a/src/new_fields/Schema.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from "./Types"; -import { Doc, Field } from "./Doc"; -import { ObjectField } from "./ObjectField"; -import { RefField } from "./RefField"; -import { SelfProxy } from "./FieldSymbols"; - -type AllToInterface = { - 1: ToInterface> & AllToInterface>, - 0: ToInterface> -}[HasTail extends true ? 1 : 0]; - -export const emptySchema = createSchema({}); -export const Document = makeInterface(emptySchema); -export type Document = makeInterface<[typeof emptySchema]>; - -export interface InterfaceFunc { - (docs: Doc[]): makeInterface[]; - (): makeInterface; - (doc: Doc): makeInterface; -} - -export type makeInterface = AllToInterface & Doc & { proto: Doc | undefined }; -// export function makeInterface(schemas: T): (doc: U) => All; -// export function makeInterface(schema: T): (doc: U) => makeInterface; -export function makeInterface(...schemas: T): InterfaceFunc { - const schema: Interface = {}; - for (const s of schemas) { - for (const key in s) { - schema[key] = s[key]; - } - } - const proto = new Proxy({}, { - get(target: any, prop, receiver) { - const field = receiver.doc[prop]; - if (prop in schema) { - const desc = prop === "proto" ? Doc : (schema as any)[prop]; // bcz: proto doesn't appear in schemas ... maybe it should? - if (typeof desc === "object" && "defaultVal" in desc && "type" in desc) {//defaultSpec - return Cast(field, desc.type, desc.defaultVal); - } else if (typeof desc === "function" && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) { - const doc = Cast(field, Doc); - if (doc === undefined) { - return undefined; - } else if (doc instanceof Doc) { - return desc(doc); - } else { - return doc.then(doc => doc && desc(doc)); - } - } else { - return Cast(field, desc); - } - } - return field; - }, - set(target: any, prop, value, receiver) { - receiver.doc[prop] = value; - return true; - } - }); - const fn = (doc: Doc) => { - doc = doc[SelfProxy]; - // if (!(doc instanceof Doc)) { - // throw new Error("Currently wrapping a schema in another schema isn't supported"); - // } - const obj = Object.create(proto, { doc: { value: doc, writable: false } }); - return obj; - }; - return function (doc?: Doc | Doc[]) { - doc = doc || new Doc; - if (doc instanceof Doc) { - return fn(doc); - } else { - return doc.map(fn); - } - }; -} - -export type makeStrictInterface = Partial>; -export function makeStrictInterface(schema: T): (doc: Doc) => makeStrictInterface { - const proto = {}; - for (const key in schema) { - const type = schema[key]; - Object.defineProperty(proto, key, { - get() { - return Cast(this.__doc[key], type as any); - }, - set(value) { - value = Cast(value, type as any); - if (value !== undefined) { - this.__doc[key] = value; - return; - } - throw new TypeError("Expected type " + type); - } - }); - } - return function (doc: any) { - if (!(doc instanceof Doc)) { - throw new Error("Currently wrapping a schema in another schema isn't supported"); - } - const obj = Object.create(proto); - obj.__doc = doc; - return obj; - }; -} - -export function createSchema(schema: T): T & { proto: ToConstructor } { - (schema as any).proto = Doc; - return schema as any; -} - -export function listSpec>(type: U): ListSpec> { - return { List: type as any };//TODO Types -} - -export function defaultSpec>(type: T, defaultVal: ToType): DefaultFieldConstructor> { - return { - type: type as any, - defaultVal - }; -} \ No newline at end of file diff --git a/src/new_fields/SchemaHeaderField.ts b/src/new_fields/SchemaHeaderField.ts deleted file mode 100644 index 07c90f5a2..000000000 --- a/src/new_fields/SchemaHeaderField.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, primitive } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { Copy, ToScriptString, ToString, OnUpdate } from "./FieldSymbols"; -import { scriptingGlobal } from "../client/util/Scripting"; -import { ColumnType } from "../client/views/collections/CollectionSchemaView"; - -export const PastelSchemaPalette = new Map([ - // ["pink1", "#FFB4E8"], - ["pink2", "#ff9cee"], - ["pink3", "#ffccf9"], - ["pink4", "#fcc2ff"], - ["pink5", "#f6a6ff"], - ["purple1", "#b28dff"], - ["purple2", "#c5a3ff"], - ["purple3", "#d5aaff"], - ["purple4", "#ecd4ff"], - // ["purple5", "#fb34ff"], - ["purple6", "#dcd3ff"], - ["purple7", "#a79aff"], - ["purple8", "#b5b9ff"], - ["purple9", "#97a2ff"], - ["bluegreen1", "#afcbff"], - ["bluegreen2", "#aff8db"], - ["bluegreen3", "#c4faf8"], - ["bluegreen4", "#85e3ff"], - ["bluegreen5", "#ace7ff"], - // ["bluegreen6", "#6eb5ff"], - ["bluegreen7", "#bffcc6"], - ["bluegreen8", "#dbffd6"], - ["yellow1", "#f3ffe3"], - ["yellow2", "#e7ffac"], - ["yellow3", "#ffffd1"], - ["yellow4", "#fff5ba"], - // ["red1", "#ffc9de"], - ["red2", "#ffabab"], - ["red3", "#ffbebc"], - ["red4", "#ffcbc1"], - ["orange1", "#ffd5b3"], -]); - -export const RandomPastel = () => Array.from(PastelSchemaPalette.values())[Math.floor(Math.random() * PastelSchemaPalette.size)]; - -export const DarkPastelSchemaPalette = new Map([ - ["pink2", "#c932b0"], - ["purple4", "#913ad6"], - ["bluegreen1", "#3978ed"], - ["bluegreen7", "#2adb3e"], - ["bluegreen5", "#21b0eb"], - ["yellow4", "#edcc0c"], - ["red2", "#eb3636"], - ["orange1", "#f2740f"], -]); - -@scriptingGlobal -@Deserializable("schemaheader") -export class SchemaHeaderField extends ObjectField { - @serializable(primitive()) - heading: string; - @serializable(primitive()) - color: string; - @serializable(primitive()) - type: number; - @serializable(primitive()) - width: number; - @serializable(primitive()) - collapsed: boolean | undefined; - @serializable(primitive()) - desc: boolean | undefined; // boolean determines sort order, undefined when no sort - - constructor(heading: string = "", color: string = RandomPastel(), type?: ColumnType, width?: number, desc?: boolean, collapsed?: boolean) { - super(); - - this.heading = heading; - this.color = color; - this.type = type ? type : 0; - this.width = width ? width : -1; - this.desc = desc; - this.collapsed = collapsed; - } - - setHeading(heading: string) { - this.heading = heading; - this[OnUpdate](); - } - - setColor(color: string) { - this.color = color; - this[OnUpdate](); - } - - setType(type: ColumnType) { - this.type = type; - this[OnUpdate](); - } - - setWidth(width: number) { - this.width = width; - this[OnUpdate](); - } - - setDesc(desc: boolean | undefined) { - this.desc = desc; - this[OnUpdate](); - } - - setCollapsed(collapsed: boolean | undefined) { - this.collapsed = collapsed; - this[OnUpdate](); - } - - [Copy]() { - return new SchemaHeaderField(this.heading, this.color, this.type); - } - - [ToScriptString]() { - return `invalid`; - } - [ToString]() { - return `SchemaHeaderField`; - } -} \ No newline at end of file diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts deleted file mode 100644 index 4b790f483..000000000 --- a/src/new_fields/ScriptField.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { ObjectField } from "./ObjectField"; -import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions, CompileError, CompileResult } from "../client/util/Scripting"; -import { Copy, ToScriptString, ToString, Parent, SelfProxy } from "./FieldSymbols"; -import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; -import { Deserializable, autoObject } from "../client/util/SerializationHelper"; -import { Doc, Field } from "../new_fields/Doc"; -import { Plugins, setter } from "./util"; -import { computedFn } from "mobx-utils"; -import { ProxyField } from "./Proxy"; -import { Cast } from "./Types"; - -function optional(propSchema: PropSchema) { - return custom(value => { - if (value !== undefined) { - return propSchema.serializer(value); - } - return SKIP; - }, (jsonValue: any, context: any, oldValue: any, callback: (err: any, result: any) => void) => { - if (jsonValue !== undefined) { - return propSchema.deserializer(jsonValue, callback, context, oldValue); - } - return SKIP; - }); -} - -const optionsSchema = createSimpleSchema({ - requiredType: true, - addReturn: true, - typecheck: true, - editable: true, - readonly: true, - params: optional(map(primitive())) -}); - -const scriptSchema = createSimpleSchema({ - options: object(optionsSchema), - originalScript: true -}); - -async function deserializeScript(script: ScriptField) { - const captures: ProxyField = (script as any).captures; - if (captures) { - const doc = (await captures.value())!; - const captured: any = {}; - const keys = Object.keys(doc); - const vals = await Promise.all(keys.map(key => doc[key]) as any); - keys.forEach((key, i) => captured[key] = vals[i]); - (script.script.options as any).capturedVariables = captured; - } - const comp = CompileScript(script.script.originalScript, script.script.options); - if (!comp.compiled) { - throw new Error("Couldn't compile loaded script"); - } - (script as any).script = comp; -} - -@scriptingGlobal -@Deserializable("script", deserializeScript) -export class ScriptField extends ObjectField { - @serializable(object(scriptSchema)) - readonly script: CompiledScript; - @serializable(object(scriptSchema)) - readonly setterscript: CompiledScript | undefined; - - @serializable(autoObject()) - private captures?: ProxyField; - - constructor(script: CompiledScript, setterscript?: CompileResult) { - super(); - - if (script?.options.capturedVariables) { - const doc = Doc.assign(new Doc, script.options.capturedVariables); - this.captures = new ProxyField(doc); - } - this.setterscript = setterscript?.compiled ? setterscript : undefined; - this.script = script; - } - - // init(callback: (res: Field) => any) { - // const options = this.options!; - // const keys = Object.keys(options.options.capturedIds); - // Server.GetFields(keys).then(fields => { - // let captured: { [name: string]: Field } = {}; - // keys.forEach(key => captured[options.options.capturedIds[key]] = fields[key]); - // const opts: ScriptOptions = { - // addReturn: options.options.addReturn, - // params: options.options.params, - // requiredType: options.options.requiredType, - // capturedVariables: captured - // }; - // const script = CompileScript(options.script, opts); - // if (!script.compiled) { - // throw new Error("Can't compile script"); - // } - // this._script = script; - // callback(this); - // }); - // } - - [Copy](): ObjectField { - return new ScriptField(this.script); - } - toString() { - return `${this.script.originalScript}`; - } - - [ToScriptString]() { - return "script field"; - } - [ToString]() { - return this.script.originalScript; - } - public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) { - const compiled = CompileScript(script, { - params: { this: Doc.name, self: Doc.name, _last_: "any", ...params }, - typecheck: false, - editable: true, - addReturn: addReturn, - capturedVariables - }); - return compiled; - } - public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) { - const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); - return compiled.compiled ? new ScriptField(compiled) : undefined; - } - - public static MakeScript(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }) { - const compiled = ScriptField.CompileScript(script, params, false, capturedVariables); - return compiled.compiled ? new ScriptField(compiled) : undefined; - } -} - -@scriptingGlobal -@Deserializable("computed", deserializeScript) -export class ComputedField extends ScriptField { - _lastComputedResult: any; - //TODO maybe add an observable cache based on what is passed in for doc, considering there shouldn't really be that many possible values for doc - value = computedFn((doc: Doc) => this._valueOutsideReaction(doc)); - _valueOutsideReaction = (doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult }, console.log).result; - - - constructor(script: CompiledScript, setterscript?: CompiledScript) { - super(script, - !setterscript && script?.originalScript.includes("self.timecode") ? - ScriptField.CompileScript("self['x' + self.timecode] = value", { value: "any" }, true) : setterscript); - } - - public static MakeScript(script: string, params: object = {}) { - const compiled = ScriptField.CompileScript(script, params, false); - return compiled.compiled ? new ComputedField(compiled) : undefined; - } - public static MakeFunction(script: string, params: object = {}, capturedVariables?: { [name: string]: Field }, setterScript?: string) { - const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); - const setCompiled = setterScript ? ScriptField.CompileScript(setterScript, params, true, capturedVariables) : undefined; - return compiled.compiled ? new ComputedField(compiled, setCompiled?.compiled ? setCompiled : undefined) : undefined; - } - public static MakeInterpolated(fieldKey: string, interpolatorKey: string) { - const getField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}]`, {}, true, {}); - const setField = ScriptField.CompileScript(`(self['${fieldKey}-indexed'])[self.${interpolatorKey}] = value`, { value: "any" }, true, {}); - return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; - } -} - -export namespace ComputedField { - let useComputed = true; - export function DisableComputedFields() { - useComputed = false; - } - - export function EnableComputedFields() { - useComputed = true; - } - - export const undefined = "__undefined"; - - export function WithoutComputed(fn: () => T) { - DisableComputedFields(); - try { - return fn(); - } finally { - EnableComputedFields(); - } - } - - export function initPlugin() { - Plugins.addGetterPlugin((doc, _, value) => { - if (useComputed && value instanceof ComputedField) { - return { value: value._valueOutsideReaction(doc), shouldReturn: true }; - } - }); - } -} \ No newline at end of file diff --git a/src/new_fields/Types.ts b/src/new_fields/Types.ts deleted file mode 100644 index 3d784448d..000000000 --- a/src/new_fields/Types.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Field, Opt, FieldResult, Doc } from "./Doc"; -import { List } from "./List"; -import { RefField } from "./RefField"; -import { DateField } from "./DateField"; -import { ScriptField } from "./ScriptField"; - -export type ToType = - T extends "string" ? string : - T extends "number" ? number : - T extends "boolean" ? boolean : - T extends ListSpec ? List : - // T extends { new(...args: any[]): infer R } ? (R | Promise) : never; - T extends DefaultFieldConstructor ? never : - T extends { new(...args: any[]): List } ? never : - T extends { new(...args: any[]): infer R } ? R : - T extends (doc?: Doc) => infer R ? R : never; - -export type ToConstructor = - T extends string ? "string" : - T extends number ? "number" : - T extends boolean ? "boolean" : - T extends List ? ListSpec : - new (...args: any[]) => T; - -export type ToInterface = { - [P in Exclude]: T[P] extends DefaultFieldConstructor ? Exclude, undefined> : FieldResult>; -}; - -// type ListSpec = { List: ToContructor> | ListSpec> }; -export type ListSpec = { List: ToConstructor }; - -export type DefaultFieldConstructor = { - type: ToConstructor, - defaultVal: T -}; - -// type ListType = { 0: List>>, 1: ToType> }[HasTail extends true ? 0 : 1]; - -export type Head = T extends [any, ...any[]] ? T[0] : never; -export type Tail = - ((...t: T) => any) extends ((_: any, ...tail: infer TT) => any) ? TT : []; -export type HasTail = T extends ([] | [any]) ? false : true; - -export type InterfaceValue = ToConstructor | ListSpec | DefaultFieldConstructor | ((doc?: Doc) => any); -//TODO Allow you to optionally specify default values for schemas, which should then make that field not be partial -export interface Interface { - [key: string]: InterfaceValue; - // [key: string]: ToConstructor | ListSpec; -} -export type WithoutRefField = T extends RefField ? never : T; - -export type CastCtor = ToConstructor | ListSpec; - -export function Cast(field: FieldResult, ctor: T): FieldResult>; -export function Cast(field: FieldResult, ctor: T, defaultVal: WithoutList>> | null): WithoutList>; -export function Cast(field: FieldResult, ctor: T, defaultVal?: ToType | null): FieldResult> | undefined { - if (field instanceof Promise) { - return defaultVal === undefined ? field.then(f => Cast(f, ctor) as any) as any : defaultVal === null ? undefined : defaultVal; - } - if (field !== undefined && !(field instanceof Promise)) { - if (typeof ctor === "string") { - if (typeof field === ctor) { - return field as ToType; - } - } else if (typeof ctor === "object") { - if (field instanceof List) { - return field as any; - } - } else if (field instanceof (ctor as any)) { - return field as ToType; - } - } - return defaultVal === null ? undefined : defaultVal; -} - -export function NumCast(field: FieldResult, defaultVal: number | null = 0) { - return Cast(field, "number", defaultVal); -} - -export function StrCast(field: FieldResult, defaultVal: string | null = "") { - return Cast(field, "string", defaultVal); -} - -export function BoolCast(field: FieldResult, defaultVal: boolean | null = false) { - return Cast(field, "boolean", defaultVal); -} -export function DateCast(field: FieldResult) { - return Cast(field, DateField, null); -} - -export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) { - return Cast(field, ScriptField, defaultVal); -} - -type WithoutList = T extends List ? (R extends RefField ? (R | Promise)[] : R[]) : T; - -export function FieldValue>(field: FieldResult, defaultValue: U): WithoutList; -export function FieldValue(field: FieldResult): Opt; -export function FieldValue(field: FieldResult, defaultValue?: T): Opt { - return (field instanceof Promise || field === undefined) ? defaultValue : field; -} - -export interface PromiseLike { - then(callback: (field: Opt) => void): void; -} -export function PromiseValue(field: FieldResult): PromiseLike> { - return field instanceof Promise ? field : { then(cb: ((field: Opt) => void)) { return cb(field); } }; -} \ No newline at end of file diff --git a/src/new_fields/URLField.ts b/src/new_fields/URLField.ts deleted file mode 100644 index fb71160ca..000000000 --- a/src/new_fields/URLField.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Deserializable } from "../client/util/SerializationHelper"; -import { serializable, custom } from "serializr"; -import { ObjectField } from "./ObjectField"; -import { ToScriptString, ToString, Copy } from "./FieldSymbols"; -import { Scripting, scriptingGlobal } from "../client/util/Scripting"; - -function url() { - return custom( - function (value: URL) { - return value.href; - }, - function (jsonValue: string) { - return new URL(jsonValue); - } - ); -} - -export abstract class URLField extends ObjectField { - @serializable(url()) - readonly url: URL; - - constructor(url: string); - constructor(url: URL); - constructor(url: URL | string) { - super(); - if (typeof url === "string") { - url = new URL(url); - } - this.url = url; - } - - [ToScriptString]() { - return `new ${this.constructor.name}("${this.url.href}")`; - } - [ToString]() { - return this.url.href; - } - - [Copy](): this { - return new (this.constructor as any)(this.url); - } -} - -export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short.ogg"; - -@scriptingGlobal @Deserializable("audio") export class AudioField extends URLField { } -@scriptingGlobal @Deserializable("image") export class ImageField extends URLField { } -@scriptingGlobal @Deserializable("video") export class VideoField extends URLField { } -@scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { } -@scriptingGlobal @Deserializable("web") export class WebField extends URLField { } -@scriptingGlobal @Deserializable("youtube") export class YoutubeField extends URLField { } -@scriptingGlobal @Deserializable("webcam") export class WebCamField extends URLField { } - diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts deleted file mode 100644 index cacba43b6..000000000 --- a/src/new_fields/documentSchemas.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { makeInterface, createSchema, listSpec } from "./Schema"; -import { ScriptField } from "./ScriptField"; -import { Doc } from "./Doc"; -import { DateField } from "./DateField"; - -export const documentSchema = createSchema({ - // content properties - type: "string", // enumerated type of document -- should be template-specific (ie, start with an '_') - title: "string", // document title (can be on either data document or layout) - isTemplateForField: "string",// if specified, it indicates the document is a template that renders the specified field - creationDate: DateField, // when the document was created - links: listSpec(Doc), // computed (readonly) list of links associated with this document - - // "Location" properties in a very general sense - currentTimecode: "number", // current play back time of a temporal document (video / audio) - displayTimecode: "number", // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) - inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently - x: "number", // x coordinate when in a freeform view - y: "number", // y coordinate when in a freeform view - z: "number", // z "coordinate" - non-zero specifies the overlay layer of a freeformview - zIndex: "number", // zIndex of a document in a freeform view - scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) - scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) - - // appearance properties on the layout - _autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents - _nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set - _nativeHeight: "number", // " - _width: "number", // width of document in its container's coordinate system - _height: "number", // " - _xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set - _yPadding: "number", // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set - _xMargin: "number", // margin added on left/right of most documents to add separation from their container - _yMargin: "number", // margin added on top/bottom of most documents to add separation from their container - _overflow: "string", // sets overflow behvavior for CollectionFreeForm views - _showCaption: "string", // whether editable caption text is overlayed at the bottom of the document - _showTitle: "string", // the fieldkey whose contents should be displayed at the top of the document - _showTitleHover: "string", // the showTitle should be shown only on hover - _showAudio: "boolean", // whether to show the audio record icon on documents - _freeformLayoutEngine: "string",// the string ID for the layout engine to use to layout freeform view documents - _LODdisable: "boolean", // whether to disbale LOD switching for CollectionFreeFormViews - _pivotField: "string", // specifies which field key should be used as the timeline/pivot axis - _replacedChrome: "string", // what the default chrome is replaced with. Currently only supports the value of 'replaced' for PresBox's. - _chromeStatus: "string", // determines the state of the collection chrome. values allowed are 'replaced', 'enabled', 'disabled', 'collapsed' - _fontSize: "number", - _fontFamily: "string", - _sidebarWidthPercent: "string", // percent of text window width taken up by sidebar - - // appearance properties on the data document - backgroundColor: "string", // background color of document - borderRounding: "string", // border radius rounding of document - boxShadow: "string", // the amount of shadow around the perimeter of a document - color: "string", // foreground color of document - fitToBox: "boolean", // whether freeform view contents should be zoomed/panned to fill the area of the document view - fontSize: "string", - layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below - layoutKey: "string", // holds the field key for the field that actually holds the current lyoat - letterSpacing: "string", - opacity: "number", // opacity of document - strokeWidth: "number", - textTransform: "string", - treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden - treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree - treeViewPreventOpen: "boolean", // ignores the treeViewOpen flag (for allowing a view to not be slaved to other views of the document) - - // interaction and linking properties - ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) - onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) - onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) - onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) - onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. - followLinkLocation: "string",// flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab, ) - isInPlaceContainer: "boolean",// whether the marked object will display addDocTab() calls that target "inPlace" destinations - isLinkButton: "boolean", // whether document functions as a link follow button to follow the first link on the document when clicked - isBackground: "boolean", // whether document is a background element and ignores input events (can only select with marquee) - lockedPosition: "boolean", // whether the document can be moved (dragged) - lockedTransform: "boolean", // whether the document can be panned/zoomed - - // drag drop properties - dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. - dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move") - targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move' - childDropAction: "string", // specify the override for what should happen when the child of a collection is dragged from it and dropped (can be "alias" or "copy") - removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped -}); - - -export const collectionSchema = createSchema({ - childLayoutTemplateName: "string", // the name of a template to use to override the layoutKey when rendering a document -- ONLY used in DocHolderBox - childLayoutTemplate: Doc, // layout template to use to render children of a collecion - childLayoutString: "string", //layout string to use to render children of a collection - childClickedOpenTemplateView: Doc, // layout template to apply to a child when its clicked on in a collection and opened (requires onChildClick or other script to read this value and apply template) - dontRegisterChildViews: "boolean", // whether views made of this document are registered so that they can be found when drawing links scrollToLinkID: "string", // id of link being traversed. allows this doc to scroll/highlight/etc its link anchor. scrollToLinkID should be set to undefined by this doc after it sets up its scroll,etc. - onChildClick: ScriptField, // script to run for each child when its clicked - onChildDoubleClick: ScriptField, // script to run for each child when its clicked - onCheckedClick: ScriptField, // script to run when a checkbox is clicked next to a child in a tree view -}); - -export type Document = makeInterface<[typeof documentSchema]>; -export const Document = makeInterface(documentSchema); diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts deleted file mode 100644 index a287b0210..000000000 --- a/src/new_fields/util.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { UndoManager } from "../client/util/UndoManager"; -import { Doc, Field, FieldResult, UpdatingFromServer, LayoutSym } from "./Doc"; -import { SerializationHelper } from "../client/util/SerializationHelper"; -import { ProxyField, PrefetchProxy } from "./Proxy"; -import { RefField } from "./RefField"; -import { ObjectField } from "./ObjectField"; -import { action, trace } from "mobx"; -import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols"; -import { DocServer } from "../client/DocServer"; -import { ComputedField } from "./ScriptField"; -import { ScriptCast } from "./Types"; - -function _readOnlySetter(): never { - throw new Error("Documents can't be modified in read-only mode"); -} - -const tracing = false; -export function TraceMobx() { - tracing && trace(); -} - -export interface GetterResult { - value: FieldResult; - shouldReturn?: boolean; -} -export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined; -const getterPlugins: GetterPlugin[] = []; - -export namespace Plugins { - export function addGetterPlugin(plugin: GetterPlugin) { - getterPlugins.push(plugin); - } -} - -const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { - //console.log("-set " + target[SelfProxy].title + "(" + target[SelfProxy][prop] + ")." + prop.toString() + " = " + value); - if (SerializationHelper.IsSerializing()) { - target[prop] = value; - return true; - } - - if (typeof prop === "symbol") { - target[prop] = value; - return true; - } - if (value !== undefined) { - value = value[SelfProxy] || value; - } - const curValue = target.__fields[prop]; - if (curValue === value || (curValue instanceof ProxyField && value instanceof RefField && curValue.fieldId === value[Id])) { - // TODO This kind of checks correctly in the case that curValue is a ProxyField and value is a RefField, but technically - // curValue should get filled in with value if it isn't already filled in, in case we fetched the referenced field some other way - return true; - } - if (value instanceof RefField) { - value = new ProxyField(value); - } - if (value instanceof ObjectField) { - if (value[Parent] && value[Parent] !== receiver && !(value instanceof PrefetchProxy)) { - throw new Error("Can't put the same object in multiple documents at the same time"); - } - value[Parent] = receiver; - value[OnUpdate] = updateFunction(target, prop, value, receiver); - } - if (curValue instanceof ObjectField) { - delete curValue[Parent]; - delete curValue[OnUpdate]; - } - const writeMode = DocServer.getFieldWriteMode(prop as string); - const fromServer = target[UpdatingFromServer]; - const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail); - const writeToDoc = sameAuthor || (writeMode !== DocServer.WriteMode.LiveReadonly); - const writeToServer = sameAuthor || (writeMode === DocServer.WriteMode.Default); - if (writeToDoc) { - if (value === undefined) { - delete target.__fields[prop]; - } else { - target.__fields[prop] = value; - } - if (typeof value === "object" && !(value instanceof ObjectField)) debugger; - if (writeToServer) { - if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } }); - else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } }); - } else { - DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); - } - UndoManager.AddEvent({ - redo: () => receiver[prop] = value, - undo: () => receiver[prop] = curValue - }); - } - return true; -}); - -let _setter: (target: any, prop: string | symbol | number, value: any, receiver: any) => boolean = _setterImpl; - -export function makeReadOnly() { - _setter = _readOnlySetter; -} - -export function makeEditable() { - _setter = _setterImpl; -} - -const layoutProps = ["panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "fitWidth", "fitToBox", - "LODdisable", "chromeStatus", "viewType", "gridGap", "xMargin", "yMargin", "autoHeight"]; -export function setter(target: any, in_prop: string | symbol | number, value: any, receiver: any): boolean { - let prop = in_prop; - if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { - if (!prop.startsWith("_")) { - console.log(prop + " is deprecated - switch to _" + prop); - prop = "_" + prop; - } - if (target.__LAYOUT__) { - target.__LAYOUT__[prop] = value; - return true; - } - } - if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript) { - return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; - } - return _setter(target, prop, value, receiver); -} - -export function getter(target: any, in_prop: string | symbol | number, receiver: any): any { - let prop = in_prop; - if (prop === LayoutSym) { - return target.__LAYOUT__; - } - if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && (prop.startsWith("_") || layoutProps.includes(prop))) { - if (!prop.startsWith("_")) { - console.log(prop + " is deprecated - switch to _" + prop); - prop = "_" + prop; - } - if (target.__LAYOUT__) return target.__LAYOUT__[prop]; - } - if (prop === "then") {//If we're being awaited - return undefined; - } - if (typeof prop === "symbol") { - return target.__fields[prop] || target[prop]; - } - if (SerializationHelper.IsSerializing()) { - return target[prop]; - } - return getFieldImpl(target, prop, receiver); -} - -function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any { - receiver = receiver || target[SelfProxy]; - let field = target.__fields[prop]; - for (const plugin of getterPlugins) { - const res = plugin(receiver, prop, field); - if (res === undefined) continue; - if (res.shouldReturn) { - return res.value; - } else { - field = res.value; - } - } - if (field === undefined && !ignoreProto && prop !== "proto") { - const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters - if (proto instanceof Doc) { - return getFieldImpl(proto[Self], prop, receiver, ignoreProto); - } - return undefined; - } - return field; - -} -export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any { - return getFieldImpl(target, prop, undefined, ignoreProto); -} - -export function deleteProperty(target: any, prop: string | number | symbol) { - if (typeof prop === "symbol") { - delete target[prop]; - return true; - } - target[SelfProxy][prop] = undefined; - return true; -} - -export function updateFunction(target: any, prop: any, value: any, receiver: any) { - let current = ObjectField.MakeCopy(value); - return (diff?: any) => { - if (true || !diff) { - diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; - const oldValue = current; - const newValue = ObjectField.MakeCopy(value); - current = newValue; - UndoManager.AddEvent({ - redo() { receiver[prop] = newValue; }, - undo() { receiver[prop] = oldValue; } - }); - } - target[Update](diff); - }; -} \ No newline at end of file diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index b8a82ab4d..3b6170f68 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -1,9 +1,9 @@ import { NDollarRecognizer } from "./ndollar"; import { Type } from "typescript"; -import { InkField, PointData } from "../new_fields/InkField"; +import { InkField, PointData } from "../fields/InkField"; import { Docs } from "../client/documents/Documents"; -import { Doc, WidthSym, HeightSym } from "../new_fields/Doc"; -import { NumCast } from "../new_fields/Types"; +import { Doc, WidthSym, HeightSym } from "../fields/Doc"; +import { NumCast } from "../fields/Types"; import { CollectionFreeFormView } from "../client/views/collections/collectionFreeForm/CollectionFreeFormView"; import { Rect } from "react-measure"; import { Scripting } from "../client/util/Scripting"; diff --git a/src/server/ApiManagers/GooglePhotosManager.ts b/src/server/ApiManagers/GooglePhotosManager.ts index 11841a603..be17b698e 100644 --- a/src/server/ApiManagers/GooglePhotosManager.ts +++ b/src/server/ApiManagers/GooglePhotosManager.ts @@ -3,7 +3,7 @@ import { Method, _error, _success, _invalid } from "../RouteManager"; import * as path from "path"; import { GoogleApiServerUtils } from "../apis/google/GoogleApiServerUtils"; import { BatchedArray, TimeUnit } from "array-batcher"; -import { Opt } from "../../new_fields/Doc"; +import { Opt } from "../../fields/Doc"; import { DashUploadUtils, InjectSize, SizeSuffix } from "../DashUploadUtils"; import { Database } from "../database"; import { red } from "colors"; diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts index 68b3107ae..0d1d8f218 100644 --- a/src/server/ApiManagers/UserManager.ts +++ b/src/server/ApiManagers/UserManager.ts @@ -3,7 +3,7 @@ import { Method } from "../RouteManager"; import { Database } from "../database"; import { msToTime } from "../ActionUtilities"; import * as bcrypt from "bcrypt-nodejs"; -import { Opt } from "../../new_fields/Doc"; +import { Opt } from "../../fields/Doc"; export const timeMap: { [id: string]: number } = {}; interface ActivityUnit { diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 8567631cd..b74904ada 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import * as sharp from 'sharp'; import request = require('request-promise'); import { ExifImage } from 'exif'; -import { Opt } from '../new_fields/Doc'; +import { Opt } from '../fields/Doc'; import { AcceptibleMedia, Upload } from './SharedMediaTypes'; import { filesDirectory, publicDirectory } from '.'; import { File } from 'formidable'; diff --git a/src/server/Message.ts b/src/server/Message.ts index 01aae5de7..80f372733 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -1,6 +1,6 @@ import { Utils } from "../Utils"; import { Point } from "../pen-gestures/ndollar"; -import { Doc } from "../new_fields/Doc"; +import { Doc } from "../fields/Doc"; import { Image } from "canvas"; import { AnalysisResult, ImportResults } from "../scraping/buxton/final/BuxtonImporter"; diff --git a/src/server/Recommender.ts b/src/server/Recommender.ts index aacdb4053..423ce9b46 100644 --- a/src/server/Recommender.ts +++ b/src/server/Recommender.ts @@ -1,6 +1,6 @@ -// //import { Doc } from "../new_fields/Doc"; -// //import { StrCast } from "../new_fields/Types"; -// //import { List } from "../new_fields/List"; +// //import { Doc } from "../fields/Doc"; +// //import { StrCast } from "../fields/Types"; +// //import { List } from "../fields/List"; // //import { CognitiveServices } from "../client/cognitive_services/CognitiveServices"; // // var w2v = require('word2vec'); diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts index 2e4811c86..20f96f432 100644 --- a/src/server/apis/google/GoogleApiServerUtils.ts +++ b/src/server/apis/google/GoogleApiServerUtils.ts @@ -1,6 +1,6 @@ import { google } from "googleapis"; import { OAuth2Client, Credentials, OAuth2ClientOptions } from "google-auth-library"; -import { Opt } from "../../../new_fields/Doc"; +import { Opt } from "../../../fields/Doc"; import { GaxiosResponse } from "gaxios"; import request = require('request-promise'); import * as qs from "query-string"; diff --git a/src/server/database.ts b/src/server/database.ts index ed9a246e3..a5f23c4b1 100644 --- a/src/server/database.ts +++ b/src/server/database.ts @@ -1,6 +1,6 @@ import * as mongodb from 'mongodb'; import { Transferable } from './Message'; -import { Opt } from '../new_fields/Doc'; +import { Opt } from '../fields/Doc'; import { Utils, emptyFunction } from '../Utils'; import { Credentials } from 'google-auth-library'; import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils'; diff --git a/test/test.ts b/test/test.ts index 245733e9b..5fc156b46 100644 --- a/test/test.ts +++ b/test/test.ts @@ -8,10 +8,10 @@ const dom = new JSDOM("", { import { autorun, reaction } from "mobx"; -import { Doc } from '../src/new_fields/Doc'; -import { Cast } from '../src/new_fields/Types'; -import { createSchema, makeInterface, defaultSpec } from '../src/new_fields/Schema'; -import { ImageField } from '../src/new_fields/URLField'; +import { Doc } from '../src/fields/Doc'; +import { Cast } from '../src/fields/Types'; +import { createSchema, makeInterface, defaultSpec } from '../src/fields/Schema'; +import { ImageField } from '../src/fields/URLField'; describe("Document", () => { it('should hold fields', () => { const key = "Test"; -- cgit v1.2.3-70-g09d2