diff options
Diffstat (limited to 'src/client/views/nodes/ImageBox.tsx')
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 318 |
1 files changed, 200 insertions, 118 deletions
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index fa8eb7736..207546936 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -4,14 +4,13 @@ import { faAsterisk, faFileAudio, faImage, faPaintBrush, faBrain } from '@fortaw import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable, runInAction, trace } from 'mobx'; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; +import { Doc, DocListCast, HeightSym, WidthSym, DataSym } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { createSchema, listSpec, makeInterface } from '../../../new_fields/Schema'; import { ComputedField } from '../../../new_fields/ScriptField'; -import { BoolCast, Cast, FieldValue, NumCast, StrCast } from '../../../new_fields/Types'; +import { Cast, NumCast, StrCast } from '../../../new_fields/Types'; import { AudioField, ImageField } from '../../../new_fields/URLField'; -import { RouteStore } from '../../../server/RouteStore'; -import { Utils, returnOne, emptyFunction, OmitKeys } from '../../../Utils'; +import { Utils, returnOne, emptyFunction } from '../../../Utils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; @@ -19,7 +18,6 @@ import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocAnnotatableComponent } from '../DocComponent'; -import { InkingControl } from '../InkingControl'; import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; @@ -28,9 +26,14 @@ import { SearchUtil } from '../../util/SearchUtil'; import { ClientRecommender } from '../../ClientRecommender'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { documentSchema } from '../../../new_fields/documentSchemas'; -import { Id } from '../../../new_fields/FieldSymbols'; -var requestImageSize = require('../../util/request-image-size'); -var path = require('path'); +import { Id, Copy } from '../../../new_fields/FieldSymbols'; +import { TraceMobx } from '../../../new_fields/util'; +import { SelectionManager } from '../../util/SelectionManager'; +import { cache } from 'sharp'; +import { ObjectField } from '../../../new_fields/ObjectField'; +import { Networking } from '../../Network'; +const requestImageSize = require('../../util/request-image-size'); +const path = require('path'); const { Howl } = require('howler'); @@ -41,7 +44,6 @@ library.add(faFileAudio, faAsterisk); export const pageSchema = createSchema({ curPage: "number", fitWidth: "boolean", - rotation: "number", googlePhotosUrl: "string", googlePhotosTags: "string" }); @@ -58,6 +60,13 @@ declare class MediaRecorder { type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>; const ImageDocument = makeInterface(pageSchema, documentSchema); +const uploadIcons = { + idle: "downarrow.png", + loading: "loading.gif", + success: "greencheck.png", + failure: "redx.png" +}; + @observer export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocument>(ImageDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } @@ -65,33 +74,40 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum private _dropDisposer?: DragManager.DragDropDisposer; @observable private _audioState = 0; @observable static _showControls: boolean; + @observable uploadIcon = uploadIcons.idle; protected createDropTarget = (ele: HTMLDivElement) => { this._dropDisposer && this._dropDisposer(); - ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } })); + ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this))); } @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - if (de.data instanceof DragManager.DocumentDragData) { - if (de.mods === "AltKey" && de.data.draggedDocuments.length && de.data.draggedDocuments[0].data instanceof ImageField) { - Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new ImageField(de.data.draggedDocuments[0].data.url); - e.stopPropagation(); + if (de.complete.docDragData) { + if (de.metaKey) { + de.complete.docDragData.droppedDocuments.forEach(action((drop: Doc) => { + Doc.AddDocToList(this.dataDoc, this.props.fieldKey + "-alternates", drop); + e.stopPropagation(); + })); + } else if (de.altKey || !this.dataDoc[this.props.fieldKey]) { + const layoutDoc = de.complete.docDragData?.draggedDocuments[0]; + const targetField = Doc.LayoutFieldKey(layoutDoc); + if (layoutDoc?.[DataSym][targetField] instanceof ImageField) { + this.dataDoc[this.props.fieldKey] = ObjectField.MakeCopy(layoutDoc[DataSym][targetField] as ImageField); + this.dataDoc[this.props.fieldKey + "-nativeWidth"] = NumCast(layoutDoc[DataSym][targetField + "-nativeWidth"]); + this.dataDoc[this.props.fieldKey + "-nativeHeight"] = NumCast(layoutDoc[DataSym][targetField + "-nativeHeight"]); + e.stopPropagation(); + } } - de.mods === "MetaKey" && de.data.droppedDocuments.forEach(action((drop: Doc) => { - this.extensionDoc && Doc.AddDocToList(Doc.GetProto(this.extensionDoc), "Alternates", drop); - e.stopPropagation(); - })); } } recordAudioAnnotation = () => { let gumStream: any; let recorder: any; - let self = this; - const extensionDoc = this.extensionDoc; - extensionDoc && navigator.mediaDevices.getUserMedia({ + const self = this; + navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) { gumStream = stream; @@ -99,18 +115,18 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum recorder.ondataavailable = async function (e: any) { const formData = new FormData(); formData.append("file", e.data); - const res = await fetch(Utils.prepend(RouteStore.upload), { + const res = await fetch(Utils.prepend("/uploadFormData"), { method: 'POST', body: formData }); const files = await res.json(); const url = Utils.prepend(files[0].path); // upload to server with known URL - let audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", width: 200, height: 32 }); + const audioDoc = Docs.Create.AudioDocument(url, { title: "audio test", _width: 200, _height: 32 }); audioDoc.treeViewExpandedView = "layout"; - let audioAnnos = Cast(extensionDoc.audioAnnotations, listSpec(Doc)); + const audioAnnos = Cast(this.dataDoc[this.props.fieldKey + "-audioAnnotations"], listSpec(Doc)); if (audioAnnos === undefined) { - extensionDoc.audioAnnotations = new List([audioDoc]); + this.dataDoc[this.props.fieldKey + "-audioAnnotations"] = new List([audioDoc]); } else { audioAnnos.push(audioDoc); } @@ -127,26 +143,26 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum @undoBatch rotate = action(() => { - let nw = this.Document.nativeWidth; - let nh = this.Document.nativeHeight; - let w = this.Document.width; - let h = this.Document.height; - this.Document.rotation = ((this.Document.rotation || 0) + 90) % 360; - this.Document.nativeWidth = nh; - this.Document.nativeHeight = nw; - this.Document.width = h; - this.Document.height = w; + const nw = NumCast(this.Document[this.props.fieldKey + "-nativeWidth"]); + const nh = NumCast(this.Document[this.props.fieldKey + "-nativeHeight"]); + const w = this.Document._width; + const h = this.Document._height; + this.dataDoc[this.props.fieldKey + "-rotation"] = (NumCast(this.dataDoc[this.props.fieldKey + "-rotation"]) + 90) % 360; + this.dataDoc[this.props.fieldKey + "-nativeWidth"] = nh; + this.dataDoc[this.props.fieldKey + "-nativeHeight"] = nw; + this.Document._width = h; + this.Document._height = w; }); specificContextMenu = (e: React.MouseEvent): void => { const field = Cast(this.Document[this.props.fieldKey], ImageField); if (field) { - let funcs: ContextMenuProps[] = []; + const funcs: ContextMenuProps[] = []; funcs.push({ description: "Copy path", event: () => Utils.CopyText(field.url.href), icon: "expand-arrows-alt" }); funcs.push({ description: "Rotate", event: this.rotate, icon: "expand-arrows-alt" }); - let existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers..."); - let modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; + const existingAnalyze = ContextMenu.Instance.findByDescription("Analyzers..."); + const modes: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; modes.push({ description: "Generate Tags", event: this.generateMetadata, icon: "tag" }); modes.push({ description: "Find Faces", event: this.extractFaces, icon: "camera" }); //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" }); @@ -157,33 +173,33 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum } extractFaces = () => { - let converter = (results: any) => { - let faceDocs = new List<Doc>(); + const converter = (results: any) => { + const faceDocs = new List<Doc>(); results.reduce((face: CognitiveServices.Image.Face, faceDocs: List<Doc>) => faceDocs.push(Docs.Get.DocumentHierarchyFromJson(face, `Face: ${face.faceId}`)!), new List<Doc>()); return faceDocs; }; - this.url && this.extensionDoc && CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["faces"], this.url, Service.Face, converter); + this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.props.fieldKey + "-faces"], this.url, Service.Face, converter); } generateMetadata = (threshold: Confidence = Confidence.Excellent) => { - let converter = (results: any) => { - let tagDoc = new Doc; - let tagsList = new List(); + const converter = (results: any) => { + const tagDoc = new Doc; + const tagsList = new List(); results.tags.map((tag: Tag) => { tagsList.push(tag.name); - let sanitized = tag.name.replace(" ", "_"); + const sanitized = tag.name.replace(" ", "_"); tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`); }); - this.extensionDoc && (this.extensionDoc.generatedTags = tagsList); + this.dataDoc[this.props.fieldKey + "-generatedTags"] = tagsList; tagDoc.title = "Generated Tags Doc"; tagDoc.confidence = threshold; return tagDoc; }; - this.url && this.extensionDoc && CognitiveServices.Image.Appliers.ProcessImage(this.extensionDoc, ["generatedTagsDoc"], this.url, Service.ComputerVision, converter); + this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.props.fieldKey + "-generatedTagsDoc"], this.url, Service.ComputerVision, converter); } @computed private get url() { - let data = Cast(this.dataDoc[this.props.fieldKey], ImageField); + const data = Cast(this.dataDoc[this.props.fieldKey], ImageField); return data ? data.url.href : undefined; } @@ -196,7 +212,7 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum } else if (!(lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) { return url.href;//Why is this here } - let ext = path.extname(url.href); + const ext = path.extname(url.href); const suffix = this.props.renderDepth < 1 ? "_o" : this._curSuffix; return url.href.replace(ext, suffix + ext); } @@ -209,37 +225,54 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum if (this._curSuffix === "_m") this._mediumRetryCount++; if (this._curSuffix === "_l") this._largeRetryCount++; } - @action onError = () => { - let timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; + @action onError = (error: any) => { + const timeout = this._curSuffix === "_s" ? this._smallRetryCount : this._curSuffix === "_m" ? this._mediumRetryCount : this._largeRetryCount; if (timeout < 10) { - setTimeout(this.retryPath, Math.min(10000, timeout * 5)); + // setTimeout(this.retryPath, 500); + } + const original = StrCast(this.dataDoc.originalUrl); + if (error.type === "error" && original) { + this.dataDoc[this.props.fieldKey] = new ImageField(original); } } _curSuffix = "_m"; - resize = (srcpath: string) => { - requestImageSize(srcpath) - .then((size: any) => { - let rotation = NumCast(this.dataDoc.rotation) % 180; - let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; - let aspect = realsize.height / realsize.width; - if (this.Document.width && (Math.abs(1 - NumCast(this.Document.height) / NumCast(this.Document.width) / (realsize.height / realsize.width)) > 0.1)) { - setTimeout(action(() => { - this.Document.height = this.Document[WidthSym]() * aspect; - this.Document.nativeHeight = realsize.height; - this.Document.nativeWidth = realsize.width; - }), 0); - } + resize = (imgPath: string) => { + const cachedNativeSize = { + width: NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"]), + height: NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"]) + }; + const cachedImgPath = this.dataDoc[this.props.fieldKey + "-imgPath"]; + if (!cachedNativeSize.width || !cachedNativeSize.height || imgPath !== cachedImgPath) { + (!this.layoutDoc.isTemplateDoc || this.dataDoc !== this.layoutDoc) && requestImageSize(imgPath).then((inquiredSize: any) => { + const rotation = NumCast(this.dataDoc[this.props.fieldKey + "-rotation"]) % 180; + const rotatedNativeSize = rotation === 90 || rotation === 270 ? { height: inquiredSize.width, width: inquiredSize.height } : inquiredSize; + const rotatedAspect = rotatedNativeSize.height / rotatedNativeSize.width; + const docAspect = this.Document[HeightSym]() / this.Document[WidthSym](); + setTimeout(action(() => { + if (this.Document[WidthSym]() && (!cachedNativeSize.width || !cachedNativeSize.height || Math.abs(1 - docAspect / rotatedAspect) > 0.1)) { + this.Document._height = this.Document[WidthSym]() * rotatedAspect; + this.dataDoc[this.props.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = rotatedNativeSize.width; + this.dataDoc[this.props.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = rotatedNativeSize.height; + } + this.dataDoc[this.props.fieldKey + "-imgPath"] = imgPath; + }), 0); }) - .catch((err: any) => console.log(err)); + .catch((err: any) => console.log(err)); + } else if (this.Document._nativeHeight !== cachedNativeSize.width || this.Document._nativeWidth !== cachedNativeSize.height) { + !(this.Document[StrCast(this.props.Document.layoutKey)] instanceof Doc) && setTimeout(() => { + this.Document._nativeWidth = cachedNativeSize.width; + this.Document._nativeHeight = cachedNativeSize.height; + }, 0); + } } @action onPointerEnter = () => { - let self = this; - let audioAnnos = this.extensionDoc && DocListCast(this.extensionDoc.audioAnnotations); + const self = this; + const audioAnnos = DocListCast(this.dataDoc[this.props.fieldKey + "-audioAnnotations"]); if (audioAnnos && audioAnnos.length && this._audioState === 0) { - let anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)]; + const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)]; anno.data instanceof AudioField && new Howl({ src: [anno.data.url.href], format: ["mp3"], @@ -270,80 +303,130 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum return !tags ? (null) : (<img id={"google-tags"} src={"/assets/google_tags.png"} />); } - @computed get content() { - const extensionDoc = this.extensionDoc; - if (!extensionDoc) return (null); - // let transform = this.props.ScreenToLocalTransform().inverse(); - let pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; - // var [sptX, sptY] = transform.transformPoint(0, 0); - // let [bptX, bptY] = transform.transformPoint(pw, this.props.PanelHeight()); - // let w = bptX - sptX; - - let nativeWidth = (this.Document.nativeWidth || pw); - let nativeHeight = (this.Document.nativeHeight || 0); + @computed + private get considerDownloadIcon() { + const data = this.dataDoc[this.props.fieldKey]; + if (!(data instanceof ImageField)) { + return (null); + } + const primary = data.url.href; + if (primary.includes(window.location.origin)) { + return (null); + } + return ( + <img + id={"upload-icon"} + src={`/assets/${this.uploadIcon}`} + onClick={async () => { + const { dataDoc } = this; + const { success, failure, idle, loading } = uploadIcons; + runInAction(() => this.uploadIcon = loading); + const [{ clientAccessPath }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] }); + dataDoc.originalUrl = primary; + let succeeded = true; + let data: ImageField | undefined; + try { + data = new ImageField(Utils.prepend(clientAccessPath)); + } catch { + succeeded = false; + } + runInAction(() => this.uploadIcon = succeeded ? success : failure); + setTimeout(action(() => { + this.uploadIcon = idle; + if (data) { + dataDoc[this.props.fieldKey] = data; + } + }), 2000); + }} + /> + ); + } + + @computed get nativeSize() { + const pw = typeof this.props.PanelWidth === "function" ? this.props.PanelWidth() : typeof this.props.PanelWidth === "number" ? (this.props.PanelWidth as any) as number : 50; + const nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], pw); + const nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], 1); + return { nativeWidth, nativeHeight }; + } + + @computed get paths() { let paths = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")]; // this._curSuffix = ""; // if (w > 20) { - let alts = DocListCast(extensionDoc.Alternates); - let altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url)); - let field = this.dataDoc[this.props.fieldKey]; + const alts = DocListCast(this.dataDoc[this.props.fieldKey + "-alternates"]); + const altpaths = alts.filter(doc => doc.data instanceof ImageField).map(doc => this.choosePath((doc.data as ImageField).url)); + const field = this.dataDoc[this.props.fieldKey]; // if (w < 100 && this._smallRetryCount < 10) this._curSuffix = "_s"; // else if (w < 600 && this._mediumRetryCount < 10) this._curSuffix = "_m"; // else if (this._largeRetryCount < 10) this._curSuffix = "_l"; if (field instanceof ImageField) paths = [this.choosePath(field.url)]; paths.push(...altpaths); - // } - let interactive = InkingControl.Instance.selectedTool || this.Document.isBackground ? "" : "-interactive"; - let rotation = NumCast(this.Document.rotation, 0); - let aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1; - let shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; - let srcpath = paths[Math.min(paths.length - 1, (this.Document.curPage || 0))]; - let fadepath = paths[Math.min(paths.length - 1, 1)]; + return paths; + } + + @computed get content() { + TraceMobx(); + + const srcpath = this.paths[NumCast(this.props.Document.curPage, 0)]; + const fadepath = this.paths[Math.min(1, this.paths.length - 1)]; + const { nativeWidth, nativeHeight } = this.nativeSize; + const rotation = NumCast(this.dataDoc[this.props.fieldKey + "-rotation"]); + const aspect = (rotation % 180) ? this.Document[HeightSym]() / this.Document[WidthSym]() : 1; + const shift = (rotation % 180) ? (nativeHeight - nativeWidth / aspect) / 2 : 0; !this.Document.ignoreAspect && this.resize(srcpath); - return ( - <div className={`imageBox-cont${interactive}`} key={this.props.Document[Id]} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}> - <div id="cf"> - <img - key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys - src={srcpath} - style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }} - width={nativeWidth} - ref={this._imgRef} - onError={this.onError} /> - {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker"> <img className="imageBox-fadeaway" + return <div className="imageBox-cont" key={this.props.Document[Id]} ref={this.createDropTarget} onContextMenu={this.specificContextMenu}> + <div className="imageBox-fader" > + <img key={this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys + src={srcpath} + style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }} + width={nativeWidth} + ref={this._imgRef} + onError={this.onError} /> + {fadepath === srcpath ? (null) : <div className="imageBox-fadeBlocker"> + <img className="imageBox-fadeaway" key={"fadeaway" + this._smallRetryCount + (this._mediumRetryCount << 4) + (this._largeRetryCount << 8)} // force cache to update on retrys src={fadepath} - style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})` }} + style={{ transform: `translate(0px, ${shift}px) rotate(${rotation}deg) scale(${aspect})`, }} width={nativeWidth} ref={this._imgRef} onError={this.onError} /></div>} - </div> - <div className="imageBox-audioBackground" - onPointerDown={this.audioDown} - onPointerEnter={this.onPointerEnter} - style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }} - > - <FontAwesomeIcon className="imageBox-audioFont" - style={{ color: [DocListCast(extensionDoc.audioAnnotations).length ? "blue" : "gray", "green", "red"][this._audioState] }} icon={!DocListCast(extensionDoc.audioAnnotations).length ? "microphone" : faFileAudio} size="sm" /> - </div> - {this.considerGooglePhotosLink()} - <FaceRectangles document={extensionDoc} color={"#0000FF"} backgroundColor={"#0000FF"} /> - </div>); + </div> + <div className="imageBox-audioBackground" + onPointerDown={this.audioDown} + onPointerEnter={this.onPointerEnter} + style={{ height: `calc(${.1 * nativeHeight / nativeWidth * 100}%)` }} + > + <FontAwesomeIcon className="imageBox-audioFont" + style={{ color: [DocListCast(this.dataDoc[this.props.fieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._audioState] }} + icon={!DocListCast(this.dataDoc[this.props.fieldKey + "-audioAnnotations"]).length ? "microphone" : faFileAudio} size="sm" /> + </div> + {this.considerDownloadIcon} + {this.considerGooglePhotosLink()} + <FaceRectangles document={this.dataDoc} color={"#0000FF"} backgroundColor={"#0000FF"} /> + </div>; } + contentFunc = () => [this.content]; render() { - return (<div className={"imageBox-container"} onContextMenu={this.specificContextMenu}> + TraceMobx(); + const dragging = !SelectionManager.GetIsDragging() ? "" : "-dragging"; + return (<div className={`imageBox${dragging}`} onContextMenu={this.specificContextMenu} + style={{ + transform: `scale(${this.props.ContentScaling()})`, + width: `${100 / this.props.ContentScaling()}%`, + height: `${100 / this.props.ContentScaling()}%` + }} > <CollectionFreeFormView {...this.props} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth} - annotationsKey={this.annotationsKey} + annotationsKey={this.annotationKey} isAnnotationOverlay={true} focus={this.props.focus} isSelected={this.props.isSelected} select={emptyFunction} - active={this.active} + active={this.annotationsActive} ContentScaling={returnOne} whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} @@ -351,11 +434,10 @@ export class ImageBox extends DocAnnotatableComponent<FieldViewProps, ImageDocum addDocument={this.addDocument} CollectionView={undefined} ScreenToLocalTransform={this.props.ScreenToLocalTransform} - ruleProvider={undefined} renderDepth={this.props.renderDepth + 1} ContainingCollectionDoc={this.props.ContainingCollectionDoc} chromeCollapsed={true}> - {() => [this.content]} + {this.contentFunc} </CollectionFreeFormView> </div >); } |