aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
diff options
context:
space:
mode:
authorStanley Yip <stanley_yip@brown.edu>2020-05-20 22:18:13 -0700
committerStanley Yip <stanley_yip@brown.edu>2020-05-20 22:18:13 -0700
commit1113da5f2db22bd2b2b457b1f5b183d7f1f2e68d (patch)
treea559fa400fedb87799cba4f85451460f649c46dd /src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
parent4109fea6787608022c8f5c147fa4fb9ad26e92eb (diff)
parent45386f6950e5ae8390486900a430061bfe9e4846 (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx128
1 files changed, 90 insertions, 38 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c3a77841e..0b61aad8a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,18 +4,18 @@ 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, ComputedField } 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 { aggregateBounds, intersectRect, returnOne, Utils, returnZero, returnFalse, numberRange } from "../../../../Utils";
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
import { Docs } from "../../../documents/Documents";
@@ -46,7 +46,7 @@ import React = require("react");
import { CollectionViewType } from "../CollectionView";
import { Timeline } from "../../animationtimeline/Timeline";
import { SnappingManager } from "../../../util/SnappingManager";
-import GestureOverlay from "../../GestureOverlay";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -54,6 +54,8 @@ export const panZoomSchema = createSchema({
_panX: "number",
_panY: "number",
scale: "number",
+ currentTimecode: "number",
+ displayTimecode: "number",
arrangeScript: ScriptField,
arrangeInit: ScriptField,
useClusters: "boolean",
@@ -124,7 +126,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newBox);
}
- private addDocument = (newBox: Doc | Doc[]) => {
+ addDocument = (newBox: Doc | Doc[]) => {
+ if (this.Document.currentTimecode !== undefined && !this.props.isAnnotationOverlay) {
+ CollectionFreeFormDocumentView.setupKeyframes((newBox instanceof Doc) ? [newBox] : newBox, this.Document.currentTimecode, this.props.Document);
+ }
+
if (newBox instanceof Doc) {
const added = this.props.addDocument(newBox);
added && this.bringToFront(newBox);
@@ -135,6 +141,31 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
// bcz: deal with clusters
}
}
+
+ @undoBatch
+ @action
+ nextKeyframe = (): void => {
+ const currentTimecode = this.Document.currentTimecode;
+ if (currentTimecode === undefined) {
+ this.Document.currentTimecode = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0, this.props.Document);
+ }
+ CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentTimecode || 0);
+ this.Document.currentTimecode = Math.max(0, (currentTimecode || 0) + 1);
+ this.Document.lastTimecode = Math.max(NumCast(this.Document.currentTimecode), NumCast(this.Document.lastTimecode));
+ }
+ @undoBatch
+ @action
+ prevKeyframe = (): void => {
+ const currentTimecode = this.Document.currentTimecode;
+ if (currentTimecode === undefined) {
+ this.Document.currentTimecode = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0, this.props.Document);
+ }
+ CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
+ this.Document.currentTimecode = Math.max(0, (currentTimecode || 0) - 1);
+ }
+
private selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true));
@@ -175,8 +206,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
for (let i = 0; i < droppedDocs.length; i++) {
const d = droppedDocs[i];
const layoutDoc = Doc.Layout(d);
- d.x = x + NumCast(d.x) - dropX;
- d.y = y + NumCast(d.y) - dropY;
+ if (this.Document.currentTimecode !== undefined && !this.props.isAnnotationOverlay) {
+ CollectionFreeFormDocumentView.setValues(this.Document.currentTimecode, d, x + NumCast(d.x) - dropX, y + NumCast(d.y) - dropY, Cast(d.opacity, "number", null));
+ } else {
+ d.x = x + NumCast(d.x) - dropX;
+ d.y = y + NumCast(d.y) - dropY;
+ }
if (!NumCast(layoutDoc._width)) {
layoutDoc._width = 300;
}
@@ -735,7 +770,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.props.Document.lockedTransform || this.props.Document.inOverlay) return;
+ if (this.layoutDoc._lockedTransform || this.props.Document.inOverlay) return;
if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
}
@@ -771,7 +806,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
}
}
- if (!this.Document.lockedTransform || this.Document.inOverlay) {
+ if (!this.layoutDoc._lockedTransform || this.Document.inOverlay) {
this.Document.panTransformType = panType;
const scale = this.getLocalTransform().inverse().Scale;
const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX));
@@ -944,10 +979,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return { x: 0, y: 0, transition: "transform 1s", ...result, pair: params.pair, replica: "" };
}
const layoutDoc = Doc.Layout(params.pair.layout);
- const { x, y, z, color, zIndex } = params.pair.layout;
+ const { x, y, opacity } = this.Document.currentTimecode === undefined ? params.pair.layout :
+ CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document.currentTimecode || 0);
+ const { z, color, zIndex } = params.pair.layout;
return {
x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
- transition: StrCast(layoutDoc.transition),
+ transition: StrCast(layoutDoc.transition), opacity: Cast(opacity, "number", null),
width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number"), pair: params.pair, replica: ""
};
}
@@ -1007,8 +1044,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
doFreeformLayout(poolData: Map<string, PoolData>) {
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);
- const state = initResult && initResult.success ? initResult.result.scriptState : undefined;
- const elements = initResult && initResult.success ? this.viewDefsToJSX(initResult.result.views) : [];
+ const state = initResult?.success ? initResult.result.scriptState : undefined;
+ const elements = initResult?.success ? this.viewDefsToJSX(initResult.result.views) : [];
this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => {
const pos = this.getCalculatedPositions({ pair, index: i, collection: this.Document, docs: layoutDocs, state });
@@ -1036,11 +1073,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const { newPool, computedElementData } = this.doInternalLayoutComputation;
const array = Array.from(newPool.entries());
runInAction(() => {
- 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) {
+ if (!lastPos || newPos.opacity !== lastPos.opacity || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) {
this._layoutPoolData.set(entry[0], newPos);
}
if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
@@ -1064,7 +1100,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.backgroundActive ?
true :
(this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? false : undefined}
- jitterRotation={NumCast(this.props.Document._jitterRotation)}
+ jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))}
//fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
fitToBox={BoolCast(this.props.freezeChildDimensions)} // bcz: check this
FreezeDimensions={BoolCast(this.props.freezeChildDimensions)}
@@ -1134,6 +1170,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight());
}
+ @undoBatch
+ @action
+ toggleLockTransform = (): void => {
+ this.layoutDoc._lockedTransform = this.layoutDoc._lockedTransform ? undefined : true;
+ }
+
private thumbIdentifier?: number;
onContextMenu = (e: React.MouseEvent) => {
@@ -1154,6 +1196,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
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" });
optionItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" });
this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" });
+ optionItems.push({ description: this.layoutDoc._lockedTransform ? "Unlock Transform" : "Lock Transform", event: this.toggleLockTransform, icon: this.layoutDoc._lockedTransform ? "unlock" : "lock" });
optionItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" });
// layoutItems.push({ description: "Analyze Strokes", event: this.analyzeStrokes, icon: "paint-brush" });
optionItems.push({
@@ -1299,18 +1342,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
render() {
TraceMobx();
const clientRect = this._mainCont?.getBoundingClientRect();
- // update the actual dimensions of the collection so that they can inquired (e.g., by a minimap)
- // this.Document.fitX = this.contentBounds && this.contentBounds.x;
- // this.Document.fitY = this.contentBounds && this.contentBounds.y;
- // this.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x);
- // this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y);
- // 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
- return <div className={"collectionfreeformview-container"}
- ref={this.createDashEventsTarget}
+ return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget}
onPointerOver={this.onPointerOver}
- onWheel={this.onPointerWheel} onClick={this.onClick} //pointerEvents: DraggingManager.GetIsDragging() ? "all" : undefined,
- onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu}
+ onWheel={this.onPointerWheel}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}
+ onPointerMove={this.onCursorMove}
+ onDrop={this.onExternalDrop.bind(this)}
+ onDragOver={e => {
+ e.preventDefault();
+ }}
+ onContextMenu={this.onContextMenu}
style={{
pointerEvents: this.backgroundEvents ? "all" : undefined,
transform: this.contentScaling ? `scale(${this.contentScaling})` : "",
@@ -1321,12 +1363,22 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
{!this.Document._LODdisable && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ?
this.placeholder : this.marqueeView}
<CollectionFreeFormOverlayView elements={this.elementFunc} />
-
+ {this.isAnnotationOverlay || !this.props.isSelected() ? (null) :
+ <>
+ <div key="back" className="backKeyframe" onClick={this.prevKeyframe}>
+ <FontAwesomeIcon icon={"caret-left"} size={"lg"} />
+ </div>
+ <div key="num" className="numKeyframe" >
+ {NumCast(this.props.Document.currentTimecode)}
+ </div>
+ <div key="fwd" className="fwdKeyframe" onClick={this.nextKeyframe}>
+ <FontAwesomeIcon icon={"caret-right"} size={"lg"} />
+ </div>
+ </>}
<div className={"pullpane-indicator"}
style={{
display: this._pullDirection ? "block" : "none",
top: clientRect ? this._pullDirection === "bottom" ? this._pullCoords[1] - clientRect.y : 0 : "auto",
- // left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x - MainView.Instance.flyoutWidth : 0 : "auto",
left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x : 0 : "auto",
width: clientRect ? this._pullDirection === "left" ? this._pullCoords[0] - clientRect.left : this._pullDirection === "right" ? clientRect.right - this._pullCoords[0] : clientRect.width : 0,
height: clientRect ? this._pullDirection === "top" ? this._pullCoords[1] - clientRect.top : this._pullDirection === "bottom" ? clientRect.bottom - this._pullCoords[1] : clientRect.height : 0,