diff options
| author | Andy Rickert <andrew_rickert@brown.edu> | 2020-07-31 19:04:32 -0400 |
|---|---|---|
| committer | Andy Rickert <andrew_rickert@brown.edu> | 2020-07-31 19:04:32 -0400 |
| commit | cfa39673564b6cd12d61ebbf3778cea8c3d0eff2 (patch) | |
| tree | fd43d10a62c5d8f15057af7f1d788f263194ee0d /src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | |
| parent | a1950ec49c56f5f9e2612da7e60a1e2615209386 (diff) | |
| parent | c71d7fa4f84149bcb62246d50e06bfd1481365bc (diff) | |
merge
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 218 |
1 files changed, 132 insertions, 86 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 546a4307c..57336131a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,53 +1,53 @@ import { library } from "@fortawesome/fontawesome-svg-core"; -import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons"; +import { faEye } 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, _allowStateChangesInsideComputed, trace } from "mobx"; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; -import { Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../../fields/Doc"; -import { documentSchema, collectionSchema } from "../../../../fields/documentSchemas"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../fields/Doc"; +import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas"; import { Id } from "../../../../fields/FieldSymbols"; -import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkField"; +import { InkData, InkField, InkTool } 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 { createSchema, 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, numberRange } from "../../../../Utils"; +import { aggregateBounds, intersectRect, returnFalse, returnOne, returnZero, Utils } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from "../../../documents/Documents"; +import { DocumentType } from "../../../documents/DocumentTypes"; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager, dropActionType } from "../../../util/DragManager"; import { HistoryUtil } from "../../../util/History"; import { InteractionUtils } from "../../../util/InteractionUtils"; import { SelectionManager } from "../../../util/SelectionManager"; +import { SnappingManager } from "../../../util/SnappingManager"; import { Transform } from "../../../util/Transform"; import { undoBatch, UndoManager } from "../../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"; +import { Timeline } from "../../animationtimeline/Timeline"; import { ContextMenu } from "../../ContextMenu"; -import { ContextMenuProps } from "../../ContextMenuItem"; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; -import { DocumentViewProps, DocumentView } from "../../nodes/DocumentView"; +import { DocumentLinksButton } from "../../nodes/DocumentLinksButton"; +import { DocumentViewProps } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; -import PDFMenu from "../../pdf/PDFMenu"; import { CollectionDockingView } from "../CollectionDockingView"; import { CollectionSubView } from "../CollectionSubView"; -import { computePivotLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult, computerStarburstLayout, computerPassLayout } from "./CollectionFreeFormLayoutEngines"; +import { CollectionViewType } from "../CollectionView"; +import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; import "./CollectionFreeFormView.scss"; import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { CollectionViewType } from "../CollectionView"; -import { Timeline } from "../../animationtimeline/Timeline"; -import { SnappingManager } from "../../../util/SnappingManager"; -import { InkingStroke, ActiveArrowStart, ActiveArrowEnd, ActiveInkColor, ActiveFillColor, ActiveInkWidth, ActiveInkBezierApprox, ActiveDash } from "../../InkingStroke"; -import { DocumentType } from "../../../documents/DocumentTypes"; -import { DocumentLinksButton } from "../../nodes/DocumentLinksButton"; +import { SearchUtil } from "../../../util/SearchUtil"; +import { LinkManager } from "../../../util/LinkManager"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -57,7 +57,6 @@ export const panZoomSchema = createSchema({ currentTimecode: "number", displayTimecode: "number", currentFrame: "number", - arrangeScript: ScriptField, arrangeInit: ScriptField, useClusters: "boolean", fitToBox: "boolean", @@ -77,7 +76,8 @@ export type collectionFreeformViewProps = { forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; childPointerEvents?: boolean; - scaleField?: string; + scaleField?: string; // used by formattedTextBox when displaying a sidebar freeform view which needs its own scale field + noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) }; @observer @@ -103,6 +103,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @observable _clusterSets: (Doc[])[] = []; @observable _timelineRef = React.createRef<Timeline>(); + @observable _marqueeRef = React.createRef<HTMLDivElement>(); + @observable canPanX: boolean = true; + @observable canPanY: boolean = true; + @computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; } @computed get fitToContent() { return (this.props.fitToBox || this.Document._fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; } @@ -182,7 +186,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P private selectDocuments = (docs: Doc[]) => { SelectionManager.DeselectAll(); - docs.map(doc => DocumentManager.Instance.getDocumentView(doc)).map(dv => dv && SelectionManager.SelectDoc(dv, true)); + docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectDoc(dv, true)); } public isCurrent(doc: Doc) { return (Math.abs(NumCast(doc.displayTimecode, -1) - NumCast(this.Document.currentTimecode, -1)) < 1.5 || NumCast(doc.displayTimecode, -1) === -1); } @@ -207,7 +211,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P for (let i = 0; i < docDragData.droppedDocuments.length; i++) { const d = docDragData.droppedDocuments[i]; const layoutDoc = Doc.Layout(d); - if (this.Document.currentFrame !== undefined && !this.props.isAnnotationOverlay) { + if (this.Document.currentFrame !== undefined) { const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); CollectionFreeFormDocumentView.setValues(this.Document.currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.opacity); } else { @@ -248,7 +252,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } else { const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" }); this.props.addDocument(source); - linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation"); // TODODO this is where in text links get passed + linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation", ""); // TODODO this is where in text links get passed e.stopPropagation(); return true; } @@ -492,10 +496,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const start = this.getTransform().transformPoint(Math.min(...ge.points.map(p => p.X)), Math.min(...ge.points.map(p => p.Y))); this._inkToTextStartX = start[0]; this._inkToTextStartY = start[1]; - console.log("start"); break; case GestureUtils.Gestures.EndBracket: - console.log("end"); if (this._inkToTextStartX && this._inkToTextStartY) { const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y))); const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === "rtf" && s.color); @@ -602,7 +604,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => { // bcz: theres should be a better way of doing these than referencing these static instances directly MarqueeOptionsMenu.Instance?.fadeOut(true);// I think it makes sense for the marquee menu to go away when panned. -syip2 - PDFMenu.Instance.fadeOut(true); const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, undefined, true); @@ -870,7 +871,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => { const state = HistoryUtil.getState(); - // TODO This technically isn't correct if type !== "doc", as + // TODO This technically isn't correct if type !== "doc", as // currently nothing is done, but we should probably push a new state if (state.type === "doc" && this.Document._panX !== undefined && this.Document._panY !== undefined) { const init = state.initializers![this.Document[Id]]; @@ -890,7 +891,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.props.focus(doc); } else { const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height); - const offset = annotOn && (contextHgt / 2 * 96 / 72); + const offset = annotOn && (contextHgt / 2); this.props.Document._scrollY = NumCast(doc.y) - offset; } @@ -935,22 +936,26 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } @computed get libraryPath() { return this.props.LibraryPath ? [...this.props.LibraryPath, this.props.Document] : []; } - @computed get onChildClickHandler() { return this.props.childClickScript || ScriptCast(this.Document.onChildClick); } - @computed get onChildDoubleClickHandler() { return this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); } @computed get backgroundActive() { return this.layoutDoc.isBackground && (this.props.ContainingCollectionView?.active() || this.props.active()); } + onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); + onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); backgroundHalo = () => BoolCast(this.Document.useClusters); parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.backgroundActive ? true : false; getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { - ...this.props, + addDocument: this.props.addDocument, + removeDocument: this.props.removeDocument, + moveDocument: this.props.moveDocument, + pinToPres: this.props.pinToPres, + whenActiveChanged: this.props.whenActiveChanged, NativeHeight: returnZero, NativeWidth: returnZero, fitToBox: false, DataDoc: childData, Document: childLayout, LibraryPath: this.libraryPath, - LayoutTemplate: this.props.ChildLayoutTemplate, - LayoutTemplateString: this.props.ChildLayoutString, + LayoutTemplate: childLayout.z ? undefined : this.props.ChildLayoutTemplate, + LayoutTemplateString: childLayout.z ? undefined : this.props.ChildLayoutString, FreezeDimensions: this.props.freezeChildDimensions, layoutKey: undefined, setupDragLines: this.setupDragLines, @@ -999,10 +1004,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return this.props.addDocTab(doc, where); }); getCalculatedPositions(params: { pair: { layout: Doc, data?: Doc }, index: number, collection: Doc, docs: Doc[], state: any }): PoolData { - const result = this.Document.arrangeScript?.script.run(params, console.log); - if (result?.success) { - return { x: 0, y: 0, transition: "transform 1s", ...result, pair: params.pair, replica: "" }; - } const layoutDoc = Doc.Layout(params.pair.layout); const { x, y, opacity } = this.Document.currentFrame === undefined ? params.pair.layout : CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document.currentFrame || 0); @@ -1030,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const transform = `translate(${x}px, ${y}px)`; 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 fontSize = Cast(viewDef.fontSize, "number"); + const fontSize = Cast(viewDef.fontSize, "string"); return [text, x, y].some(val => val === undefined) ? undefined : { ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z + color} style={{ width, height, color, fontSize, transform }}> @@ -1068,7 +1069,7 @@ 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 initResult = this.Document.arrangeInit?.script.run({ docs: layoutDocs, collection: this.Document }, console.log); const state = initResult?.success ? initResult.result.scriptState : undefined; const elements = initResult?.success ? this.viewDefsToJSX(initResult.result.views) : []; @@ -1143,13 +1144,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action componentDidMount() { super.componentDidMount?.(); - this._layoutComputeReaction = reaction(() => this.doLayoutComputation, + this._layoutComputeReaction = reaction(() => { TraceMobx(); return this.doLayoutComputation }, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); + + this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any); } + componentWillUnmount() { this._layoutComputeReaction?.(); + this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any); } + @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } elementFunc = () => this._layoutElements; @@ -1158,6 +1164,29 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); } + + // <div ref={this._marqueeRef}> + + @action + onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => { + if ((e as any).handlePan || this.props.isAnnotationOverlay) return; + (e as any).handlePan = true; + + if (this._marqueeRef?.current) { + const dragX = e.detail.clientX; + const dragY = e.detail.clientY; + const bounds = this._marqueeRef.current?.getBoundingClientRect(); + + const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0; + const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0; + if (deltaX !== 0 || deltaY !== 0) { + this.Document._panY = NumCast(this.Document._panY) + deltaY / 2; + this.Document._panX = NumCast(this.Document._panX) + deltaX / 2; + } + } + e.stopPropagation(); + } + promoteCollection = undoBatch(action(() => { const childDocs = this.childDocs.slice(); childDocs.forEach(doc => { @@ -1204,59 +1233,73 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P private thumbIdentifier?: number; onContextMenu = (e: React.MouseEvent) => { - if (this.props.annotationsKey) return; + if (this.props.annotationsKey || !ContextMenu.Instance) return; const appearance = ContextMenu.Instance.findByDescription("Appearance..."); const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : []; - appearanceItems.push({ description: "reset view", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); - appearanceItems.push({ description: `${this.fitToContent ? "Unset" : "Set"} Fit To Container`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); - appearanceItems.push({ description: `${this.Document.useClusters ? "Uncluster" : "Use Clusters"}`, event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); - appearanceItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" }); + appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" }); + appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" }); + appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }); !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" }); + const viewctrls = ContextMenu.Instance.findByDescription("UI Controls..."); + const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : []; + viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }); + viewCtrlItems.push({ description: (this.Document.useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document.useClusters), icon: "braille" }); + !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" }); + const options = ContextMenu.Instance.findByDescription("Options..."); const optionItems = options && "subitems" in options ? options.subitems : []; - !this.props.isAnnotationOverlay && + !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode && optionItems.push({ description: (this.showTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this.showTimeline = !this.showTimeline), icon: faEye }); this.props.ContainingCollectionView && optionItems.push({ description: "Promote Collection", event: this.promoteCollection, icon: "table" }); - optionItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " snap lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }); 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" }); + appearanceItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" }); if (!Doc.UserDoc().noviceMode) { optionItems.push({ description: (!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" }); optionItems.push({ description: `${this.Document._freeformLOD ? "Enable LOD" : "Disable LOD"}`, event: () => this.Document._freeformLOD = !this.Document._freeformLOD, icon: "table" }); - optionItems.push({ - description: "Import document", icon: "upload", event: ({ x, y }) => { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".zip"; - input.onchange = async _e => { - const upload = Utils.prepend("/uploadDoc"); - const formData = new FormData(); - const file = input.files && input.files[0]; - if (file) { - formData.append('file', file); - formData.append('remap', "true"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json !== "error") { - const doc = await DocServer.GetRefField(json); - if (doc instanceof Doc) { - const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y); - doc.x = xx, doc.y = yy; - this.props.addDocument?.(doc); - } - } - } - }; - input.click(); - } - }); + } !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" }); + const mores = ContextMenu.Instance.findByDescription("More..."); + const moreItems = mores && "subitems" in mores ? mores.subitems : []; + moreItems.push({ description: "Import document", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) }); + !mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" }); + } + importDocument = (x: number, y: number) => { + const input = document.createElement("input"); + input.type = "file"; + input.accept = ".zip"; + input.onchange = async _e => { + const upload = Utils.prepend("/uploadDoc"); + const formData = new FormData(); + const file = input.files && input.files[0]; + if (file) { + formData.append('file', file); + formData.append('remap', "true"); + const response = await fetch(upload, { method: "POST", body: formData }); + const json = await response.json(); + if (json !== "error") { + const doc = await DocServer.GetRefField(json); + if (doc instanceof Doc) { + const [xx, yy] = this.props.ScreenToLocalTransform().transformPoint(x, y); + doc.x = xx, doc.y = yy; + this.props.addDocument?.(doc); + setTimeout(() => { + SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => { + docs.docs.forEach(d => LinkManager.Instance.addLink(d)); + }) + }, 2000); // need to give solr some time to update so that this query will find any link docs we've added. + } + } + } + }; + input.click(); } + + @observable showTimeline = false; intersectRect(r1: { left: number, top: number, width: number, height: number }, @@ -1339,7 +1382,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return false; }); @computed get marqueeView() { - return <MarqueeView {...this.props} + return <MarqueeView + {...this.props} nudge={this.nudge} addDocTab={this.addDocTab} activeDocuments={this.getActiveDocuments} @@ -1349,14 +1393,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}> - <CollectionFreeFormViewPannableContents - centeringShiftX={this.centeringShiftX} - centeringShiftY={this.centeringShiftY} - transition={Cast(this.layoutDoc._viewTransition, "string", null)} - viewDefDivClick={this.props.viewDefDivClick} - zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}> - {this.children} - </CollectionFreeFormViewPannableContents> + <div ref={this._marqueeRef}> + <CollectionFreeFormViewPannableContents + centeringShiftX={this.centeringShiftX} + centeringShiftY={this.centeringShiftY} + transition={Cast(this.layoutDoc._viewTransition, "string", null)} + viewDefDivClick={this.props.viewDefDivClick} + zoomScaling={this.zoomScaling} panX={this.panX} panY={this.panY}> + {this.children} + </CollectionFreeFormViewPannableContents></div> {this.showTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)} </MarqueeView>; } @@ -1373,6 +1418,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P render() { TraceMobx(); const clientRect = this._mainCont?.getBoundingClientRect(); + !this.fitToContent && this._layoutElements?.length && setTimeout(() => this.Document._renderContentBounds = new List<number>([this.contentBounds.x, this.contentBounds.y, this.contentBounds.r, this.contentBounds.b]), 0); return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget} onPointerOver={this.onPointerOver} onWheel={this.onPointerWheel} @@ -1391,7 +1437,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P }}> {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && !this.props.annotationsKey && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} - <CollectionFreeFormOverlayView elements={this.elementFunc} /> + {!this.props.noOverlay ? <CollectionFreeFormOverlayView elements={this.elementFunc} /> : (null)} <div className={"pullpane-indicator"} style={{ @@ -1453,4 +1499,4 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF {this.props.children()} </div>; } -}
\ No newline at end of file +} |
