diff options
author | usodhi <61431818+usodhi@users.noreply.github.com> | 2020-07-01 16:56:14 +0530 |
---|---|---|
committer | usodhi <61431818+usodhi@users.noreply.github.com> | 2020-07-01 16:56:14 +0530 |
commit | 0caf34bf91e51b7c1459b8b069daf3ec69ead855 (patch) | |
tree | cf0dd0b095687cfb70de76e5266f9facad0fd831 | |
parent | 467afe6346bf3188461721fa78eed0f9ac214da7 (diff) | |
parent | a3ce7e180110df7e5908b93418b2dbff80aa89b5 (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into acls_uv
-rw-r--r-- | src/Utils.ts | 16 | ||||
-rw-r--r-- | src/client/documents/Documents.ts | 30 | ||||
-rw-r--r-- | src/client/util/DocumentManager.ts | 8 | ||||
-rw-r--r-- | src/client/views/ContextMenu.tsx | 2 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 92 | ||||
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 6 | ||||
-rw-r--r-- | src/client/views/PreviewCursor.tsx | 21 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSubView.tsx | 61 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 12 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/FontIconBox.tsx | 9 |
11 files changed, 149 insertions, 111 deletions
diff --git a/src/Utils.ts b/src/Utils.ts index dba802f98..a01a94134 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -7,6 +7,22 @@ import { ColorState } from 'react-color'; export namespace Utils { export let DRAG_THRESHOLD = 4; + export function readUploadedFileAsText(inputFile: File) { + const temporaryFileReader = new FileReader(); + + return new Promise((resolve, reject) => { + temporaryFileReader.onerror = () => { + temporaryFileReader.abort(); + reject(new DOMException("Problem parsing input file.")); + }; + + temporaryFileReader.onload = () => { + resolve(temporaryFileReader.result); + }; + temporaryFileReader.readAsText(inputFile); + }); + } + export function GenerateGuid(): string { return v4(); } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4c27cf47d..85da621e0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,5 +1,5 @@ import { runInAction } from "mobx"; -import { extname } from "path"; +import { extname, basename } from "path"; import { DateField } from "../../fields/DateField"; import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym } from "../../fields/Doc"; import { HtmlField } from "../../fields/HtmlField"; @@ -48,6 +48,8 @@ import { PresElementBox } from "../views/presentationview/PresElementBox"; import { RecommendationsBox } from "../views/RecommendationsBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; +import { Networking } from "../Network"; +import { Upload } from "../../server/SharedMediaTypes"; const path = require('path'); export interface DocumentOptions { @@ -1104,6 +1106,32 @@ export namespace DocUtils { }); return optionsCollection; } + + export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { + const generatedDocuments: Doc[] = []; + for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) { + if (result instanceof Error) { + alert(`Upload failed: ${result.message}`); + return []; + } + const full = { ...options, _width: 400, title: name }; + const pathname = Utils.prepend(result.accessPaths.agnostic.client); + const doc = await DocUtils.DocumentFromType(type, pathname, full); + if (!doc) { + continue; + } + const proto = Doc.GetProto(doc); + proto.text = result.rawText; + proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""); + if (Upload.isImageInformation(result)) { + proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400; + proto["data-nativeHeight"] = (result.nativeWidth > result.nativeHeight) ? 400 : 400 / (result.nativeWidth / result.nativeHeight); + proto.contentSize = result.contentSize; + } + generatedDocuments.push(doc); + } + return generatedDocuments; + } } Scripting.addGlobal("Docs", Docs); diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index fb5d1717e..1fa5faeb3 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -146,7 +146,7 @@ export class DocumentManager { }; const docView = getFirstDocView(targetDoc, originatingDoc); let annotatedDoc = await Cast(targetDoc.annotationOn, Doc); - if (annotatedDoc) { + if (annotatedDoc && !linkDoc?.isPushpin) { const first = getFirstDocView(annotatedDoc); if (first) { annotatedDoc = first.props.Document; @@ -156,7 +156,11 @@ export class DocumentManager { } } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? - docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); + if (linkDoc?.isPushpin) docView.props.Document.hidden = !docView.props.Document.hidden; + else { + docView.props.Document.hidden && (docView.props.Document.hidden = undefined); + docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); + } highlight(); } else { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 941d7b44a..07f7b8e6d 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -155,9 +155,11 @@ export class ContextMenu extends React.Component { @action closeMenu = () => { + const wasOpen = this._display; this.clearItems(); this._display = false; this._shouldDisplay = false; + return wasOpen; } @computed get filteredItems(): (OriginalMenuProps | string[])[] { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4fda10926..a45ef8862 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -600,52 +600,54 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> zIndex: SelectionManager.SelectedDocuments().length > 1 ? 900 : 0, }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} > </div> - <div className="documentDecorations-container" ref={this.setTextBar} style={{ - width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px", - height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px", - left: bounds.x - this._resizeBorderWidth / 2, - top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, - }}> - {maximizeIcon} - {titleArea} - {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : - <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}> - {"_"} - </div>} - <div className="documentDecorations-closeButton" title="Open Document in Tab" onPointerDown={this.onMaximizeDown}> - {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."} + {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <> + <div className="documentDecorations-container" key="container" ref={this.setTextBar} style={{ + width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px", + height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px", + left: bounds.x - this._resizeBorderWidth / 2, + top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, + }}> + {maximizeIcon} + {titleArea} + {SelectionManager.SelectedDocuments().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : + <div className="documentDecorations-iconifyButton" title={`${seldoc.finalLayoutKey.includes("icon") ? "De" : ""}Iconify Document`} onPointerDown={this.onIconifyDown}> + {"_"} + </div>} + <div className="documentDecorations-closeButton" title="Open Document in Tab" onPointerDown={this.onMaximizeDown}> + {SelectionManager.SelectedDocuments().length === 1 ? DocumentDecorations.DocumentIcon(StrCast(seldoc.props.Document.layout, "...")) : "..."} + </div> + <div id="documentDecorations-rotation" className="documentDecorations-rotation" + onPointerDown={this.onRotateDown}> ⟲ </div> + <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-topResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-centerCont"></div> + <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" + onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> + {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : + <div id="documentDecorations-levelSelector" className="documentDecorations-selector" + title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}> + <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" /> + </div>} + <div id="documentDecorations-borderRadius" className="documentDecorations-radius" + onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div> + + </div > + <div className="link-button-container" key="links" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}> + <DocumentButtonBar views={SelectionManager.SelectedDocuments} /> </div> - <div id="documentDecorations-rotation" className="documentDecorations-rotation" - onPointerDown={this.onRotateDown}> ⟲ </div> - <div id="documentDecorations-topLeftResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-topResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-topRightResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-leftResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-centerCont"></div> - <div id="documentDecorations-rightResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-bottomLeftResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-bottomResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - <div id="documentDecorations-bottomRightResizer" className="documentDecorations-resizer" - onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()}></div> - {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) : - <div id="documentDecorations-levelSelector" className="documentDecorations-selector" - title="tap to select containing document" onPointerDown={this.onSelectorUp} onContextMenu={e => e.preventDefault()}> - <FontAwesomeIcon className="documentdecorations-times" icon={faArrowAltCircleUp} size="lg" /> - </div>} - <div id="documentDecorations-borderRadius" className="documentDecorations-radius" - onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}></div> - - </div > - <div className="link-button-container" style={{ left: bounds.x - this._resizeBorderWidth / 2 + 10, top: bounds.b + this._resizeBorderWidth / 2 }}> - <DocumentButtonBar views={SelectionManager.SelectedDocuments} /> - </div> + </>} </div > ); } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index e3546dece..c85849adb 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -20,6 +20,7 @@ import { MainView } from "./MainView"; import { DocumentView } from "./nodes/DocumentView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import PDFMenu from "./pdf/PDFMenu"; +import { ContextMenu } from "./ContextMenu"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>; @@ -81,16 +82,17 @@ export default class KeyManager { DocumentLinksButton.StartLink = undefined; const main = MainView.Instance; Doc.SetSelectedTool(InkTool.None); + var doDeselect = true; if (main.isPointerDown) { DragManager.AbortDrag(); } else { if (CollectionDockingView.Instance.HasFullScreen()) { CollectionDockingView.Instance.CloseFullScreen(); } else { - SelectionManager.DeselectAll(); + doDeselect = !ContextMenu.Instance.closeMenu(); } } - SelectionManager.DeselectAll(); + doDeselect && SelectionManager.DeselectAll(); DictationManager.Controls.stop(); // RecommendationsBox.Instance.closeMenu(); GoogleAuthenticationManager.Instance.cancel(); diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 1ab99881d..6583589f3 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -3,13 +3,18 @@ import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; import "./PreviewCursor.scss"; -import { Docs } from '../documents/Documents'; +import { Docs, DocUtils } from '../documents/Documents'; import { Doc } from '../../fields/Doc'; import { Transform } from "../util/Transform"; import { DocServer } from '../DocServer'; -import { undoBatch } from '../util/UndoManager'; +import { undoBatch, UndoManager } from '../util/UndoManager'; import { NumCast } from '../../fields/Types'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; +import * as rp from 'request-promise'; +import { Utils } from '../../Utils'; +import { Networking } from '../Network'; +import { Upload } from '../../server/SharedMediaTypes'; +import { basename } from 'path'; @observer export class PreviewCursor extends React.Component<{}> { @@ -26,7 +31,7 @@ export class PreviewCursor extends React.Component<{}> { document.addEventListener("paste", this.paste); } - paste = (e: ClipboardEvent) => { + paste = async (e: ClipboardEvent) => { if (PreviewCursor.Visible && e.clipboardData) { const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]); runInAction(() => PreviewCursor.Visible = false); @@ -104,6 +109,16 @@ export class PreviewCursor extends React.Component<{}> { x: newPoint[0], y: newPoint[1], })))(); + } else if (e.clipboardData.items.length) { + const batch = UndoManager.StartBatch("collection view drop"); + const files: File[] = []; + for (let i = 0; i < e.clipboardData.items.length; i++) { + const file = e.clipboardData.items[i].getAsFile(); + file && files.push(file); + } + const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] }); + generatedDocuments.forEach(PreviewCursor._addDocument); + batch.end(); } } } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d107db86b..ecd16d22f 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -225,7 +225,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: 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.addDocument(addedDocs) : true; - added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res; + added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document) || de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res; } else { added = this.addDocument(docDragData.droppedDocuments); } @@ -238,21 +238,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: } return false; } - readUploadedFileAsText = (inputFile: File) => { - const temporaryFileReader = new FileReader(); - return new Promise((resolve, reject) => { - temporaryFileReader.onerror = () => { - temporaryFileReader.abort(); - reject(new DOMException("Problem parsing input file.")); - }; - - temporaryFileReader.onload = () => { - resolve(temporaryFileReader.result); - }; - temporaryFileReader.readAsText(inputFile); - }); - } @undoBatch @action protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) { @@ -271,8 +257,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: e.stopPropagation(); e.preventDefault(); - const { addDocument } = this; - if (!addDocument) { + if (!this.addDocument) { alert("this.props.addDocument does not exist. Aborting drop operation."); return; } @@ -286,14 +271,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: DocServer.GetRefField(docid).then(f => { if (f instanceof Doc) { if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView - (f instanceof Doc) && addDocument(f); + (f instanceof Doc) && this.addDocument(f); } }); } else { - addDocument(Docs.Create.WebDocument(href, { ...options, title: href })); + this.addDocument(Docs.Create.WebDocument(href, { ...options, title: href })); } } else if (text) { - addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 })); + this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 })); } return; } @@ -313,7 +298,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: if (source.startsWith("http")) { const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 }); ImageUtils.ExtractExif(doc); - addDocument(doc); + this.addDocument(doc); } return; } else { @@ -360,7 +345,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: if (text) { if (text.includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) { const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/").split("&")[0]; - addDocument(Docs.Create.VideoDocument(url, { + this.addDocument(Docs.Create.VideoDocument(url, { ...options, title: url, _width: 400, @@ -413,10 +398,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: const file = item.getAsFile(); file?.type && files.push(file); - file?.type === "application/json" && this.readUploadedFileAsText(file).then(result => { + file?.type === "application/json" && Utils.readUploadedFileAsText(file).then(result => { console.log(result); const json = JSON.parse(result as string); - addDocument(Docs.Create.TreeDocument( + this.addDocument(Docs.Create.TreeDocument( json["rectangular-puzzle"].crossword.clues[0].clue.map((c: any) => { const label = Docs.Create.LabelDocument({ title: c["#text"], _width: 120, _height: 20 }); const proto = Doc.GetProto(label); @@ -428,38 +413,18 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: }); } } - for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) { - if (result instanceof Error) { - alert(`Upload failed: ${result.message}`); - return; - } - const full = { ...options, _width: 400, title: name }; - const pathname = Utils.prepend(result.accessPaths.agnostic.client); - const doc = await DocUtils.DocumentFromType(type, pathname, full); - if (!doc) { - continue; - } - const proto = Doc.GetProto(doc); - proto.text = result.rawText; - proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""); - if (Upload.isImageInformation(result)) { - proto["data-nativeWidth"] = (result.nativeWidth > result.nativeHeight) ? 400 * result.nativeWidth / result.nativeHeight : 400; - proto["data-nativeHeight"] = (result.nativeWidth > result.nativeHeight) ? 400 : 400 / (result.nativeWidth / result.nativeHeight); - proto.contentSize = result.contentSize; - } - generatedDocuments.push(doc); - } + generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options)); if (generatedDocuments.length) { const set = generatedDocuments.length > 1 && generatedDocuments.map(d => DocUtils.iconify(d)); if (set) { - addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!); + this.addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!); } else { - generatedDocuments.forEach(addDocument); + generatedDocuments.forEach(this.addDocument); } completed?.(); } else { if (text && !text.includes("https://")) { - addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 })); + this.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 })); } } batch.end(); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index dab82185a..705fbdd34 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -141,15 +141,17 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus } else { added.map(doc => { const context = Cast(doc.context, Doc, null); - if (context && (context?.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { - const pushpin = Docs.Create.FreeformDocument([], { - title: "pushpin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), - _width: 10, _height: 10, _backgroundColor: "red", isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null) + if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG) && + !DocListCast(doc.links).some(d => d.isPushpin)) { + const pushpin = Docs.Create.FontIconDocument({ + icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), _backgroundColor: "#0000003d", color: "#ACCEF7", + _width: 15, _height: 15, _xPadding: 0, isLinkButton: true, displayTimecode: Cast(doc.displayTimecode, "number", null) }); Doc.AddDocToList(context, Doc.LayoutFieldKey(context) + "-annotations", pushpin); - DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin"); + const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin"); const first = DocListCast(pushpin.links).find(d => d instanceof Doc); first && (first.hidden = true); + pushpinLink && (Doc.GetProto(pushpinLink).isPushpin = true); doc.displayTimecode = undefined; } doc.context = this.props.Document; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 21b6d8310..09eeaee36 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1169,8 +1169,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } render() { - if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null); if (!(this.props.Document instanceof Doc)) return (null); + if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null); + if (this.props.Document.hidden) return (null); const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); const finalOpacity = this.props.opacity ? this.props.opacity() : opacity; diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index cf0b16c7c..5e8dd2497 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -5,7 +5,7 @@ import { createSchema, makeInterface } from '../../../fields/Schema'; import { DocComponent } from '../DocComponent'; import './FontIconBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; -import { StrCast, Cast } from '../../../fields/Types'; +import { StrCast, Cast, NumCast } from '../../../fields/Types'; import { Utils } from "../../../Utils"; import { runInAction, observable, reaction, IReactionDisposer } from 'mobx'; import { Doc } from '../../../fields/Doc'; @@ -59,13 +59,14 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>( render() { const referenceDoc = (this.layoutDoc.dragFactory instanceof Doc ? this.layoutDoc.dragFactory : this.layoutDoc); - const referenceLayout = Doc.Layout(referenceDoc); + const refLayout = Doc.Layout(referenceDoc); return <button className="fontIconBox-outerDiv" title={StrCast(this.layoutDoc.title)} ref={this._ref} onContextMenu={this.specificContextMenu} style={{ - background: StrCast(referenceLayout.backgroundColor), + padding: Cast(this.layoutDoc._xPadding, "number", null), + background: StrCast(refLayout._backgroundColor, StrCast(refLayout.backgroundColor)), boxShadow: this.layoutDoc.ischecked ? `4px 4px 12px black` : undefined }}> - <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={this._foregroundColor} size="sm" /> + <FontAwesomeIcon className="fontIconBox-icon" icon={this.dataDoc.icon as any} color={StrCast(this.layoutDoc.color, this._foregroundColor)} size="sm" /> {!this.rootDoc.label ? (null) : <div className="fontIconBox-label"> {StrCast(this.rootDoc.label).substring(0, 5)} </div>} </button>; } |