diff options
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 2 | ||||
-rw-r--r-- | src/client/util/DragManager.ts | 12 | ||||
-rw-r--r-- | src/client/views/DocComponent.tsx | 2 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 14 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackingView.tsx | 15 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 21 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentLinksButton.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.scss | 13 | ||||
-rw-r--r-- | src/client/views/nodes/PDFBox.tsx | 21 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.scss | 23 | ||||
-rw-r--r-- | src/client/views/nodes/WebBox.tsx | 195 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 17 | ||||
-rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 6 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 16 |
15 files changed, 255 insertions, 106 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e55527f84..baa7aa43b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -816,7 +816,7 @@ export namespace Docs { export function PdfDocument(url: string, options: DocumentOptions = {}) { const pdfProto = Prototypes.get(DocumentType.PDF); pdfProto._fitWidth = true; // backward compatibility -- can be removed after db is reset - return InstanceFromProto(pdfProto, new PdfField(new URL(url)), { _viewType: "stacking", ...options }); + return InstanceFromProto(pdfProto, new PdfField(new URL(url)), { ...options }); } export function WebDocument(url: string, options: DocumentOptions = {}) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index aafb986be..23cb9634b 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -772,7 +772,7 @@ export class CurrentUserUtils { static async setupFilesystem(doc: Doc) { await doc.myFilesystem; if (doc.myFilesystem === undefined) { - doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "file orphans", _stayInCollection: true, system: true, isFolder: true }); + doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true }); doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileRoot as Doc, doc.myFileOrphans as Doc], { title: "My Documents", _height: 100, diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 437fea0ea..0d154bc3a 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -12,6 +12,7 @@ import { Docs, DocUtils } from "../documents/Documents"; import * as globalCssVariables from "../views/globalCssVariables.scss"; import { UndoManager } from "./UndoManager"; import { SnappingManager } from "./SnappingManager"; +import { DocumentView } from "../views/nodes/DocumentView"; export type dropActionType = "alias" | "copy" | "move" | "same" | "proto" | "none" | undefined; // undefined = move, "same" = move but don't call removeDropProperties export function SetupDrag( @@ -138,13 +139,14 @@ export namespace DragManager { isSelectionMove?: boolean; // indicates that an explicitly selected Document is being dragged. this will suppress onDragStart scripts } export class LinkDragData { - constructor(dragDoc: Doc, linkSourceGetAnchor: () => Doc) { - this.dragDocument = dragDoc; + constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc,) { + this.linkDragView = dragView; this.linkSourceGetAnchor = linkSourceGetAnchor; } - dragDocument: Doc; + get dragDocument() { return this.linkDragView.props.Document; } linkSourceGetAnchor: () => Doc; linkSourceDoc?: Doc; + linkDragView: DocumentView; } export class ColumnDragData { constructor(colKey: SchemaHeaderField) { @@ -253,8 +255,8 @@ export namespace DragManager { } // drags a linker button and creates a link on drop - export function StartLinkDrag(ele: HTMLElement, sourceDoc: Doc, sourceDocGetAnchor: undefined | (() => Doc), downX: number, downY: number, options?: DragOptions) { - StartDrag([ele], new DragManager.LinkDragData(sourceDoc, () => sourceDocGetAnchor?.() ?? sourceDoc), downX, downY, options); + export function StartLinkDrag(ele: HTMLElement, sourceView: DocumentView, sourceDocGetAnchor: undefined | (() => Doc), downX: number, downY: number, options?: DragOptions) { + StartDrag([ele], new DragManager.LinkDragData(sourceView, () => sourceDocGetAnchor?.() ?? sourceView.rootDoc), downX, downY, options); } // drags a column from a schema view diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 589cfafc5..f8aede717 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -201,7 +201,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T } whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); - active = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.props.Document._) && + active = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool === InkTool.None && (this.props.rootSelected(outsideReaction) || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0 || BoolCast((this.layoutDoc as any).forceActive)) ? true : false) annotationsActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None || (this.props.layerProvider?.(this.props.Document) === false && this.props.active()) || (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index c356aa0f5..3dfff8c87 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -245,7 +245,7 @@ export class MainView extends React.Component { getContentsHeight = () => this._panelHeight; @computed get mainDocView() { - return <DocumentView + return <DocumentView key="main" Document={this.mainContainer!} DataDoc={undefined} addDocument={undefined} @@ -273,7 +273,7 @@ export class MainView extends React.Component { } @computed get dockingContent() { - return <div className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }} + return <div key="docking" className={`mainContent-div${this._flyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }} style={{ minWidth: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)`, width: `calc(100% - ${this._flyoutWidth + this.menuPanelWidth() + this.propertiesWidth()}px)` }}> {!this.mainContainer ? (null) : this.mainDocView} </div>; @@ -331,10 +331,10 @@ export class MainView extends React.Component { @computed get flyout() { - return !this._flyoutWidth ? <div className={`mainView-libraryFlyout-out`}> + return !this._flyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}> {this.docButtons} </div> : - <div className="mainView-libraryFlyout" style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} > + <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._flyoutWidth, width: this._flyoutWidth }} > <div className="mainView-contentArea" > <DocumentView Document={this._sidebarContent.proto || this._sidebarContent} @@ -368,7 +368,7 @@ export class MainView extends React.Component { } @computed get menuPanel() { - return <div className="mainView-menuPanel"> + return <div key="menu" className="mainView-menuPanel"> <DocumentView Document={Doc.UserDoc().menuStack as Doc} DataDoc={undefined} @@ -424,7 +424,7 @@ export class MainView extends React.Component { @computed get mainInnerContent() { return <> {this.menuPanel} - <div className={`mainView-innerContent${this.darkScheme ? "-dark" : ""}`}> + <div key="inner" className={`mainView-innerContent${this.darkScheme ? "-dark" : ""}`}> {this.flyout} <div className="mainView-libraryHandle" style={{ display: !this._flyoutWidth ? "none" : undefined, }} onPointerDown={this.onFlyoutPointerDown} > <FontAwesomeIcon icon="chevron-left" color={this.darkScheme ? "white" : "black"} style={{ opacity: "50%" }} size="sm" /> @@ -432,7 +432,7 @@ export class MainView extends React.Component { {this.dockingContent} - <div className="mainView-propertiesDragger" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> + <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.darkScheme ? "white" : "black"} size="sm" /> </div> {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.getContentsHeight()} />} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index df39ed3e1..f3c1c4464 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -12,7 +12,7 @@ import { SchemaHeaderField } from "../../../fields/SchemaHeaderField"; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from "../../../fields/util"; import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils"; -import { DocUtils } from "../../documents/Documents"; +import { DocUtils, Docs } from "../../documents/Documents"; import { DragManager, dropActionType } from "../../util/DragManager"; import { SnappingManager } from "../../util/SnappingManager"; import { Transform } from "../../util/Transform"; @@ -37,6 +37,7 @@ const StackingDocument = makeInterface(collectionSchema, documentSchema); export type collectionStackingViewProps = { chromeStatus?: string; + viewType?: CollectionViewType; NativeWidth?: () => number; NativeHeight?: () => number; }; @@ -60,7 +61,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); } @computed get yMargin() { return this.props.yMargin || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); } @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); } - @computed get isStackingView() { return this.layoutDoc._viewType === CollectionViewType.Stacking; } + @computed get isStackingView() { return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking; } @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; } @computed get showAddAGroup() { return (this.pivotField && (this.chromeStatus !== 'view-mode' && this.chromeStatus !== 'disabled')); } @computed get columnWidth() { @@ -315,7 +316,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height()); if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && (i === this._docXfs.length - 1 || where[1] < pos1[1])) { dropInd = i; - const axis = this.Document._viewType === CollectionViewType.Masonry ? 0 : 1; + const axis = this.isStackingView ? 1 : 0; dropAfter = where[axis] > (pos[axis] + pos1[axis]) / 2 ? 1 : 0; } }); @@ -334,7 +335,13 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, } } } - if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); + else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) { + const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, _fitWidth: true, title: "dropped annotation" }); + this.props.addDocument?.(source); + de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed + e.stopPropagation(); + } + else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData); return false; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 019fc9bae..5c5164d98 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -290,25 +290,22 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @undoBatch internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) { - if (linkDragData.dragDocument === this.props.Document || this.props.Document.annotationOn) return false; - if (!linkDragData.dragDocument.context || StrCast(Cast(linkDragData.dragDocument.context, Doc, null)?.type) === DocumentType.COL) { - // 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 - return false; - } else { - const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" }); - this.props.addDocument?.(source); - de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed - e.stopPropagation(); + if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) { // dragged document is a child of this collection + if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) { // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document + const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" }); + this.props.addDocument?.(source); + de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed + } + e.stopPropagation(); // do nothing if link is dropped into any freeform view parent of dragged document return true; } + return false; } onInternalDrop = (e: Event, de: DragManager.DropEvent) => { const [xp, yp] = this.getTransform().transformPoint(de.x, de.y); if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData, xp, yp); - else if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp); + else if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp); else if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp); return false; } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 3622be5fc..57d1a41b6 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -51,7 +51,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp if (this.props.InMenu && this.props.StartLink) { if (this._linkButton.current !== null) { const linkDrag = UndoManager.StartBatch("Drag Link"); - this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View.props.Document, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, { + this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View, this.props.View.ComponentView?.getAnchor, e.pageX, e.pageY, { dragComplete: dropEv => { if (this.props.View && dropEv.linkDocument) {// dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop !dropEv.linkDocument.linkRelationship && (Doc.GetProto(dropEv.linkDocument).linkRelationship = "hyperlink"); diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 74b331ae1..564873cf5 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -18,6 +18,19 @@ top: 0; left: 0; + .pdfBox-overlayButton-sidebar { + background: #121721; + height: 25px; + width: 25px; + right: 0; + display: flex; + position: absolute; + align-items: center; + justify-content: center; + border-radius: 3px; + pointer-events: all; + } + .pdfBox-pageNums { display: flex; flex-direction: row; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 4e4db323c..e4aa639ff 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -143,7 +143,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum } renderTag = (tag: string) => { const active = StrListCast(this.rootDoc[this.sidebarKey() + "-docFilters"]).includes(`${tag}:${tag}:check`); - return <div className={`pdfbox-filterTag${active ? "-active" : ""}`} + return <div key={tag} className={`pdfbox-filterTag${active ? "-active" : ""}`} onClick={e => Doc.setDocFilter(this.rootDoc, tag, tag, "check", true, this.sidebarKey())}> {tag} </div>; @@ -179,6 +179,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum CollectionView={undefined} ScreenToLocalTransform={this.sidebarTransform} renderDepth={this.props.renderDepth + 1} + viewType={CollectionViewType.Stacking} fieldKey={this.sidebarKey()} pointerEvents={"all"} /> @@ -216,7 +217,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum } }); - whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); setPdfViewer = (pdfViewer: PDFViewer) => { this._pdfViewer = pdfViewer; if (this.initialScrollTarget) { @@ -225,7 +225,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum } } searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value; - + toggleSidebar = () => { + if (this.layoutDoc.nativeWidth === this.layoutDoc[this.fieldKey + "-nativeWidth"]) { + this.layoutDoc.nativeWidth = 250 + NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); + } else { + this.layoutDoc.nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]); + } + this.layoutDoc._width = NumCast(this.layoutDoc._nativeWidth) * (NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) / NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"])) + } settingsPanel() { const pageBtns = <> <button className="pdfBox-overlayButton-back" key="back" title="Page Back" @@ -270,6 +277,10 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum onClick={action(() => this._pageControls = !this._pageControls)} /> {this._pageControls ? pageBtns : (null)} </div> + <button className="pdfBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar" style={{ right: this.sidebarWidth() + 7 }} + onPointerDown={e => e.stopPropagation()} onClick={e => this.toggleSidebar()} > + <FontAwesomeIcon style={{ color: "white" }} icon={"chevron-left"} size="sm" /> + </button> </div>); } @@ -279,6 +290,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Fit Width " + (this.Document._fitWidth ? "Off" : "On"), event: () => this.Document._fitWidth = !this.Document._fitWidth, icon: "expand-arrows-alt" }); funcs.push({ description: "Toggle Annotation View ", event: () => this.Document._showSidebar = !this.Document._showSidebar, icon: "expand-arrows-alt" }); + funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } @@ -306,7 +318,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum </div> </div>; } - isChildActive = (outsideReaction?: boolean) => this._isChildActive; @computed get renderPdfView() { TraceMobx(); const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); @@ -316,12 +327,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} + active={this.active} anchorMenuClick={this.anchorMenuClick} loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined} setPdfViewer={this.setPdfViewer} addDocument={this.addDocument} whenActiveChanged={this.whenActiveChanged} - isChildActive={this.isChildActive} startupLive={true} ContentScaling={this.props.scaling} sidebarWidth={this.sidebarWidth} diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index ca6611a6b..198e7ef10 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -6,6 +6,29 @@ position: relative; display: flex; + + + .webBox-tagList { + display: flex; + flex-direction: row; + overflow: auto; + flex-flow: row; + flex-wrap: wrap; + .webBox-filterTag, .webBox-filterTag-active { + font-weight: bold; + padding-left: 6; + padding-right: 6; + box-shadow: black 1px 1px 4px; + border-radius: 5; + margin: 2; + height: 20; + background-color: lightgrey; + } + .webBox-filterTag-active { + background-color: white; + } + } + .pdfViewerDash-dragAnnotationBox { position: absolute; background-color: transparent; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index b15865c1f..ab17703c8 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from "mobx-react"; import { Dictionary } from "typescript-collections"; import * as WebRequest from 'web-request'; -import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { HtmlField } from "../../../fields/HtmlField"; @@ -14,8 +14,8 @@ import { listSpec, makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { WebField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; -import { emptyFunction, OmitKeys, returnOne, smoothScroll, Utils } from "../../../Utils"; -import { Docs } from "../../documents/Documents"; +import { emptyFunction, OmitKeys, returnOne, smoothScroll, Utils, returnZero, returnTrue } from "../../../Utils"; +import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { ImageUtils } from "../../util/Import & Export/ImageUtils"; import { undoBatch } from "../../util/UndoManager"; @@ -32,6 +32,11 @@ import "./WebBox.scss"; import { DocumentType } from '../../documents/DocumentTypes'; import React = require("react"); import { CurrentUserUtils } from "../../util/CurrentUserUtils"; +import { SearchBox } from "../search/SearchBox"; +import { CollectionStackingView } from "../collections/CollectionStackingView"; +import { StyleProp } from "../StyleProvider"; +import { FormattedTextBox } from "./formattedText/FormattedTextBox"; +import { CollectionViewType } from "../collections/CollectionView"; const htmlToText = require("html-to-text"); type WebDocument = makeInterface<[typeof documentSchema]>; @@ -461,6 +466,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum const funcs: ContextMenuProps[] = []; funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); funcs.push({ description: (this.layoutDoc[this.fieldKey + "-contentWidth"] ? "Unfreeze" : "Freeze") + " Content Width", event: () => this.layoutDoc[this.fieldKey + "-contentWidth"] = this.layoutDoc[this.fieldKey + "-contentWidth"] ? undefined : Doc.NativeWidth(this.layoutDoc), icon: "snowflake" }); + funcs.push({ description: "Toggle Annotation View ", event: () => this.Document._showSidebar = !this.Document._showSidebar, icon: "expand-arrows-alt" }); cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } @@ -483,6 +489,83 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum return view; } + anchorMenuClick = (anchor: Doc) => { + this.Document._showSidebar = true; + const startup = StrListCast(this.rootDoc.docFilters).map(filter => filter.split(":")[0]).join(" "); + const target = Docs.Create.TextDocument(startup, { + title: "anno", + annotationOn: this.rootDoc, _width: 200, _height: 50, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize), + _fontFamily: StrCast(Doc.UserDoc().fontFamily) + }); + FormattedTextBox.SelectOnLoad = target[Id]; + FormattedTextBox.DontSelectInitialText = true; + this.allTags.map(tag => target[tag] = tag); + DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup", "annotation"); + this.sidebarAddDocument(target); + } + sidebarKey = () => this.fieldKey + "-sidebar"; + sidebarFiltersHeight = () => 50; + sidebarTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.dataDoc), 0).scale(this.props.scaling?.() || 1); + sidebarWidth = () => !this.layoutDoc._showSidebar ? 0 : (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth); + sidebarHeight = () => this.props.PanelHeight() - this.sidebarFiltersHeight() - 20; + sidebarAddDocument = (doc: Doc | Doc[]) => this.addDocument(doc, this.sidebarKey()); + sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.sidebarKey()); + sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.sidebarKey()); + sidebarDocFilters = () => [...StrListCast(this.layoutDoc._docFilters), ...StrListCast(this.layoutDoc[this.sidebarKey() + "-docFilters"])]; + @computed get allTags() { + const keys = new Set<string>(); + DocListCast(this.rootDoc[this.sidebarKey()]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key))); + return Array.from(keys.keys()).filter(key => key[0]).filter(key => !key.startsWith("_") && (key[0] === "#" || key[0] === key[0].toUpperCase())).sort(); + } + renderTag = (tag: string) => { + const active = StrListCast(this.rootDoc[this.sidebarKey() + "-docFilters"]).includes(`${tag}:${tag}:check`); + return <div className={`webBox-filterTag${active ? "-active" : ""}`} + onClick={e => Doc.setDocFilter(this.rootDoc, tag, tag, "check", true, this.sidebarKey())}> + {tag} + </div>; + } + @computed get sidebarOverlay() { + return !this.layoutDoc._showSidebar ? (null) : + <div style={{ + position: "absolute", pointerEvents: this.active() ? "all" : undefined, top: 0, right: 0, + background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor), + width: `${this.sidebarWidth()}px`, + height: "100%" + }}> + <div style={{ width: "100%", height: this.sidebarHeight(), position: "relative" }}> + <CollectionStackingView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} + NativeWidth={returnZero} + NativeHeight={returnZero} + PanelHeight={this.sidebarHeight} + PanelWidth={this.sidebarWidth} + xMargin={0} + yMargin={0} + docFilters={this.sidebarDocFilters} + chromeStatus={"enabled"} + scaleField={this.sidebarKey() + "-scale"} + isAnnotationOverlay={false} + select={emptyFunction} + active={this.annotationsActive} + scaling={returnOne} + whenActiveChanged={this.whenActiveChanged} + childHideDecorationTitle={returnTrue} + removeDocument={this.sidebarRemDocument} + moveDocument={this.sidebarMoveDocument} + addDocument={this.sidebarAddDocument} + CollectionView={undefined} + ScreenToLocalTransform={this.sidebarTransform} + renderDepth={this.props.renderDepth + 1} + viewType={CollectionViewType.Stacking} + fieldKey={this.sidebarKey()} + pointerEvents={"all"} + /> + </div> + <div className="webBox-tagList" style={{ height: this.sidebarFiltersHeight(), width: this.sidebarWidth() }}> + {this.allTags.map(tag => this.renderTag(tag))} + </div> + </div>; + } + @computed get content() { const frozen = !this.props.isSelected() || DocumentDecorations.Instance?.Interacting; @@ -521,63 +604,73 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum @action onMarqueeDown = (e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.active(true)) this._marqueeing = [e.clientX, e.clientY]; + if (!e.altKey && e.button === 0 && this.active(true)) { + this._marqueeing = [e.clientX, e.clientY]; + this.props.select(false); + } } @action - finishMarquee = () => { - this._marqueeing = undefined; - this.props.select(true); - } + finishMarquee = () => this._marqueeing = undefined; - panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); + panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); render() { const inactiveLayer = this.props.layerProvider?.(this.layoutDoc) === false; const scale = this.props.scaling?.() || 1; - return (<div className="webBox" ref={this._mainCont} > - <div className={`webBox-container`} - style={{ pointerEvents: inactiveLayer ? "none" : undefined }} - onWheel={this.onWebWheel} - onContextMenu={this.specificContextMenu}> - <base target="_blank" /> - {this.content} - <div className={"webBox-outerContent"} ref={this._outerRef} - style={{ - width: `${100 / scale}%`, height: `${100 / scale}%`, transform: `scale(${scale})`, - pointerEvents: !this.layoutDoc.isAnnotating || inactiveLayer ? "none" : "all" - }} - onWheel={this.onWheel} - onPointerDown={this.onMarqueeDown} - onScroll={this.onScroll} - > - <div className={"webBox-innerContent"} style={{ - height: NumCast(this.scrollHeight, 50), - pointerEvents: inactiveLayer ? "none" : undefined - }}> - <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} - renderDepth={this.props.renderDepth + 1} - CollectionView={undefined} - fieldKey={this.annotationKey} - isAnnotationOverlay={true} - scaling={returnOne} - PanelWidth={this.panelWidth} - PanelHeight={this.panelHeight} - ScreenToLocalTransform={this.scrollXf} - removeDocument={this.removeDocument} - moveDocument={this.moveDocument} - addDocument={this.addDocument} - select={emptyFunction} - active={this.active} - whenActiveChanged={this.whenActiveChanged} /> + return ( + <div className="webBox" ref={this._mainCont} > + <div className={`webBox-container`} + style={{ pointerEvents: inactiveLayer ? "none" : undefined }} + onWheel={this.onWebWheel} + onContextMenu={this.specificContextMenu}> + <base target="_blank" /> + {this.content} + <div className={"webBox-outerContent"} ref={this._outerRef} + style={{ + width: `calc(${100 / scale}% - ${this.sidebarWidth()}px)`, height: `${100 / scale}%`, transform: `scale(${scale})`, + pointerEvents: !this.layoutDoc.isAnnotating || inactiveLayer ? "none" : "all" + }} + onWheel={this.onWheel} + onPointerDown={this.onMarqueeDown} + onScroll={this.onScroll} + > + <div className={"webBox-innerContent"} style={{ + height: NumCast(this.scrollHeight, 50), + pointerEvents: inactiveLayer ? "none" : undefined + }}> + <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} + renderDepth={this.props.renderDepth + 1} + CollectionView={undefined} + fieldKey={this.annotationKey} + isAnnotationOverlay={true} + scaling={returnOne} + PanelWidth={this.panelWidth} + PanelHeight={this.panelHeight} + ScreenToLocalTransform={this.scrollXf} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocument} + select={emptyFunction} + active={this.active} + whenActiveChanged={this.whenActiveChanged} /> + </div> </div> - </div> - {this.annotationLayer} - {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - <MarqueeAnnotator rootDoc={this.rootDoc} scrollTop={NumCast(this.rootDoc._scrollTop)} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocument} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />} - </div > - {this.props.isSelected() ? this.editToggleBtn() : null} - </div>); + {this.annotationLayer} + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : + <MarqueeAnnotator rootDoc={this.rootDoc} + anchorMenuClick={this.anchorMenuClick} + scrollTop={NumCast(this.rootDoc._scrollTop)} + down={this._marqueeing} scaling={this.props.scaling} + addDocument={this.addDocument} + finishMarquee={this.finishMarquee} + savedAnnotations={this._savedAnnotations} + annotationLayer={this._annotationLayer.current} + mainCont={this._mainCont.current} />} + </div > + {this.sidebarOverlay} + {this.props.isSelected() ? this.editToggleBtn() : null} + </div>); } }
\ No newline at end of file diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 985c5d807..9259e6c25 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1105,6 +1105,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } onPointerDown = (e: React.PointerEvent): void => { + this.tryUpdateScrollHeight(); // if a doc a fitwidth doc is being viewed in different context (eg freeform & lightbox), then it will have conflicting heights. so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view. if ((e.target as any).tagName === "AUDIOTAG") { e.preventDefault(); e.stopPropagation(); @@ -1394,13 +1395,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } } tryUpdateScrollHeight() { - const proseHeight = this.ProseRef?.scrollHeight || 0; - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; - if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { - setScrollHeight(); - } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) { + const proseHeight = this.ProseRef?.scrollHeight || 0; + const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation + const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; + if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { + setScrollHeight(); + } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... + } } } fitToBox = () => this.props.Document._fitToBox; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 5a3c2103e..0a1e0ba8f 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -15,6 +15,7 @@ import { SelectionManager } from "../../util/SelectionManager"; export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { static Instance: AnchorMenu; + private _disposer: IReactionDisposer | undefined; private _commentCont = React.createRef<HTMLButtonElement>(); private _palette = [ "rgba(208, 2, 27, 0.8)", @@ -60,12 +61,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { AnchorMenu.Instance._canFade = false; } - _disposer: IReactionDisposer | undefined; componentDidMount() { this._disposer = reaction(() => SelectionManager.Views(), - selected => { - AnchorMenu.Instance.fadeOut(true); - }); + selected => AnchorMenu.Instance.fadeOut(true)); } pointerDown = (e: React.PointerEvent) => { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index c7359f74e..c9bee5101 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -8,14 +8,16 @@ import { documentSchema } from "../../../fields/documentSchemas"; import { Id } from "../../../fields/FieldSymbols"; import { InkTool } from "../../../fields/InkField"; import { createSchema, makeInterface } from "../../../fields/Schema"; -import { Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types"; +import { Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from "../../../fields/util"; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; import { Networking } from "../../Network"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { CompiledScript, CompileScript } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; +import { SharingManager } from "../../util/SharingManager"; import { SnappingManager } from "../../util/SnappingManager"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; @@ -27,8 +29,6 @@ import { Annotation } from "./Annotation"; import "./PDFViewer.scss"; const pdfjs = require('pdfjs-dist/es5/build/pdf.js'); import React = require("react"); -import { SharingManager } from "../../util/SharingManager"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); const _global = (window /* browser */ || global /* node */) as any; @@ -52,7 +52,6 @@ interface IViewerProps extends FieldViewProps { url: string; startupLive: boolean; loaded?: (nw: number, nh: number, np: number) => void; - isChildActive: (outsideReaction?: boolean) => boolean; setPdfViewer: (view: PDFViewer) => void; ContentScaling?: () => number; sidebarWidth: () => number; @@ -69,6 +68,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu @observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>(); @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript; @observable private _marqueeing: number[] | undefined; + @observable private _textSelecting = true; @observable private _showWaiting = true; @observable private _showCover = false; @observable private _zoomed = 1; @@ -379,9 +379,11 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu if ((e.button !== 0 || e.altKey) && this.active(true)) { this._setPreviewCursor?.(e.clientX, e.clientY, true); } - if (!e.altKey && e.button === 0 && this.active(true)) { + if (!e.altKey && e.button === 0 && this.props.active(true)) { + this.props.select(false); this._marqueeing = [e.clientX, e.clientY]; if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { + this._textSelecting = false; document.addEventListener("pointermove", this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called } else { // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee. @@ -400,8 +402,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu @action finishMarquee = () => { this._marqueeing = undefined; + this._textSelecting = true; document.removeEventListener("pointermove", this.onSelectMove); - this.props.select(false); } onSelectMove = (e: PointerEvent) => e.stopPropagation(); @@ -541,7 +543,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu </div>; } @computed get pdfViewerDiv() { - return <div className={"pdfViewerDash-text" + ((this.props.isSelected() || this.props.isChildActive()) ? "-selected" : "")} ref={this._viewer} />; + return <div className={"pdfViewerDash-text" + (this._textSelecting && (this.props.isSelected() || this.props.active()) ? "-selected" : "")} ref={this._viewer} />; } @computed get contentScaling() { return this.props.ContentScaling?.() || 1; } @computed get standinViews() { |