diff options
-rw-r--r-- | src/client/util/DragManager.ts | 10 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.scss | 8 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 10 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMapView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 14 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 53 |
6 files changed, 75 insertions, 22 deletions
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 66c7ecf7d..2a9c1633a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -263,16 +263,16 @@ export namespace DragManager { const denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); if (denominator === 0) return undefined; // Lines are parallel - let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator; + const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator; // let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator; //if (ua < 0 || ua > 1 || ub < 0 || ub > 1) return undefined; // is the intersection along the segments // Return a object with the x and y coordinates of the intersection - let x = x1 + ua * (x2 - x1) - let y = y1 + ua * (y2 - y1) + const x = x1 + ua * (x2 - x1); + const y = y1 + ua * (y2 - y1); const dist = Math.sqrt((dragx - x) * (dragx - x) + (dragy - y) * (dragy - y)); - return { pt: [x, y], dist } - } + return { pt: [x, y], dist }; + }; SnappingManager.vertSnapLines().forEach((xCoord, i) => { const pt = intersect(dragPt[0], dragPt[1], dragPt[0] + snapAspect, dragPt[1] + 1, xCoord, -1, xCoord, 1, dragPt[0], dragPt[1]); if (pt && pt.dist < closest) { diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 15eb537da..a4d4af2f0 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -138,9 +138,10 @@ $linkGap : 3px; .documentDecorations-contextMenu { width: 25px; height: calc(100% + 8px); // 8px for the height of the top resizer bar - grid-column-start: 1; + grid-column-start: 2; grid-column-end : 2; pointer-events: all; + padding-left: 5px; } .documentDecorations-title { opacity: 1; @@ -185,9 +186,12 @@ $linkGap : 3px; position: absolute; left: 0px; top: 0px; - width: $MINIMIZED_ICON_SIZE; + width: 8px; height: $MINIMIZED_ICON_SIZE; max-height: 20px; + > svg { + margin:0; + } } .documentDecorations-background { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 1f4f45f0e..fc7c16296 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -476,12 +476,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> <FontAwesomeIcon size="lg" color={SelectionManager.SelectedDocuments()[0].props.Document.title === SelectionManager.SelectedDocuments()[0].props.Document[Id] ? "green" : undefined} icon="sticky-note"></FontAwesomeIcon> </div>} </> : - <div className="documentDecorations-title" onPointerDown={this.onTitleDown} > - {minimal ? (null) : <div className="documentDecorations-contextMenu" title="Show context menu" onPointerDown={this.onSettingsDown}> + <> + {minimal ? (null) : <div className="documentDecorations-contextMenu" key="menu" title="Show context menu" onPointerDown={this.onSettingsDown}> <FontAwesomeIcon size="lg" icon="cog" /> </div>} - <span style={{ width: "calc(100% - 25px)", display: "inline-block" }}>{`${this.selectionTitle}`}</span> - </div>; + <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} > + <span style={{ width: "calc(100% - 25px)", display: "inline-block" }}>{`${this.selectionTitle}`}</span> + </div> + </> bounds.x = Math.max(0, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2; bounds.y = Math.max(0, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight; diff --git a/src/client/views/collections/CollectionMapView.tsx b/src/client/views/collections/CollectionMapView.tsx index da902cecd..d91337ce9 100644 --- a/src/client/views/collections/CollectionMapView.tsx +++ b/src/client/views/collections/CollectionMapView.tsx @@ -1,4 +1,4 @@ -import { GoogleApiWrapper, Map as GeoMap, MapProps as IMapProps, Marker } from "google-maps-react"; +import { GoogleApiWrapper, Map as GeoMap, IMapProps, Marker } from "google-maps-react"; import { observer } from "mobx-react"; import { Doc, Opt, DocListCast, FieldResult, Field } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a4e843c8e..c9eb08b45 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -206,6 +206,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: } } + addDocument = (doc: Doc | Doc[]) => this.props.addDocument(doc); + @undoBatch @action protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { @@ -214,21 +216,21 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: if (docDragData) { let added = false; if (docDragData.dropAction || docDragData.userDropAction) { - added = this.props.addDocument(docDragData.droppedDocuments); + added = this.addDocument(docDragData.droppedDocuments); } else if (docDragData.moveDocument) { const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d); const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d); - const res = addedDocs.length ? this.props.addDocument(addedDocs) : true; - added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.props.addDocument) : res; + const res = addedDocs.length ? this.addDocument(addedDocs) : true; + added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.addDocument) : res; } else { - added = this.props.addDocument(docDragData.droppedDocuments); + added = this.addDocument(docDragData.droppedDocuments); } e.stopPropagation(); return added; } else if (de.complete.annoDragData) { e.stopPropagation(); - return this.props.addDocument(de.complete.annoDragData.dropDocument); + return this.addDocument(de.complete.annoDragData.dropDocument); } return false; } @@ -265,7 +267,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: e.stopPropagation(); e.preventDefault(); - const { addDocument } = this.props; + const { addDocument } = this; if (!addDocument) { alert("this.props.addDocument does not exist. Aborting drop operation."); return; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index af78fbe34..bf679309c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -11,11 +11,11 @@ import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkFie import { List } from "../../../../fields/List"; import { RichTextField } from "../../../../fields/RichTextField"; import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema"; -import { ScriptField } from "../../../../fields/ScriptField"; +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,6 @@ import React = require("react"); import { CollectionViewType } from "../CollectionView"; import { Timeline } from "../../animationtimeline/Timeline"; import { SnappingManager } from "../../../util/SnappingManager"; -import GestureOverlay from "../../GestureOverlay"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -124,7 +123,18 @@ 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[]) => { + const timecode = Cast(this.props.Document.timecode, "number", null); + if (timecode !== undefined) { + ((newBox instanceof Doc) ? [newBox] : newBox).map(doc => { + doc["x-indexed"] = new List<number>(numberRange(timecode + 1).map(i => NumCast(doc.x))); + doc["y-indexed"] = new List<number>(numberRange(timecode + 1).map(i => NumCast(doc.y))); + doc.timecode = ComputedField.MakeFunction("collection.timecode", {}, { collection: this.props.Document }); + doc.x = ComputedField.MakeInterpolated("x", "timecode"); + doc.y = ComputedField.MakeInterpolated("y", "timecode"); + }); + } + if (newBox instanceof Doc) { const added = this.props.addDocument(newBox); added && this.bringToFront(newBox); @@ -1132,6 +1142,39 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth(), this.props.NativeHeight()); } + @undoBatch + @action + snaphsotInterpolated = (): void => { + if (this.props.Document.timecode === undefined) { + this.childDocs.map(doc => { + this.props.Document.timecode = 0; + doc["x-indexed"] = new List<number>([NumCast(doc.x)]); + doc["y-indexed"] = new List<number>([NumCast(doc.y)]); + doc.timecode = ComputedField.MakeFunction("collection.timecode", {}, { collection: this.props.Document }); + doc.x = ComputedField.MakeInterpolated("x", "timecode"); + doc.y = ComputedField.MakeInterpolated("y", "timecode"); + }); + } + const timecode = NumCast(this.props.Document.timecode); + this.childDocs.map(doc => { + const xindexed = Cast(doc['x-indexed'], listSpec("number"), null); + const yindexed = Cast(doc['y-indexed'], listSpec("number"), null); + xindexed.length <= timecode + 1 && xindexed.push(NumCast(doc.x)); + yindexed.length <= timecode + 1 && yindexed.push(NumCast(doc.y)); + }); + this.childDocs.map(doc => doc.transition = "transform 1s"); + this.props.Document.timecode = Math.max(0, timecode + 1); + setTimeout(() => this.childDocs.map(doc => doc.transition = undefined), 1010); + } + @undoBatch + @action + backupInterpolated = (): void => { + this.childDocs.map(doc => doc.transition = "transform 1s"); + this.props.Document.timecode = Math.max(0, NumCast(this.props.Document.timecode) - 1); + setTimeout(() => this.childDocs.map(doc => doc.transition = undefined), 1010); + } + + private thumbIdentifier?: number; onContextMenu = (e: React.MouseEvent) => { @@ -1142,6 +1185,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this._timelineVisible = !this._timelineVisible; }), icon: this._timelineVisible ? faEyeSlash : faEye }); + ContextMenu.Instance.addItem({ description: "Advance", event: this.snaphsotInterpolated, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); + ContextMenu.Instance.addItem({ description: "Backup ", event: this.backupInterpolated, icon: BoolCast(this.Document.lockedTransform) ? "unlock" : "lock" }); const options = ContextMenu.Instance.findByDescription("Options..."); const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; |