diff options
author | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-04-04 13:38:55 -0400 |
---|---|---|
committer | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-04-04 13:38:55 -0400 |
commit | 15c1c952c50bb2de972c614e46c5f33c2b2952cc (patch) | |
tree | e8d7330336b80939e82749290c22ef5a05b01224 /src/client/views/nodes/DocumentView.tsx | |
parent | 479dff344ff2cf92ace9c68c3ce6d03e6e6dce22 (diff) | |
parent | ee17752109ba1238d645a4df7cee1cf60855f8df (diff) |
merging
Diffstat (limited to 'src/client/views/nodes/DocumentView.tsx')
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 82 |
1 files changed, 38 insertions, 44 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 231c9ff38..1c4008a55 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,18 +1,19 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction, reaction, IReactionDisposer } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast } from "../../../fields/Doc"; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from "../../../fields/List"; +import { ObjectField } from "../../../fields/ObjectField"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types"; import { AudioField } from "../../../fields/URLField"; import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; -import { emptyFunction, hasDescendantTarget, OmitKeys, returnFalse, returnVal, Utils } from "../../../Utils"; +import { emptyFunction, hasDescendantTarget, OmitKeys, returnVal, Utils } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; @@ -34,19 +35,18 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { InkingStroke } from "../InkingStroke"; +import { LightboxView } from "../LightboxView"; import { StyleLayers, StyleProp } from "../StyleProvider"; import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; import { DocumentContentsView } from "./DocumentContentsView"; import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; -import { FieldViewProps } from "./FieldView"; import { LinkAnchorBox } from './LinkAnchorBox'; import { LinkDocPreview } from "./LinkDocPreview"; import { PresBox } from './PresBox'; import { RadialMenu } from './RadialMenu'; import React = require("react"); -import { ObjectField } from "../../../fields/ObjectField"; -import { LightboxView } from "../LightboxView"; +import { ScriptingBox } from "./ScriptingBox"; const { Howl } = require('howler'); interface Window { @@ -75,7 +75,7 @@ export interface DocFocusOptions { } export type DocAfterFocusFunc = (notFocused: boolean) => Promise<ViewAdjustment>; export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void; -export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string) => any; +export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any; export interface DocComponentView { getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus @@ -86,6 +86,7 @@ export interface DocComponentView { getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; + setFocus?: () => void; } export interface DocumentViewSharedProps { renderDepth: number; @@ -106,11 +107,10 @@ export interface DocumentViewSharedProps { docFilters: () => string[]; docRangeFilters: () => string[]; searchFilterDocs: () => Doc[]; - contentsActive?: (setActive: () => boolean) => void; - parentActive: (outsideReaction: boolean) => boolean; - whenActiveChanged: (isActive: boolean) => void; + whenChildContentsActiveChanged: (isActive: boolean) => void; rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected addDocTab: (doc: Doc, where: string) => boolean; + filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example) addDocument?: (doc: Doc | Doc[]) => boolean; removeDocument?: (doc: Doc | Doc[]) => boolean; moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean; @@ -119,8 +119,9 @@ export interface DocumentViewSharedProps { bringToFront: (doc: Doc, sendToBack?: boolean) => void; dropAction?: dropActionType; dontRegisterView?: boolean; + hideLinkButton?: boolean; ignoreAutoHeight?: boolean; - cantBrush?: boolean; // whether the document doesn't show brush highlighting + disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over. pointerEvents?: string; scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document createNewFilterDoc?: () => void; @@ -133,6 +134,8 @@ export interface DocumentViewProps extends DocumentViewSharedProps { 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; + isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events + isContentActive: () => boolean | undefined; // whether a document should handle pointer events 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[]; LayoutTemplateString?: string; @@ -177,7 +180,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class private get topMost() { return this.props.renderDepth === 0; } - private get active() { return this.props.isSelected(true) || this.props.parentActive(true); } public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive public get ContentDiv() { return this._mainCont.current; } public get LayoutFieldKey() { return Doc.LayoutFieldKey(this.layoutDoc); } @@ -281,7 +283,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps this._downX = touch.clientX; this._downY = touch.clientY; if (!e.nativeEvent.cancelBubble) { - if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation(); + if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation(); this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); @@ -292,10 +294,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => { - if (e.cancelBubble && this.active) { + if (e.cancelBubble && this.props.isDocumentActive?.()) { this.removeMoveListeners(); } - else if (!e.cancelBubble && (this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { + else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { const touch = me.touchEvent.changedTouches.item(0); if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) { @@ -407,9 +409,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true), 0); else if (!this._titleRef.current.setIsFocused(true)) { // if focus didn't change, focus on interior text... this._titleRef.current?.setIsFocused(false); - const any = (this._mainCont.current?.getElementsByClassName("ProseMirror")?.[0] as any); - any.keeplocation = true; - any?.focus(); + this._componentView?.setFocus?.(); } } } @@ -440,16 +440,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps clearTimeout(this._timeout); this._timeout = undefined; } - if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself + if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) { // bcz: hack? don't execute script if you're clicking on a scripting box itself + const { clientX, clientY, shiftKey } = e; const func = () => this.onDoubleClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, scriptContext: this.props.scriptContext, thisContainer: this.props.ContainingCollectionDoc, documentView: this.props.DocumentView(), - clientX: e.clientX, - clientY: e.clientY, - shiftKey: e.shiftKey + clientX, clientY, shiftKey }, console.log); UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on double click"); } else if (!Doc.IsSystem(this.rootDoc)) { @@ -459,28 +458,23 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } Doc.UnBrushDoc(this.props.Document); } - } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) { // bcz: hack? don't execute script if you're clicking on a scripting box itself - const shiftKey = e.shiftKey; - const clientX = e.clientX; - const clientY = e.clientY; + } else if (this.onClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) { // bcz: hack? don't execute script if you're clicking on a scripting box itself + const { clientX, clientY, shiftKey } = e; const func = () => this.onClickHandler.script.run({ this: this.layoutDoc, self: this.rootDoc, scriptContext: this.props.scriptContext, thisContainer: this.props.ContainingCollectionDoc, documentView: this.props.DocumentView(), - clientX: clientX, - clientY: clientY, - shiftKey - }, console.log); - const clickFunc = () => this.props.Document.dontUndo ? func() : - UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on click"); + clientX, clientY, shiftKey + }, console.log).result?.select === true ? this.props.select(false) : ""; + const clickFunc = () => this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, "on click"); if (this.onDoubleClickHandler) { this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 350); } else clickFunc(); } else if (this.Document["onClick-rawScript"] && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes("ScriptingBox")) {// bcz: hack? don't edit a script if you're clicking on a scripting box itself this.props.addDocTab(DocUtils.makeCustomViewClicked(Doc.MakeAlias(this.props.Document), undefined, "onClick"), "add:right"); - } else if (this.allLinks && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { + } else if (this.allLinks && this.Document.type !== DocumentType.LINK && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) { this.allLinks.length && LinkManager.FollowLink(undefined, this.props.Document, this.props, e.altKey); } else { if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) { // onDragStart implies a button doc that we don't want to select when clicking. RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part @@ -512,7 +506,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps 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))) { - if ((this.active || this.layoutDoc.onDragStart) && + if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !e.ctrlKey && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { @@ -529,10 +523,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps onPointerMove = (e: PointerEvent): void => { if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) return; - if (e.cancelBubble && this.active) { + if (e.cancelBubble && this.props.isDocumentActive?.()) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (this.active || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { + else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { document.removeEventListener("pointermove", this.onPointerMove); @@ -757,14 +751,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; panelHeight = () => this.props.PanelHeight() - this.headerMargin; - parentActive = (outsideReaction: boolean) => this.props.layerProvider?.(this.layoutDoc) === false ? this.props.parentActive(outsideReaction) : false; screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); contentScaling = () => this.ContentScale; onClickFunc = () => this.onClickHandler; setHeight = (height: number) => this.layoutDoc._height = height; setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; - @observable contentsActive: () => boolean = returnFalse; - @action setContentsActive = (setActive: () => boolean) => this.contentsActive = setActive; + isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() ? true : false; @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : @@ -788,8 +780,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps scaling={this.contentScaling} PanelHeight={this.panelHeight} setHeight={this.setHeight} - contentsActive={this.setContentsActive} - parentActive={this.parentActive} + isContentActive={this.isContentActive} ScreenToLocalTransform={this.screenToLocal} rootSelected={this.rootSelected} onClick={this.onClickFunc} @@ -813,8 +804,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true) anchorPanelWidth = () => this.props.PanelWidth() || 1; anchorPanelHeight = () => this.props.PanelHeight() || 1; - anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps | FieldViewProps>, property: string): any => { - return property !== StyleProp.LinkSource ? this.props.styleProvider?.(doc, props, property + ":anchor") : this.props.Document; // pass the LinkSource to the LinkAnchorBox + anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => { + switch (property) { + case StyleProp.PointerEvents: return "none"; + case StyleProp.LinkSource: return this.props.Document;// pass the LinkSource to the LinkAnchorBox + default: return this.props.styleProvider?.(doc, props, property); + } } @computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); } @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); } @@ -961,7 +956,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"])[highlightIndex]; const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex]; const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON]; - let highlighting = !this.props.cantBrush && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear; + let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear; highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]"; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way const boxShadow = highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` : @@ -1040,7 +1035,6 @@ export class DocumentView extends React.Component<DocumentViewProps> { @computed get centeringY() { return this.fitWidth || this.props.dontCenter?.includes("y") ? 0 : this.Yshift; } toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight()); - contentsActive = () => this.docView?.contentsActive(); focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options); getBounds = () => { if (!this.docView || !this.docView.ContentDiv || this.docView.props.renderDepth === 0 || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { |