diff options
author | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-03-13 16:40:26 -0500 |
---|---|---|
committer | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-03-13 16:40:26 -0500 |
commit | 74c8b78c7119e700655338a32c03f2031186b238 (patch) | |
tree | 57baf33d9230b7ea6f77addf34becae8faeb63b0 | |
parent | 70a5edc6e9a203141b16fd31ee9a1a26ebae2188 (diff) | |
parent | e20a8916483734bb06d08409b6bc804c2391a289 (diff) |
minor changes
44 files changed, 340 insertions, 197 deletions
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 2a1f55710..c5ff2db68 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -11,6 +11,7 @@ import "../../views/nodes/WebBox.scss"; import "./YoutubeBox.scss"; import React = require("react"); import { InkTool } from '../../../fields/InkField'; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; interface VideoTemplate { thumbnailUrl: string; @@ -350,7 +351,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> { const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - const classname = "webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); + const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); return ( <> <div className={classname} > diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d9ada89ea..e55527f84 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -59,6 +59,7 @@ import { DocumentType } from "./DocumentTypes"; import { EquationBox } from "../views/nodes/EquationBox"; import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox"; import { script } from "googleapis/build/src/apis/script"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; const path = require('path'); const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); @@ -663,7 +664,7 @@ export namespace Docs { viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; viewDoc["acl-Override"] = dataDoc["acl-Override"] = "None"; - !Doc.IsSystem(dataDoc) && ![DocumentType.PDFANNO, DocumentType.LINK, DocumentType.LINKANCHOR, DocumentType.TEXTANCHOR].includes(proto.type as any) && + !Doc.IsSystem(dataDoc) && ![DocumentType.PDFANNO, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR, DocumentType.TEXTANCHOR].includes(proto.type as any) && !dataDoc.isFolder && !protoProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc); return Doc.assign(viewDoc, delegateProps, true); @@ -1174,7 +1175,7 @@ export namespace DocUtils { created = Docs.Create.AudioDocument((field).url.href, resolved); layout = AudioBox.LayoutString; } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved); + created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved); layout = InkingStroke.LayoutString; } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); @@ -1436,4 +1437,4 @@ Scripting.addGlobal(function generateLinkTitle(self: Doc) { const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : "<?>"; const relation = self.linkRelationship || "to"; return `${anchor1title} (${relation}) ${anchor2title}`; -})
\ No newline at end of file +});
\ No newline at end of file diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 3d53ad134..aafb986be 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,4 +1,4 @@ -import { computed, observable, reaction } from "mobx"; +import { computed, observable, reaction, action } from "mobx"; import * as rp from 'request-promise'; import { DataSym, Doc, DocListCast, DocListCastAsync, AclReadonly } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; @@ -33,6 +33,8 @@ import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; import { UndoManager } from "./UndoManager"; import { SnappingManager } from "./SnappingManager"; +import { InkTool } from "../../fields/InkField"; +import { computedFn } from "mobx-utils"; export let resolvedPorts: { server: number, socket: number }; @@ -1214,6 +1216,8 @@ export class CurrentUserUtils { public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); } public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); } + public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } + @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } } Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 969eed701..8cb80ecf9 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,9 +9,6 @@ import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView'; import { Scripting } from './Scripting'; -import { CurrentUserUtils } from './CurrentUserUtils'; -import { TabDocView } from '../views/collections/TabDocView'; -import { UndoManager } from './UndoManager'; export class DocumentManager { @@ -160,12 +157,15 @@ export class DocumentManager { return false; }; const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); - const rtfView = annotatedDoc && getFirstDocView(annotatedDoc); + const annoContainerView = annotatedDoc && getFirstDocView(annotatedDoc); const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined; const targetDocContext = contextDoc || annotatedDoc; const targetDocContextView = targetDocContext && getFirstDocView(targetDocContext); - const focusView = !docView && targetDoc.type === DocumentType.TEXTANCHOR && rtfView ? rtfView : docView; + const focusView = !docView && targetDoc.type === DocumentType.TEXTANCHOR && annoContainerView ? annoContainerView : docView; + if (!docView && annoContainerView && !focusView) { + annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below + } if (focusView) { focusView && Doc.linkFollowHighlight(focusView.rootDoc); focusView.focus(targetDoc, { @@ -181,7 +181,12 @@ export class DocumentManager { } else { // otherwise try to get a view of the context of the target if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. targetDocContext._viewTransition = "transform 500ms"; - targetDocContextView.props.focus(targetDocContextView.rootDoc, { willZoom }); + targetDocContextView.props.focus(targetDocContextView.rootDoc, { + willZoom, afterFocus: async () => { + targetDocContext._viewTransition = undefined; + return ViewAdjustment.doNothing; + } + }); // now find the target document within the context if (targetDoc._timecodeToShow) { // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode; @@ -191,6 +196,7 @@ export class DocumentManager { const findView = (delay: number) => { const retryDocView = getFirstDocView(targetDoc); // test again for the target view snce we presumably created the context above by focusing on it if (retryDocView) { // we found the target in the context + Doc.linkFollowHighlight(retryDocView.rootDoc); retryDocView.props.focus(targetDoc, { willZoom, afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => { diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 6480c6507..589cfafc5 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -8,6 +8,7 @@ import { List } from '../../fields/List'; import { DateField } from '../../fields/DateField'; import { ScriptField } from '../../fields/ScriptField'; import { GetEffectiveAcl, SharingPermissions, distributeAcls, denormalizeEmail } from '../../fields/util'; +import { CurrentUserUtils } from '../util/CurrentUserUtils'; /// DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView) @@ -189,7 +190,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T } else { added.map(doc => doc.context = this.props.Document); - (targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>).push(...added); + const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>; + if (annoDocs) annoDocs.push(...added); + else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added); targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now())); } } @@ -198,9 +201,9 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T } whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); - active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.props.Document._) && + active = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.props.Document._) && (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) => (Doc.GetSelectedTool() !== InkTool.None || (this.props.layerProvider?.(this.props.Document) === false && this.props.active()) || + 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) } return Component; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c263c5cf5..6d8bd30be 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -458,7 +458,7 @@ export class DocumentDecorations extends React.Component<{ boundsLeft: number, b top: bounds.y - this._resizeBorderWidth / 2 - this._titleHeight, }}> {!canDelete ? <div /> : topBtn("close", "times", this.onCloseClick, "Close")} - {seldoc.props.Document.type === DocumentType.EQUATION ? (null) : titleArea} + {seldoc.props.hideDecorationTitle || seldoc.props.Document.type === DocumentType.EQUATION ? (null) : titleArea} {seldoc.props.hideResizeHandles || seldoc.props.Document.type === DocumentType.EQUATION ? (null) : <> {SelectionManager.Views().length !== 1 || seldoc.Document.type === DocumentType.INK ? (null) : diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index b4051194f..093e01a19 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -491,7 +491,7 @@ export class GestureOverlay extends Touchable { @action onPointerDown = (e: React.PointerEvent) => { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { this._points.push({ X: e.clientX, Y: e.clientY }); setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 20bba1299..bd67735a8 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -28,10 +28,8 @@ import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; import { AnchorMenu } from "./pdf/AnchorMenu"; import { SearchBox } from "./search/SearchBox"; -import { random } from "lodash"; -import { DocumentView } from "./nodes/DocumentView"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { SettingsManager } from "../util/SettingsManager"; -import { AudioBox } from "./nodes/AudioBox"; const modifiers = ["control", "meta", "shift", "alt"]; type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo | Promise<KeyControlInfo>; @@ -119,8 +117,7 @@ export class KeyManager { DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = false); - - Doc.SetSelectedTool(InkTool.None); + CurrentUserUtils.SelectedTool = InkTool.None; var doDeselect = true; if (SnappingManager.GetIsDragging()) { DragManager.AbortDrag(); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 462d78865..46586bf1b 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -17,6 +17,7 @@ import "./InkingStroke.scss"; import { FieldView, FieldViewProps } from "./nodes/FieldView"; import React = require("react"); import { InkStrokeProperties } from "./InkStrokeProperties"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; type InkDocument = makeInterface<[typeof documentSchema]>; const InkDocument = makeInterface(documentSchema); @@ -242,7 +243,7 @@ export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); } export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { - Doc.SetSelectedTool(pen ? InkTool.Highlighter : InkTool.None); + CurrentUserUtils.SelectedTool = pen ? InkTool.Highlighter : InkTool.None; SetActiveInkWidth(width); SetActiveInkColor(color); SetActiveFillColor(fill); @@ -250,9 +251,9 @@ Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fil SetActiveArrowEnd(arrowEnd); SetActiveDash(dash); }); -Scripting.addGlobal(function activateEraser(pen: any) { return Doc.SetSelectedTool(pen ? InkTool.Eraser : InkTool.None); }); -Scripting.addGlobal(function activateStamp(pen: any) { return Doc.SetSelectedTool(pen ? InkTool.Stamp : InkTool.None); }); -Scripting.addGlobal(function deactivateInk() { return Doc.SetSelectedTool(InkTool.None); }); +Scripting.addGlobal(function activateEraser(pen: any) { return CurrentUserUtils.SelectedTool = pen ? InkTool.Eraser : InkTool.None; }); +Scripting.addGlobal(function activateStamp(pen: any) { return CurrentUserUtils.SelectedTool = pen ? InkTool.Stamp : InkTool.None; }); +Scripting.addGlobal(function deactivateInk() { return CurrentUserUtils.SelectedTool = InkTool.None; }); Scripting.addGlobal(function setInkWidth(width: any) { return SetActiveInkWidth(width); }); Scripting.addGlobal(function setInkColor(color: any) { return SetActiveInkColor(color); }); Scripting.addGlobal(function setFillColor(fill: any) { return SetActiveFillColor(fill); }); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 63439586c..503c284aa 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -29,6 +29,7 @@ export interface MarqueeAnnotatorProps { addDocument: (doc: Doc) => boolean; getPageFromScroll?: (top: number) => number; finishMarquee: () => void; + anchorMenuClick?: (anchor: Doc) => void; } @observer export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { @@ -59,6 +60,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> { document.addEventListener("pointermove", this.onSelectMove); document.addEventListener("pointerup", this.onSelectEnd); + AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.(this.highlight("rgba(173, 216, 230, 0.75)", true)); AnchorMenu.Instance.Highlight = this.highlight; /** * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation. diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 134c50972..1d52136de 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -191,8 +191,8 @@ font-size: 10px; padding: 10px; margin-left: 5px; - max-height: 40%; - overflow-y: auto; + // max-height: 40%; + overflow-y: visible; .propertiesView-buttonContainer { float: right; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 499d8ff0c..128256733 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -900,10 +900,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { </div>; } + /** + * Checks if a currentFilter (FilterDoc) exists on the current collection (if the Properties Panel + Filters submenu are open). + * If it doesn't exist, it creates it. + */ checkFilterDoc() { if (this.filterDoc.type === DocumentType.COL && !this.filterDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.filterDoc); } + /** + * Creates a new currentFilter for this.filterDoc, + */ createNewFilterDoc = () => { const temp = this.filterDoc._docFilters; this.filterDoc._docFilters = new List<string>(); @@ -946,7 +953,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { removeDocument={returnFalse} ScreenToLocalTransform={this.getTransform} PanelWidth={this.docWidth} - PanelHeight={this.docHeight} + PanelHeight={() => this.docHeight} renderDepth={0} scriptContext={this.filterDoc.currentFilter as Doc} focus={emptyFunction} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 0dd0cd969..2585db046 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -78,7 +78,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | case StyleProp.DocContents: return undefined; case StyleProp.WidgetColor: return isAnnotated ? "lightBlue" : darkScheme() ? "lightgrey" : "dimgrey"; case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null)); - case StyleProp.HideLinkButton: return isAnchor || props?.dontRegisterView || ((!selected || doc?.type == DocumentType.PDFANNO) && (doc?.isLinkButton || doc?.hideLinkButton)); + case StyleProp.HideLinkButton: return isAnchor || props?.dontRegisterView || ((!selected || doc?.type === DocumentType.PDFANNO) && (doc?.isLinkButton || doc?.hideLinkButton)); case StyleProp.ShowTitle: return doc && !doc.presentationTargetDoc && StrCast(doc._showTitle, !Doc.IsSystem(doc) && doc.type === DocumentType.RTF ? (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : "author;creationDate") : "") || ""; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 0c8e26e91..3556e74bc 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -337,7 +337,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { } } if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && - Doc.GetSelectedTool() !== InkTool.Highlighter && Doc.GetSelectedTool() !== InkTool.Pen) { + ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { e.stopPropagation(); } } @@ -372,16 +372,17 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { this.props.Document.dockingConfig = json; setTimeout(async () => { - const sublists = DocListCast(this.props.Document[this.props.fieldKey]); - const tabs = Cast(sublists[0], Doc, null); - const other = Cast(sublists[1], Doc, null); + const sublists = await DocListCastAsync(this.props.Document[this.props.fieldKey]); + const tabs = sublists && Cast(sublists[0], Doc, null); + const other = sublists && Cast(sublists[1], Doc, null); const tabdocs = await DocListCastAsync(tabs?.data); const otherdocs = await DocListCastAsync(other?.data); tabs && (Doc.GetProto(tabs).data = new List<Doc>(docs)); const otherSet = new Set<Doc>(); otherdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); - tabdocs?.filter(doc => !docs.includes(doc)).forEach(doc => otherSet.add(doc)); - other && (Doc.GetProto(other).data = new List<Doc>(Array.from(otherSet.values()))); + tabdocs?.filter(doc => !docs.includes(doc) && doc.type !== DocumentType.KVP).forEach(doc => otherSet.add(doc)); + const vals = Array.from(otherSet.values()).filter(val => val instanceof Doc).map(d => d as Doc).filter(d => d.type !== DocumentType.KVP); + other && (Doc.GetProto(other).data = new List<Doc>(vals)); }, 0); } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 591b4161e..51a67ea7d 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -603,7 +603,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; } @action primCreated() { if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke= - Doc.SetSelectedTool(InkTool.None); + CurrentUserUtils.SelectedTool = InkTool.None; this._selectedPrimitive = this._shapePrims.length; SetActiveArrowStart("none"); SetActiveArrowEnd("none"); @@ -643,7 +643,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu this._keepPrimitiveMode = keep; if (this._selectedPrimitive !== i) { this._selectedPrimitive = i; - Doc.SetSelectedTool(InkTool.Pen); + CurrentUserUtils.SelectedTool = InkTool.Pen; SetActiveArrowStart(this._head[i]); SetActiveArrowEnd(this._end[i]); SetActiveBezierApprox("300"); @@ -651,7 +651,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu GestureOverlay.Instance.InkShape = this._shapePrims[i]; } else { this._selectedPrimitive = this._shapePrims.length; - Doc.SetSelectedTool(InkTool.None); + CurrentUserUtils.SelectedTool = InkTool.None; SetActiveArrowStart(""); SetActiveArrowEnd(""); GestureOverlay.Instance.InkShape = ""; diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index f50da0134..3b52e6408 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -283,11 +283,11 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { const colpos = this._searchTerm.indexOf(":"); const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); if (temp === "") { - Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined); + Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove"); this.updateFilter(); } else { - Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined); + Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove"); this.tempfilter = temp; Doc.setDocFilter(this.props.Document, this._key, temp, "check"); this.props.col.setColor("green"); @@ -295,7 +295,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { } } else { - Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, undefined); + Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove"); this.updateFilter(); if (this.showKeys.length) { this.onSelect(this.showKeys[0]); @@ -421,10 +421,10 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> { onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()} onChange={(e) => { - e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, undefined); + e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, "remove"); e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log(""); e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter(); - e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, undefined); + e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove"); }} checked={bool} /> diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 10494e3e2..df39ed3e1 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -233,6 +233,8 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, ScreenToLocalTransform={stackedDocTransform} focus={this.focusDocument} docFilters={this.docFilters} + hideDecorationTitle={this.props.childHideDecorationTitle?.()} + hideTitle={this.props.childHideTitle?.()} docRangeFilters={this.docRangeFilters} searchFilterDocs={this.searchFilterDocs} ContainingCollectionDoc={this.props.CollectionView?.props.Document} @@ -517,6 +519,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, height: `${1 / this.scaling * 100}%`, width: `${1 / this.scaling * 100}%`, transformOrigin: "top left", + background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor), pointerEvents: this.backgroundEvents ? "all" : undefined }} onScroll={action(e => this._scroll = e.currentTarget.scrollTop)} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index b82644128..f226cf79b 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -66,7 +66,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: @computed get dataField() { // sets the dataDoc's data field to an empty list if the data field is undefined - prevents issues with addonly // setTimeout changes it outside of the @computed section - !this.dataDoc[this.props.fieldKey] && setTimeout(() => this.dataDoc[this.props.fieldKey] = new List<Doc>()); + !this.dataDoc[this.props.fieldKey] && setTimeout(() => !this.dataDoc[this.props.fieldKey] && (this.dataDoc[this.props.fieldKey] = new List<Doc>())); return this.dataDoc[this.props.fieldKey]; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 0a4422cbd..f5eb534aa 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -193,7 +193,10 @@ export class /> </div>; } - + @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); } + truncateTitleWidth = () => this.treeViewtruncateTitleWidth; + @computed get outlineMode() { return this.doc.treeViewType === "outline"; } + @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; } onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }; active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 857982782..be5a4b852 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -72,6 +72,8 @@ export interface CollectionViewProps extends FieldViewProps { children?: never | (() => JSX.Element[]) | React.ReactNode; childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) childOpacity?: () => number; + childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children + childHideDecorationTitle?: () => boolean; childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection childLayoutString?: string; childFreezeDimensions?: boolean; // used by TimeView to coerce documents to treat their width height as their native width/height @@ -102,13 +104,15 @@ export class CollectionView extends Touchable<CollectionViewProps> { return viewField as any as CollectionViewType; } - active = (outsideReaction?: boolean) => (this.props.isSelected(outsideReaction) || - this.props.rootSelected(outsideReaction) || - (this.props.layerProvider?.(this.props.Document) !== false && (this.props.Document.forceActive || this.props.Document._isGroup)) || - this._isChildActive || - this.props.renderDepth === 0) ? - true : - false + active = (outsideReaction?: boolean) => { + return this.props.renderDepth === 0 || + this.props.isSelected(outsideReaction) || + this.props.rootSelected(outsideReaction) || + (this.props.layerProvider?.(this.props.Document) !== false && (this.props.Document.forceActive || this.props.Document._isGroup)) || + this._isChildActive ? + true : + false; + } whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index f7e067399..2ead98aa4 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -213,7 +213,6 @@ export class TabDocView extends React.Component<TabDocViewProps> { } componentDidMount() { - const selected = () => SelectionManager.Views().some(v => v.props.Document === this._document); new _global.ResizeObserver(action((entries: any) => { for (const entry of entries) { this._panelWidth = entry.contentRect.width; @@ -221,7 +220,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { } })).observe(this.props.glContainer._element[0]); this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); - this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(); + this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined); this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }), ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""), { fireImmediately: true }); @@ -229,13 +228,15 @@ export class TabDocView extends React.Component<TabDocViewProps> { componentWillUnmount() { this._tabReaction?.(); + this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab); + this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); } @action.bound - private onActiveContentItemChanged() { - if (this.props.glContainer.tab && this._isActive !== this.props.glContainer.tab.isActive) { - this._isActive = this.props.glContainer.tab.isActive; + private onActiveContentItemChanged(contentItem: any) { + if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) { + this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab; (CollectionDockingView.Instance as any)._goldenLayout?.isInitialised && CollectionDockingView.Instance.stateChanged(); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } @@ -344,6 +345,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { } render() { + this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab); return ( <div className="collectionDockingView-content" style={{ height: "100%", width: "100%" }} ref={ref => { if (this._mainCont = ref) { diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 67a992df2..1d14c63c7 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -32,6 +32,9 @@ import { CollectionTreeView } from './CollectionTreeView'; import { CollectionView, CollectionViewType } from './CollectionView'; import "./TreeView.scss"; import React = require("react"); +import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; +import { SharingManager } from '../../util/SharingManager'; export interface TreeViewProps { document: Doc; @@ -101,12 +104,10 @@ export class TreeView extends React.Component<TreeViewProps> { @observable _dref: DocumentView | undefined | null; get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } - get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.fileSysMode ? (this.doc.isFolder ? "data" : "aliases") : Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields"); } - get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs && !this.fileSysMode ? this.fieldKey : this.defaultExpandedView); } + get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.props.treeView.fileSysMode ? (this.doc.isFolder ? "data" : "aliases") : Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields"); } + get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs && !this.props.treeView.fileSysMode ? this.fieldKey : this.defaultExpandedView); } @computed get doc() { TraceMobx(); return this.props.document; } - @computed get outlineMode() { return this.props.treeView.doc.treeViewType === "outline"; } - @computed get fileSysMode() { return this.props.treeView.doc.treeViewType === "fileSystem"; } @computed get treeViewOpen() { return (!this.props.treeViewPreventOpen && !this.doc.treeViewPreventOpen && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._overrideTreeViewOpen; } @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.treeViewDefaultExpandedView); } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @@ -252,7 +253,7 @@ export class TreeView extends React.Component<TreeViewProps> { const pt = [de.x, de.y]; const rect = this._header!.current!.getBoundingClientRect(); const before = pt[1] < rect.top + rect.height / 2; - const inside = this.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); + const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length); if (de.complete.linkDragData) { const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor(); const destDoc = this.doc; @@ -270,7 +271,7 @@ export class TreeView extends React.Component<TreeViewProps> { (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean); const move = (!docDragData.dropAction || docDragData.dropAction === "proto" || docDragData.dropAction === "move" || docDragData.dropAction === "same") && docDragData.moveDocument; if (canAdd) { - UndoManager.RunInTempBatch(() => docDragData.droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) : addDoc(d)) || added, false)); + UndoManager.RunInTempBatch(() => docDragData.droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (docDragData.dropAction === "proto" ? addDoc(d) : false) : addDoc(d)) || added, false)); } } } @@ -404,7 +405,7 @@ export class TreeView extends React.Component<TreeViewProps> { onPointerDown={e => { downX = e.clientX; downY = e.clientY; e.stopPropagation(); }} onClick={(e) => { if (this.props.active() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) { - !this.outlineMode && (this.doc[sortKey] = sortings[(curSort + 1) % sortings.length]); + !this.props.treeView.outlineMode && (this.doc[sortKey] = sortings[(curSort + 1) % sortings.length]); e.stopPropagation(); } }}> @@ -433,7 +434,7 @@ export class TreeView extends React.Component<TreeViewProps> { this.onCheckedClick?.script.run({ this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc, heading: this.props.containingCollection.title, - checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? undefined : "check", + checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? "remove" : "check", containingTreeView: this.props.treeView.props.Document, }, console.log); } else { @@ -446,15 +447,14 @@ export class TreeView extends React.Component<TreeViewProps> { TraceMobx(); const iconType = this.doc.isFolder ? (this.treeViewOpen ? "chevron-down" : "chevron-right") : Doc.toIcon(this.doc); const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; - - return <div className={`bullet${this.outlineMode ? "-outline" : ""}`} key={"bullet"} + return <div className={`bullet${this.props.treeView.outlineMode ? "-outline" : ""}`} key={"bullet"} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"} onClick={this.bulletClick} - style={this.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : { + style={this.props.treeView.outlineMode ? { opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity) } : { color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"), opacity: checked === "unchecked" ? undefined : 0.4 }}> - {this.outlineMode ? + {this.props.treeView.outlineMode ? !(this.doc.text as RichTextField)?.Text ? (null) : <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> : <div className="treeView-bulletIcons" > @@ -476,16 +476,16 @@ export class TreeView extends React.Component<TreeViewProps> { <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} /> <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView} onPointerDown={action(() => { - if (this.fileSysMode) { + if (this.props.treeView.fileSysMode) { this.doc.treeViewExpandedView = this.doc.isFolder ? this.fieldKey : this.treeViewExpandedView === "layout" ? "aliases" : this.treeViewExpandedView === "aliases" && !Doc.UserDoc().noviceMode ? "fields" : "layout"; } else if (this.treeViewOpen) { this.doc.treeViewExpandedView = this.treeViewLockExpandedView ? this.doc.treeViewExpandedView : - this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields") : + this.treeViewExpandedView === this.fieldKey ? (Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields") : this.treeViewExpandedView === "fields" && this.layoutDoc ? "layout" : this.treeViewExpandedView === "layout" && DocListCast(this.doc.links).length ? "links" : (this.treeViewExpandedView === "links" || this.treeViewExpandedView === "layout") && DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : - this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.outlineMode ? "layout" : "fields"); + this.childDocs ? this.fieldKey : (Doc.UserDoc().noviceMode || this.props.treeView.outlineMode ? "layout" : "fields"); } this.treeViewOpen = true; })}> @@ -497,12 +497,11 @@ export class TreeView extends React.Component<TreeViewProps> { showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); contextMenuItems = () => this.doc.isFolder ? [{ script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }] : Doc.IsSystem(this.doc) ? [] : - this.fileSysMode && this.doc === Doc.GetProto(this.doc) ? + this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ? [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }] : [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }] - truncateTitleWidth = () => NumCast(this.props.treeView.props.Document.treeViewTruncateTitleWidth, this.props.panelWidth()); onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); - onChildDoubleClick = () => (!this.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); + onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); ignoreEvent = (e: any) => { @@ -515,7 +514,7 @@ export class TreeView extends React.Component<TreeViewProps> { if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView switch (property.split(":")[0]) { - case StyleProp.Opacity: return this.outlineMode ? undefined : 1; + case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1; case StyleProp.BackgroundColor: return this.selected ? "#7089bb" : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor)); case StyleProp.DocContents: return testDocProps(props) && !props?.treeViewDoc ? (null) : <div className="treeView-label" style={{ // just render a title for a tree view label (identified by treeViewDoc being set in 'props') @@ -532,7 +531,7 @@ export class TreeView extends React.Component<TreeViewProps> { return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView } onKeyDown = (e: React.KeyboardEvent) => { - if (this.doc.treeViewHideHeader || this.outlineMode) { + if (this.doc.treeViewHideHeader || this.props.treeView.outlineMode) { e.stopPropagation(); e.preventDefault(); switch (e.key) { @@ -553,7 +552,7 @@ export class TreeView extends React.Component<TreeViewProps> { const view = this._editTitle ? <EditableView key="_editTitle" oneLine={true} display={"inline-block"} - editing={true} + editing={this._editTitle} background={"#7089bb"} contents={StrCast(this.doc.title)} height={12} @@ -564,11 +563,11 @@ export class TreeView extends React.Component<TreeViewProps> { if (!shift) this.props.indentDocument?.(true); else this.props.outdentDocument?.(true); })} - OnEmpty={undoBatch(() => this.outlineMode && this.props.removeDoc?.(this.doc))} - OnFillDown={val => this.fileSysMode && this.makeFolder()} + OnEmpty={undoBatch(() => this.props.treeView.outlineMode && this.props.removeDoc?.(this.doc))} + OnFillDown={val => this.props.treeView.fileSysMode && this.makeFolder()} SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => { Doc.SetInPlace(this.doc, "title", value, false); - this.outlineMode && enterKey && this.makeTextCollection(); + this.props.treeView.outlineMode && enterKey && this.makeTextCollection(); })} /> : <DocumentView key="title" @@ -598,8 +597,8 @@ export class TreeView extends React.Component<TreeViewProps> { removeDocument={this.props.removeDoc} ScreenToLocalTransform={this.getTransform} NativeHeight={() => 18} - NativeWidth={this.truncateTitleWidth} - PanelWidth={this.truncateTitleWidth} + NativeWidth={this.props.treeView.truncateTitleWidth} + PanelWidth={this.props.treeView.truncateTitleWidth} PanelHeight={() => 18} contextMenuItems={this.contextMenuItems} renderDepth={1} @@ -705,7 +704,7 @@ export class TreeView extends React.Component<TreeViewProps> { @computed get renderBorder() { const sorting = this.doc[`${this.fieldKey}-sortCriteria`]; - return <div className={`treeView-border${this.outlineMode ? "outline" : ""}`} + return <div className={`treeView-border${this.props.treeView.outlineMode ? "outline" : ""}`} style={{ borderColor: sorting === undefined ? undefined : sorting === "up" ? "crimson" : sorting === "down" ? "blue" : "green" }}> {!this.treeViewOpen ? (null) : this.renderContent} </div>; @@ -725,7 +724,7 @@ export class TreeView extends React.Component<TreeViewProps> { } else this._editMaxWidth = ""; - const hideTitle = this.doc.treeViewHideHeader || this.outlineMode; + const hideTitle = this.doc.treeViewHideHeader || this.props.treeView.outlineMode; return <div className={`treeView-container${this._dref?.contentsActive() ? "-active" : ""}`} ref={this.createTreeDropTarget} onPointerDown={e => this.props.active(true) && SelectionManager.DeselectAll()} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ec5cb8a61..019fc9bae 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -455,7 +455,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action onPointerDown = (e: React.PointerEvent): void => { - if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) { + if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || + ([InkTool.Pen, InkTool.Highlighter].includes(CurrentUserUtils.SelectedTool))) { return; } this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)); @@ -465,7 +466,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); // if not using a pen and in no ink mode - if (Doc.GetSelectedTool() === InkTool.None) { + if (CurrentUserUtils.SelectedTool === InkTool.None) { this._downX = this._lastX = e.pageX; this._downY = this._lastY = e.pageY; } @@ -489,7 +490,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.addMoveListeners(); this.removeEndListeners(); this.addEndListeners(); - if (Doc.GetSelectedTool() === InkTool.None) { + if (CurrentUserUtils.SelectedTool === InkTool.None) { this._lastX = pt.pageX; this._lastY = pt.pageY; e.preventDefault(); @@ -509,7 +510,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P case GestureUtils.Gestures.Stroke: const points = ge.points; const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height); - const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.GetSelectedTool(), ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, + const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, { title: "ink stroke", x: B.x - Number(ActiveInkWidth()) / 2, y: B.y - Number(ActiveInkWidth()) / 2, _width: B.width + Number(ActiveInkWidth()), _height: B.height + Number(ActiveInkWidth()) }); this.addDocument(inkDoc); e.stopPropagation(); @@ -649,7 +650,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { if (this.props.active(true)) e.stopPropagation(); } else if (!e.cancelBubble) { - if (Doc.GetSelectedTool() === InkTool.None) { + if (CurrentUserUtils.SelectedTool === InkTool.None) { if (this.tryDragCluster(e, this._hitCluster)) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); @@ -665,7 +666,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P if (!e.cancelBubble) { const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true); if (myTouches[0]) { - if (Doc.GetSelectedTool() === InkTool.None) { + if (CurrentUserUtils.SelectedTool === InkTool.None) { if (this.tryDragCluster(e, this._hitCluster)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index e625b5b4b..9206d3767 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -320,7 +320,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque onClick = (e: React.MouseEvent): void => { if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - if (Doc.GetSelectedTool() === InkTool.None) { + if (CurrentUserUtils.SelectedTool === InkTool.None) { if (!(e.nativeEvent as any).marqueeHit) { (e.nativeEvent as any).marqueeHit = true; if (!this.props.trySelectCluster(e.shiftKey)) { diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index 715ec92f8..61cc8b3d1 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -16,6 +16,7 @@ import "./ColorBox.scss"; import { FieldView, FieldViewProps } from './FieldView'; import { DocumentType } from "../../documents/DocumentTypes"; import { RichTextMenu } from "./formattedText/RichTextMenu"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; type ColorDocument = makeInterface<[typeof documentSchema]>; const ColorDocument = makeInterface(documentSchema); @@ -30,7 +31,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument Doc.UserDoc().backgroundColor = Utils.colorString(color); SetActiveInkColor(color.hex); - if (Doc.GetSelectedTool() === InkTool.None) { + if (CurrentUserUtils.SelectedTool === InkTool.None) { const selected = SelectionManager.Views(); selected.map(view => { const targetDoc = view.props.Document.dragFactory instanceof Doc ? view.props.Document.dragFactory : diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 6f102213b..3622be5fc 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -232,7 +232,18 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp } @computed get filteredLinks() { - return DocUtils.FilterDocs(Array.from(new Set<Doc>(this.props.links)), this.props.View.props.docFilters(), []); + const results = [] as Doc[]; + Array.from(new Set<Doc>(this.props.links)).forEach(link => { + if (!DocUtils.FilterDocs([link], this.props.View.props.docFilters(), []).length) { + if (DocUtils.FilterDocs([link.anchor2 as Doc], this.props.View.props.docFilters(), []).length) { + results.push(link); + } + if (DocUtils.FilterDocs([link.anchor1 as Doc], this.props.View.props.docFilters(), []).length) { + results.push(link); + } + } else results.push(link); + }); + return results; } @computed get linkButtonInner() { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ebc65002f..c7b2f2df6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -127,6 +127,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps { freezeDimensions?: boolean; hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings + hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings treeViewDoc?: Doc; contentPointerEvents?: string; // pointer events allowed for content of a document view. eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents radialMenu?: String[]; @@ -491,7 +492,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps onPointerDown = (e: React.PointerEvent): void => { // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document) - if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) { + if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) { if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) { e.stopPropagation(); if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it @@ -503,7 +504,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this._downY = e.clientY; if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) && // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking - !((this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0))) { + !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) { if ((this.active || this.layoutDoc.onDragStart) && !e.ctrlKey && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) && @@ -520,7 +521,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } onPointerMove = (e: PointerEvent): void => { - if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) return; + if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) return; if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } @@ -622,7 +623,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps makeIntoPortal = async () => { const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document); if (!portalLink) { - const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), title: StrCast(this.props.Document.title) + ".portal" }); + const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ".portal" }); DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to"); } this.Document.followLinkLocation = "inPlace"; @@ -1009,11 +1010,11 @@ export class DocumentView extends React.Component<DocumentViewProps> { return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions)); } - shouldNotScale = () => (this.layoutDoc._fitWidth && !this.nativeWidth) || [CollectionViewType.Docking, CollectionViewType.Tree].includes(this.Document._viewType as any); - @computed get effectiveNativeWidth() { return this.shouldNotScale() ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); } - @computed get effectiveNativeHeight() { return this.shouldNotScale() ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); } + @computed get shouldNotScale() { return (this.layoutDoc._fitWidth && !this.nativeWidth) || [CollectionViewType.Docking, CollectionViewType.Tree].includes(this.Document._viewType as any); } + @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); } + @computed get effectiveNativeHeight() { return this.shouldNotScale ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); } @computed get nativeScaling() { - if (this.shouldNotScale()) return 1; + if (this.shouldNotScale) return 1; const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0; if (this.layoutDoc._fitWidth || this.props.PanelHeight() / this.effectiveNativeHeight > this.props.PanelWidth() / this.effectiveNativeWidth) { return Math.max(minTextScale, this.props.PanelWidth() / this.effectiveNativeWidth); // width-limited or fitWidth diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 65d2fe618..41e274e7a 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -87,6 +87,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc const keys = new Set<string>(noviceFields); this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key))); return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort(); + // return []; } @@ -231,10 +232,10 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc newFacet.title = facetHeader; newFacet.treeViewOpen = true; newFacet.type = DocumentType.COL; - const capturedVariables = { layoutDoc: targetDoc, system: true, _stayInCollection: true, _hideContextMenu: true, dataDoc: (targetDoc.data as any)[0][DataSym] }; - newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, "${facetHeader}")`, {}, capturedVariables); - // newFacet.target = targetDoc; - // newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${facetHeader}")`); + // const capturedVariables = { layoutDoc: targetDoc, system: true, _stayInCollection: true, _hideContextMenu: true, dataDoc: (targetDoc.data as any)[0][DataSym] }; + // newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, "${facetHeader}")`, {}, capturedVariables); + newFacet.target = targetDoc; + newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${facetHeader}")`); } newFacet && Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet); } @@ -332,6 +333,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc // TODO uncomment the line below when the treeview x works // const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet })); const options = this._allFacets.map(facet => ({ value: facet, label: facet })); + console.log(this.props.PanelHeight()); return this.props.dontRegisterView ? (null) : <div className="filterBox-treeView" style={{ width: "100%" }}> <div className="filterBox-title"> @@ -384,7 +386,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc ContainingCollectionDoc={this.props.ContainingCollectionDoc} ContainingCollectionView={this.props.ContainingCollectionView} PanelWidth={this.props.PanelWidth} - PanelHeight={this.props.PanelHeight} + PanelHeight={() => 100} rootSelected={this.props.rootSelected} renderDepth={1} dropAction={this.props.dropAction} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 8acf4081c..383650e89 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -61,10 +61,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { const options: ScriptOptions = { addReturn: true, params: { this: "Doc", _last_: "any" }, editable: false }; if (dubEq) options.typecheck = false; const script = CompileScript(value, options); - if (!script.compiled) { - return undefined; - } - return { script, type: dubEq, onDelegate: eq }; + return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq }; } public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript, forceOnDelegate?: boolean): boolean { @@ -100,9 +97,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> { e.stopPropagation(); } } - onPointerWheel = (e: React.WheelEvent): void => { - e.stopPropagation(); - } + onPointerWheel = (e: React.WheelEvent): void => e.stopPropagation(); rowHeight = () => 30; diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 16cc9d27e..74b331ae1 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -176,6 +176,26 @@ } } + .pdfBox-tagList { + display: flex; + flex-direction: row; + overflow: auto; + flex-flow: row; + flex-wrap: wrap; + .pdfBox-filterTag, .pdfBox-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; + } + .pdfBox-filterTag-active { + background-color: white; + } + } .pdfBox-title-outer { width: 100%; height: 100%; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index a3b5eea0c..4e4db323c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -3,29 +3,33 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; -import { Doc, Opt, WidthSym, HeightSym } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt, StrListCast, WidthSym } from "../../../fields/Doc"; import { documentSchema } from '../../../fields/documentSchemas'; +import { Id } from '../../../fields/FieldSymbols'; import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { PdfField } from "../../../fields/URLField"; import { TraceMobx } from '../../../fields/util'; -import { Utils, returnOne, OmitKeys, emptyFunction, returnZero } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnOne, returnTrue, returnZero, Utils } from '../../../Utils'; +import { Docs, DocUtils } from '../../documents/Documents'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch } from '../../util/UndoManager'; import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { CollectionStackingView } from '../collections/CollectionStackingView'; import { CollectionViewType } from '../collections/CollectionView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from "../DocComponent"; import { PDFViewer } from "../pdf/PDFViewer"; +import { SearchBox } from '../search/SearchBox'; +import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; +import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { DocAfterFocusFunc } from './DocumentView'; -import { Docs } from '../../documents/Documents'; -import { CollectionStackingView } from '../collections/CollectionStackingView'; -import { StyleProp } from '../StyleProvider'; +import { DocFocusOptions } from './DocumentView'; +import { List } from '../../../fields/List'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -85,6 +89,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum initialScrollTarget: Opt<Doc>; scrollFocus = (doc: Doc, smooth: boolean) => { + if (DocListCast(this.rootDoc[this.sidebarKey()]).includes(doc)) { + if (this.layoutDoc["_" + this.sidebarKey() + "-docFilters"]) { + this.layoutDoc["_" + this.sidebarKey() + "-docFilters"] = new List<string>(); + return 1; + } + } this.initialScrollTarget = doc; return this._pdfViewer?.scrollFocus(doc, smooth); } @@ -118,43 +128,64 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); } 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={`pdfbox-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.BackgroundColor), + background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.WidgetColor), width: `${this.sidebarWidth()}px`, height: "100%" }}> - <CollectionStackingView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} - NativeWidth={returnZero} - NativeHeight={returnZero} - PanelHeight={this.props.PanelHeight} - PanelWidth={this.sidebarWidth} - xMargin={0} - yMargin={0} - chromeStatus={"enabled"} - scaleField={this.sidebarKey() + "-scale"} - isAnnotationOverlay={false} - select={emptyFunction} - active={this.annotationsActive} - scaling={returnOne} - whenActiveChanged={this.whenActiveChanged} - childPointerEvents={true} - removeDocument={this.sidebarRemDocument} - moveDocument={this.sidebarMoveDocument} - addDocument={this.sidebarAddDocument} - CollectionView={undefined} - ScreenToLocalTransform={this.sidebarTransform} - renderDepth={this.props.renderDepth + 1} - fieldKey={this.sidebarKey()} - pointerEvents={"all"} - /> + <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} + fieldKey={this.sidebarKey()} + pointerEvents={"all"} + /> + </div> + <div className="pdfBox-tagList" style={{ height: this.sidebarFiltersHeight(), width: this.sidebarWidth() }}> + {this.allTags.map(tag => this.renderTag(tag))} + </div> </div>; } @@ -247,11 +278,26 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum const funcs: ContextMenuProps[] = []; 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" }); - !Doc.UserDoc().noviceMode && funcs.push({ description: "Toggle Sidebar mode ", event: () => this.Document._showSidebar = !this.Document._showSidebar, icon: "expand-arrows-alt" }); + funcs.push({ description: "Toggle Annotation View ", event: () => this.Document._showSidebar = !this.Document._showSidebar, icon: "expand-arrows-alt" }); ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); } + 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); + } + @computed get renderTitleBox() { const classname = "pdfBox" + (this.active() ? "-interactive" : ""); return <div className={classname} > @@ -260,7 +306,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum </div> </div>; } - isChildActive = (outsideReaction?: boolean) => this._isChildActive; @computed get renderPdfView() { TraceMobx(); @@ -271,6 +316,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} + anchorMenuClick={this.anchorMenuClick} loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined} setPdfViewer={this.setPdfViewer} addDocument={this.addDocument} diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index b9480fa74..844afe1e2 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -747,7 +747,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight panelHeight = () => this.props.PanelHeight() - 40; - active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && this.props.layerProvider?.(this.layoutDoc) !== false) && + active = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && this.props.layerProvider?.(this.layoutDoc) !== false) && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) /** diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 4956b315d..a14d8ccae 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -17,6 +17,7 @@ import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxBaseComponent } from "../DocComponent"; import { FieldView, FieldViewProps } from './FieldView'; import "./ScreenshotBox.scss"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; const path = require('path'); type ScreenshotDocument = makeInterface<[typeof documentSchema]>; @@ -129,7 +130,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh } @computed get content() { - const interactive = Doc.GetSelectedTool() !== InkTool.None || !this.props.isSelected() ? "" : "-interactive"; + const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive"; const style = "videoBox-content" + interactive; return <video className={`${style}`} key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 6afc2258a..3e1edb927 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -27,6 +27,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment"; import { LinkDocPreview } from "./LinkDocPreview"; import "./VideoBox.scss"; +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; const path = require('path'); type VideoDocument = makeInterface<[typeof documentSchema]>; @@ -293,7 +294,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD @computed get content() { const field = Cast(this.dataDoc[this.fieldKey], VideoField); - const interactive = Doc.GetSelectedTool() !== InkTool.None || !this.props.isSelected() ? "" : "-interactive"; + const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive"; const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive; return !field ? <div key="loading">Loading</div> : <div className="container" key="container" style={{ pointerEvents: this._isChildActive || this.active() ? "all" : "none" }}> @@ -344,7 +345,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD this._disposers.youtubeReactionDisposer?.(); this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0))); this._disposers.youtubeReactionDisposer = reaction( - () => !this.props.Document.isAnnotating && Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting, + () => !this.props.Document.isAnnotating && CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting, (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true }); }; if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 156fe64c9..b15865c1f 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -31,6 +31,7 @@ import { LinkDocPreview } from "./LinkDocPreview"; import "./WebBox.scss"; import { DocumentType } from '../../documents/DocumentTypes'; import React = require("react"); +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; const htmlToText = require("html-to-text"); type WebDocument = makeInterface<[typeof documentSchema]>; @@ -487,7 +488,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum const frozen = !this.props.isSelected() || DocumentDecorations.Instance?.Interacting; const scale = this.props.scaling?.() || 1; return (<> - <div className={"webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")} + <div className={"webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting ? "-interactive" : "")} style={{ width: NumCast(this.layoutDoc[this.fieldKey + "-contentWidth"]) || `${100 / scale}%`, height: `${100 / scale}%`, diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 86a9f4347..0eade44ac 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -71,7 +71,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> { constructor(props: IDashDocViewInternal) { super(props); - this._textBox = this.props.tbox as FormattedTextBox; + this._textBox = this.props.tbox; const updateDoc = action((dashDoc: Doc) => { this._dashDoc = dashDoc; @@ -101,7 +101,7 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> { const aliasedDoc = Doc.MakeAlias(dashDocBase, this.props.docid + this.props.alias); aliasedDoc.layoutKey = "layout"; this.props.fieldKey && DocUtils.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, this.props.fieldKey, undefined); - updateDoc(aliasedDoc) + updateDoc(aliasedDoc); } }); } else { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9c620e729..985c5d807 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -150,6 +150,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp public static FocusedBox: FormattedTextBox | undefined; public static PasteOnLoad: ClipboardEvent | undefined; public static SelectOnLoad = ""; + public static DontSelectInitialText = false; // whether initial text should be selected or not public static SelectOnLoadChar = ""; public static IsFragment(html: string) { return html.indexOf("data-pm-slice") !== -1; } public static GetHref(html: string): string { @@ -1081,10 +1082,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks); this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size)))); FormattedTextBox.SelectOnLoadChar = ""; - } else if (curText?.Text) { + } else if (curText?.Text && !FormattedTextBox.DontSelectInitialText) { selectAll(this._editorView!.state, this._editorView?.dispatch); this.startUndoTypingBatch(); } + FormattedTextBox.DontSelectInitialText = false; } selectOnLoad && this._editorView!.focus(); // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. @@ -1153,6 +1155,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate) this.setupAnchorMenu(); if (!this._downEvent) return; this._downEvent = false; + if ((e.nativeEvent as any).formattedHandled) { + console.log("handled"); + } if (!(e.nativeEvent as any).formattedHandled && this.active(true)) { const editor = this._editorView!; const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY }); @@ -1242,7 +1247,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._editorView!.dispatch(this._editorView!.state.tr.setSelection(NodeSelection.create(this._editorView!.state.doc, pcords.pos))); } } - if ((e.nativeEvent as any).formattedHandled) { e.stopPropagation(); return; } + if ((e.nativeEvent as any).formattedHandled) { + e.stopPropagation(); + return; + } this.props.isSelected(true) && ((e.nativeEvent as any).formattedHandled = true); if (this.props.isSelected(true)) { // if text box is selected, then it consumes all click events @@ -1295,8 +1303,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const originalUpHandler = view.mouseDown.up; view.root.removeEventListener("mouseup", originalUpHandler); view.mouseDown.up = (e: MouseEvent) => { - !(e as any).formattedHandled && originalUpHandler(e); - (e as any).formattedHandled = true; + if (!(e as any).formattedHandled) { + originalUpHandler(e); + (e as any).formattedHandled = true; + } else { + console.log("prehandled"); + } }; view.root.addEventListener("mouseup", view.mouseDown.up); } @@ -1394,7 +1406,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp fitToBox = () => this.props.Document._fitToBox; sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); sidebarAddDocument = (doc: Doc | Doc[]) => this.addDocument(doc, this.SidebarKey); - sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, 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); setSidebarHeight = (height: number) => this.rootDoc[this.SidebarKey + "-height"] = height; sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth(); @@ -1417,7 +1429,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const renderComponent = (tag: string) => { const ComponentTag = tag === "freeform" ? CollectionFreeFormView : tag === "translation" ? FormattedTextBox : CollectionStackingView; return <ComponentTag - {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} NativeWidth={returnZero} NativeHeight={returnZero} PanelHeight={this.props.PanelHeight} @@ -1427,8 +1439,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp chromeStatus={"enabled"} scaleField={this.SidebarKey + "-scale"} isAnnotationOverlay={false} - setHeight={this.setSidebarHeight} - fitContentsToDoc={this.fitToBox} select={emptyFunction} active={this.annotationsActive} scaling={this.sidebarContentScaling} @@ -1439,10 +1449,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp CollectionView={undefined} ScreenToLocalTransform={this.sidebarScreenToLocal} renderDepth={this.props.renderDepth + 1} + setHeight={this.setSidebarHeight} + fitContentsToDoc={this.fitToBox} noSidebar={true} fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : this.SidebarKey} />; }; - return <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} + return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.SelectedTool !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> {renderComponent(StrCast(this.layoutDoc.sidebarViewType))} </div>; @@ -1453,7 +1465,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const active = this.active(); const scale = this.props.hideOnLeave ? 1 : (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; - const interactive = (Doc.GetSelectedTool() === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false); + const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false); if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide); const minimal = this.props.ignoreAutoHeight; const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0); diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index e3d14d620..5a3c2103e 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -42,6 +42,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @observable public Highlighting: boolean = false; @observable public Status: "marquee" | "annotation" | "" = ""; + public OnClick: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined; public Delete: () => void = unimplementedFunction; @@ -71,7 +72,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { setupMoveUpEvents(this, e, (e: PointerEvent) => { this.StartDrag(e, this._commentCont.current!); return true; - }, returnFalse, returnFalse); + }, returnFalse, e => this.OnClick?.(e)); } @action diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 05d81f40f..1e574b9c0 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -56,10 +56,10 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> { } @undoBatch - pinToPres = () => this.props.pinToPres(this.annoTextRegion); + pinToPres = () => this.props.pinToPres(this.annoTextRegion) @undoBatch - makePushpin = () => this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin; + makePushpin = () => this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin isPushpin = () => BoolCast(this.annoTextRegion.isPushpin); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 3074eb4ec..390aed1e0 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -1,5 +1,15 @@ + +.pdfViewer-content { + height: 100%; + width: 100%; + position: absolute; + display: inline-block; + top: 0; + left: 0; +} .pdfViewerDash, .pdfViewerDash-interactive { + position: absolute; width: 100%; height: 100%; top: 0; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d2fa1089e..c7359f74e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -28,6 +28,7 @@ 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; @@ -55,6 +56,7 @@ interface IViewerProps extends FieldViewProps { setPdfViewer: (view: PDFViewer) => void; ContentScaling?: () => number; sidebarWidth: () => number; + anchorMenuClick: (anchor: Doc) => void; } /** @@ -511,7 +513,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (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); @computed get overlayLayer() { - return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} + return <div className={`pdfViewerDash-overlay${CurrentUserUtils.SelectedTool !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined, @@ -551,7 +553,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu contentZoom = () => this._zoomed; render() { TraceMobx(); - return <div> + return <div className="pdfViewer-content"> <div className={`pdfViewerDash${this.annotationsActive() ? "-interactive" : ""}`} ref={this._mainCont} onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick} style={{ @@ -563,11 +565,17 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu {this.pdfViewerDiv} {this.annotationLayer} {this.overlayLayer} + {this.overlayInfo} + {this.standinViews} + {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : + <MarqueeAnnotator rootDoc={this.rootDoc} scrollTop={0} down={this._marqueeing} + anchorMenuClick={this.props.anchorMenuClick} + addDocument={this.addDocument} + finishMarquee={this.finishMarquee} + getPageFromScroll={this.getPageFromScroll} + savedAnnotations={this._savedAnnotations} + annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />} </div> - {this.overlayInfo} - {this.standinViews} - {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : - <MarqueeAnnotator rootDoc={this.rootDoc} scrollTop={0} down={this._marqueeing} addDocument={this.addDocument} finishMarquee={this.finishMarquee} getPageFromScroll={this.getPageFromScroll} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />} </div>; } }
\ No newline at end of file diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx index 82c0e19c8..505a8da7e 100644 --- a/src/client/views/webcam/DashWebRTCVideo.tsx +++ b/src/client/views/webcam/DashWebRTCVideo.tsx @@ -11,6 +11,7 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./DashWebRTCVideo.scss"; import { hangup, initialize, refreshVideos } from "./WebCamLogic"; import React = require("react"); +import { CurrentUserUtils } from "../../util/CurrentUserUtils"; /** @@ -69,7 +70,7 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV </div >; const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; - const classname = "webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); + const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : ""); return ( <> diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 9147fc33f..2f9dc7f51 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -903,9 +903,6 @@ export namespace Doc { export function UserDoc(): Doc { return manager._user_doc; } export function SharingDoc(): Doc { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); } export function LinkDBDoc(): Doc { return Cast(Doc.UserDoc().myLinkDatabase, Doc, null); } - - export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } - export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } export function SetUserDoc(doc: Doc) { return (manager._user_doc = doc); } const isSearchMatchCache = computedFn(function IsSearchMatch(doc: Doc) { @@ -1060,31 +1057,31 @@ export namespace Doc { } } - /** - * Filters document in a container collection: - * all documents with the specified value for the specified key are included/excluded - * based on the modifiers :"check", "x", undefined - */ - export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers?: "remove" | "match" | "check" | "x" | undefined) { + // filters document in a container collection: + // all documents with the specified value for the specified key are included/excluded + // based on the modifiers :"check", "x", undefined + export function setDocFilter(container: Opt<Doc>, key: string, value: any, modifiers: "remove" | "match" | "check" | "x", toggle?: boolean, fieldSuffix?: string) { if (!container) return; - const docFilters = Cast(container._docFilters, listSpec("string"), []); + const filterField = "_" + (fieldSuffix ? fieldSuffix + "-" : "") + "docFilters"; + const docFilters = Cast(container[filterField], listSpec("string"), []); runInAction(() => { for (let i = 0; i < docFilters.length; i++) { const fields = docFilters[i].split(":"); // split key:value:modifier if (fields[0] === key && (fields[1] === value || modifiers === "match" || modifiers === "remove")) { - if (fields[2] === modifiers && modifiers && fields[1] === value) return; + if (fields[2] === modifiers && modifiers && fields[1] === value) { + if (toggle) modifiers = "remove"; + else return; + } docFilters.splice(i, 1); - container._docFilters = new List<string>(docFilters); + container[filterField] = new List<string>(docFilters); break; } } - if (typeof modifiers === "string") { - if (!docFilters.length && modifiers === "match" && value === undefined) { - container._docFilters = undefined; - } else if (modifiers !== "remove") { - docFilters.push(key + ":" + value + ":" + modifiers); - container._docFilters = new List<string>(docFilters); - } + if (!docFilters.length && modifiers === "match" && value === undefined) { + container[filterField] = undefined; + } else if (modifiers !== "remove") { + docFilters.push(key + ":" + value + ":" + modifiers); + container[filterField] = new List<string>(docFilters); } }); } @@ -1335,5 +1332,5 @@ Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: bo (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; }); -Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers?: "match" | "check" | "x" | undefined) { Doc.setDocFilter(container, key, value, modifiers); }); +Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers: "match" | "check" | "x" | "remove") { Doc.setDocFilter(container, key, value, modifiers); }); Scripting.addGlobal(function setDocFilterRange(container: Doc, key: string, range: number[]) { Doc.setDocFilterRange(container, key, range); }); diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index da3dbecbe..1001d9e19 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -76,7 +76,7 @@ export class MobileInterface extends React.Component { componentDidMount = () => { // if the home menu is in list view -> adjust the menu toggle appropriately this._menuListView = this._homeDoc._viewType === "stacking" ? true : false; - Doc.SetSelectedTool(InkTool.None); // ink should intially be set to none + CurrentUserUtils.SelectedTool = InkTool.None; // ink should intially be set to none Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home AudioBox.Enabled = true; @@ -419,10 +419,10 @@ export class MobileInterface extends React.Component { button.style.color = this._ink ? "black" : "white"; if (!this._ink) { - Doc.SetSelectedTool(InkTool.Pen); + CurrentUserUtils.SelectedTool = InkTool.Pen; this._ink = true; } else { - Doc.SetSelectedTool(InkTool.None); + CurrentUserUtils.SelectedTool = InkTool.None; this._ink = false; } } |