aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DocumentView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/DocumentView.tsx')
-rw-r--r--src/client/views/nodes/DocumentView.tsx275
1 files changed, 188 insertions, 87 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8682a43e6..b80d4579a 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -2,7 +2,7 @@ import { IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
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 { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, StrListCast, WidthSym } from "../../../fields/Doc";
import { Document } from '../../../fields/documentSchemas';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -14,7 +14,7 @@ import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from "../../.
import { AudioField } from "../../../fields/URLField";
import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnVal, simulateMouseClick, Utils } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnFalse, returnVal, simulateMouseClick, Utils } from "../../../Utils";
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from '../../documents/DocumentTypes';
@@ -37,7 +37,7 @@ import { DocComponent } from "../DocComponent";
import { EditableView } from '../EditableView';
import { InkingStroke } from "../InkingStroke";
import { LightboxView } from "../LightboxView";
-import { StyleLayers, StyleProp } from "../StyleProvider";
+import { StyleProp } from "../StyleProvider";
import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
import { DocumentContentsView } from "./DocumentContentsView";
import { DocumentLinksButton } from './DocumentLinksButton';
@@ -49,6 +49,8 @@ import { RadialMenu } from './RadialMenu';
import { ScriptingBox } from "./ScriptingBox";
import { PresBox } from './trails/PresBox';
import React = require("react");
+import { DocServer } from "../../DocServer";
+import { FieldViewProps } from "./FieldView";
const { Howl } = require('howler');
interface Window {
@@ -79,10 +81,11 @@ export type DocAfterFocusFunc = (notFocused: boolean) => Promise<ViewAdjustment>
export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void;
export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
export interface DocComponentView {
+ updateIcon?: () => void; // updates the icon representation of the document
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
setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
- reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
+ reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views
menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
isAnyChildContentActive?: () => boolean; // is any child content of the document active
@@ -102,20 +105,28 @@ export interface DocComponentView {
snapPt?: (pt: { X: number, Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number, Y: number }, distance: number };
search?: (str: string, bwd?: boolean, clear?: boolean) => boolean;
}
+// These props are passed to both FieldViews and DocumentViews
export interface DocumentViewSharedProps {
+ fieldKey?: string; // only used by FieldViews but helpful here to allow styleProviders to access fieldKey of FieldViewProps. In priniciple, passing a fieldKey to a documentView could override or be the default fieldKey for fieldViews
+ DocumentView?: () => DocumentView;
renderDepth: number;
Document: Doc;
DataDoc?: Doc;
- fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
+ contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
+ fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
ContainingCollectionView: Opt<CollectionView>;
ContainingCollectionDoc: Opt<Doc>;
+ suppressSetHeight?: boolean;
thumbShown?: () => boolean;
+ isHovering?: () => boolean;
setContentView?: (view: DocComponentView) => any;
CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
PanelWidth: () => number;
PanelHeight: () => number;
docViewPath: () => DocumentView[];
- layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean);
+ childHideDecorationTitle?: () => boolean;
+ childHideResizeHandles?: () => boolean;
+ dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
styleProvider: Opt<StyleProviderFunc>;
focus: DocFocusFunc;
fitWidth?: (doc: Doc) => boolean;
@@ -140,7 +151,7 @@ export interface DocumentViewSharedProps {
ignoreAutoHeight?: boolean;
forceAutoHeight?: boolean;
disableDocBrushing?: boolean; // should highlighting for this view be disabled when same document in another view is hovered over.
- pointerEvents?: string;
+ pointerEvents?: () => Opt<string>;
scriptContext?: any; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
createNewFilterDoc?: () => void;
updateFilterDoc?: (doc: Doc) => void;
@@ -148,12 +159,16 @@ export interface DocumentViewSharedProps {
originalBackgroundColor?: string;
isNoteTakingView?: boolean;
}
+
+// these props are specific to DocuentViews
export interface DocumentViewProps extends DocumentViewSharedProps {
// properties specific to DocumentViews but not to FieldView
- 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
+ hideDocumentButtonBar?: boolean;
+ hideOpenButton?: boolean;
+ hideDeleteButton?: boolean;
treeViewDoc?: Doc;
isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
@@ -161,6 +176,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
radialMenu?: String[];
LayoutTemplateString?: string;
dontCenter?: "x" | "y" | "xy";
+ dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
NativeWidth?: () => number;
NativeHeight?: () => number;
@@ -170,8 +186,11 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
onDoubleClick?: () => ScriptField;
onPointerDown?: () => ScriptField;
onPointerUp?: () => ScriptField;
+ onBrowseClick?: () => (ScriptField | undefined);
+ onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
}
+// these props are only available in DocumentViewIntenral
export interface DocumentViewInternalProps extends DocumentViewProps {
NativeWidth: () => number;
NativeHeight: () => number;
@@ -184,6 +203,7 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
@observer
export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() {
public static SelectAfterContextMenu = true; // whether a document should be selected after it's contextmenu is triggered.
+ _animateScaleTime = 300; // milliseconds;
@observable _animateScalingTo = 0;
@observable _mediaState = 0;
@observable _pendingDoubleClick = false;
@@ -209,7 +229,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); }
@computed get ContentScale() { return this.props.ContentScaling?.() || 1; }
@computed get thumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); }
- @computed get hidden() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Hidden); }
+ @computed get hidden() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden); }
@computed get opacity() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity); }
@computed get boxShadow() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow); }
@computed get borderRounding() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); }
@@ -223,7 +243,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get finalLayoutKey() { return StrCast(this.Document.layoutKey, "layout"); }
@computed get nativeWidth() { return this.props.NativeWidth(); }
@computed get nativeHeight() { return this.props.NativeHeight(); }
- @computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
+ @computed get onClickHandler() { return this.props.onClick?.() ?? (this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null))); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
@computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
@@ -239,6 +259,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this._holdDisposer = InteractionUtils.MakeHoldTouchTarget(this._mainCont.current, this.handle1PointerHoldStart.bind(this));
}
}
+ @action
cleanupHandlers(unbrush: boolean) {
this._dropDisposer?.();
this._multiTouchDisposer?.();
@@ -308,7 +329,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this._downX = touch.clientX;
this._downY = touch.clientY;
if (!e.nativeEvent.cancelBubble) {
- if ((this.props.isDocumentActive?.() || 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 && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
this.removeMoveListeners();
this.addMoveListeners();
this.removeEndListeners();
@@ -322,7 +343,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (e.cancelBubble && this.props.isDocumentActive?.()) {
this.removeMoveListeners();
}
- else if (!e.cancelBubble && (this.props.isDocumentActive?.() || 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 && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).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)) {
@@ -420,6 +441,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
dragData.offset = this.props.ScreenToLocalTransform().scale(this.ContentScale).transformDirection(x - left, y - top);
+ dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]);
+ dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
dragData.dropAction = dropAction;
dragData.treeViewDoc = this.props.treeViewDoc;
dragData.removeDocument = this.props.removeDocument;
@@ -467,7 +490,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
});
// after a timeout, the right _componentView should have been created, so call it to update its view spec values
setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
- const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
+ const focusSpeed = this._componentView?.scrollFocus?.(anchor, !options?.instant || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing;
this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
...options, afterFocus: (didFocus: boolean) =>
@@ -480,10 +503,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
(Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
let stopPropagate = true;
let preventDefault = true;
- !StrListCast(this.props.Document._layerTags).includes(StyleLayers.Background) && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+ const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
+ (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !this.onClickHandler?.script) { // disable double-click to show full screen for things that have an on click behavior since clicking them twice can be misinterpreted as a double click
if (this._timeout) {
clearTimeout(this._timeout);
+ this._pendingDoubleClick = false;
this._timeout = undefined;
}
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
@@ -499,12 +524,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on double click");
} else if (!Doc.IsSystem(this.rootDoc)) {
UndoManager.RunInBatch(() =>
- LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.())
+ LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.(), this.props.addDocTab)
, "double tap");
SelectionManager.DeselectAll();
Doc.UnBrushDoc(this.props.Document);
}
- } 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
+ } else if (this.onClickHandler?.script && !isScriptBox()) { // 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,
@@ -517,9 +542,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}, console.log).result?.select === true ? this.props.select(false) : "";
const clickFunc = () => this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, "on click");
if (this.onDoubleClickHandler) {
+ runInAction(() => this._pendingDoubleClick = true);
this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 350);
} else clickFunc();
- } else if (this.allLinks && this.Document.type !== DocumentType.LINK && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
+ } else if (this.allLinks && this.Document.type !== DocumentType.LINK && !isScriptBox() && 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
@@ -537,8 +563,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
});
onPointerDown = (e: React.PointerEvent): void => {
+ if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.ActiveTool === InkTool.Eraser) return;
// 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) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) {
+ if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) {
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
@@ -552,10 +579,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
// 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.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
+ !this.props.onBrowseClick?.() &&
!this.Document.ignoreClick &&
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
- !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+ !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
e.stopPropagation();
// don't preventDefault anymore. Goldenlayout, PDF text selection and RTF text selection all need it to go though
//if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
@@ -571,9 +599,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onPointerMove = (e: PointerEvent): void => {
if (e.cancelBubble) return;
- if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool))) return;
+ if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) return;
- if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+ if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).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);
@@ -605,12 +633,16 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
@undoBatch @action
- toggleFollowLink = (location: Opt<string>, zoom: boolean, setPushpin: boolean): void => {
+ toggleFollowLink = (location: Opt<string>, zoom?: boolean, setPushpin?: boolean): void => {
this.Document.ignoreClick = false;
- this.Document._isLinkButton = !this.Document._isLinkButton;
- setPushpin && (this.Document.isPushpin = this.Document._isLinkButton);
+ if (setPushpin) {
+ this.Document.isPushpin = !this.Document.isPushpin;
+ this.Document._isLinkButton = this.Document.isPushpin || this.Document._isLinkButton;
+ } else {
+ this.Document._isLinkButton = !this.Document._isLinkButton;
+ }
if (this.Document._isLinkButton && !this.onClickHandler) {
- this.Document.followLinkZoom = zoom;
+ zoom !== undefined && (this.Document.followLinkZoom = zoom);
this.Document.followLinkLocation = location;
} else if (this.Document._isLinkButton && this.onClickHandler) {
this.Document._isLinkButton = false;
@@ -665,7 +697,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
- de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, "link", undefined, undefined, undefined, [de.x, de.y]);
+ de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
}
}
}
@@ -676,7 +708,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
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), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" });
- DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to");
+ DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to:portal from");
}
this.Document.followLinkLocation = "inPlace";
this.Document.followLinkZoom = true;
@@ -685,7 +717,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@action
onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
- if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) {
+ if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
e.preventDefault();
e.stopPropagation();
//!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
@@ -735,13 +767,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
const appearance = cm.findByDescription("UI Controls...");
const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
- !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
- !Doc.UserDoc().noviceMode && appearanceItems.push({
+ !Doc.noviceMode && templateDoc && appearanceItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
+ !Doc.noviceMode && appearanceItems.push({
description: "Add a Field", event: () => {
const alias = Doc.MakeAlias(this.rootDoc);
alias.layout = FormattedTextBox.LayoutString("newfield");
alias.title = "newfield";
- alias._yMargin = 10;
alias._height = 35;
alias._width = 100;
alias.syncLayoutFieldWithTitle = true;
@@ -753,8 +784,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
!appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
- if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
- !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
+ if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+ !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
const existingOnClick = cm.findByDescription("OnClick...");
const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
@@ -765,11 +796,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
}
- !zorders && cm.addItem({ description: "ZOrder...", subitems: zorderItems, icon: "compass" });
+ !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" });
- onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
- !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
- onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
+ !Doc.noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
+ !Doc.noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
+ this.props.CollectionFreeFormDocumentView && onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
if (!this.Document.annotationOn) {
const options = cm.findByDescription("Options...");
@@ -792,7 +823,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const funcs: ContextMenuProps[] = [];
- if (!Doc.UserDoc().noviceMode && this.layoutDoc.onDragStart) {
+ if (!Doc.noviceMode && this.layoutDoc.onDragStart) {
funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
@@ -802,8 +833,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const more = cm.findByDescription("More...");
const moreItems = more && "subitems" in more ? more.subitems : [];
if (!Doc.IsSystem(this.rootDoc)) {
- (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.UserDoc().noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
- if (!Doc.UserDoc().noviceMode) {
+ (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
+ if (!Doc.noviceMode) {
moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" });
@@ -819,13 +850,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && CurrentUserUtils.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
}
+ !more && moreItems.length && cm.addItem({ description: "More...", subitems: moreItems, icon: "compass" });
const help = cm.findByDescription("Help...");
const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
- !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
- helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
- !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
- !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
+ helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+ !Doc.noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+ !Doc.noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+ !Doc.noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
}
@@ -844,15 +876,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
contentScaling = () => this.ContentScale;
onClickFunc = () => this.onClickHandler;
- setHeight = (height: number) => {
- if (this.props.renderDepth !== -1) {
- this.layoutDoc._height = height;
- }
- }
+ setHeight = (height: number) => this.layoutDoc._height = height;
setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
isContentActive = (outsideReaction?: boolean) => {
return this.props.isContentActive() === false ? false : (
- CurrentUserUtils.SelectedTool !== InkTool.None ||
+ CurrentUserUtils.ActiveTool !== InkTool.None ||
SnappingManager.GetIsDragging() ||
this.rootSelected() ||
this.props.Document.forceActive ||
@@ -861,8 +889,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.props.isContentActive()) ? true : undefined;
}
@observable _retryThumb = 1;
- thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
- !this._componentView?.isAnyChildContentActive?.() ? true : false;
+ thumbShown = () => {
+ return !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+ !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
+ !Doc.isBrushedHighlightedDegree(this.props.Document) &&
+ !this._componentView?.isAnyChildContentActive?.() ? true : false;
+ }
@computed get contents() {
TraceMobx();
const audioView = !this.layoutDoc._showAudio ? (null) :
@@ -871,13 +903,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }}
icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
</div>;
+
return <div className="documentView-contentsView"
style={{
- pointerEvents: this.props.pointerEvents as any ? this.props.pointerEvents as any : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
+ pointerEvents: this.props.pointerEvents?.() as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" :
+ this.props.contentPointerEvents as any ? this.props.contentPointerEvents as any :
+ this.rootDoc.type !== DocumentType.INK && this.isContentActive() ? "all" :
+ "none",
height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
}}>
{!this._retryThumb || !this.thumbShown() ? (null) :
- <img style={{ background: "white", top: 0, position: "absolute" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
+ <img style={{ background: "white", top: 0, position: "relative" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
width={this.props.PanelWidth()} height={this.props.PanelHeight()}
onError={(e: any) => {
setTimeout(action(() => this._retryThumb = 0), 0);
@@ -887,10 +923,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{...this.props}
docViewPath={this.props.viewPath}
thumbShown={this.thumbShown}
+ isHovering={this.isHovering}
setContentView={this.setContentView}
scaling={this.contentScaling}
PanelHeight={this.panelHeight}
- setHeight={this.setHeight}
+ setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
isContentActive={this.isContentActive}
ScreenToLocalTransform={this.screenToLocal}
rootSelected={this.rootSelected}
@@ -898,10 +935,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
focus={this.focus}
layoutKey={this.finalLayoutKey} />
{this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
- {this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
+ {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
<DocumentLinksButton View={this.props.DocumentView()}
ContentScaling={this.props.ContentScaling}
- Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
+ Offset={[this.topMost ? 0 : !this.props.isSelected() ? - 15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? - 15 : -30]} />
}
{audioView}
</div>;
@@ -943,11 +980,12 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get allLinkEndpoints() { // the small blue dots that mark the endpoints of links
TraceMobx();
if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
- if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
+ if (this.rootDoc.type=== DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
return filtered.map((link, i) =>
<div className="documentView-anchorCont" key={link[Id]}>
<DocumentView {...this.props}
+ isContentActive={returnFalse}
Document={link}
PanelWidth={this.anchorPanelWidth}
PanelHeight={this.anchorPanelHeight}
@@ -1041,7 +1079,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
/>
</div>;
const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
- const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor, [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
+ const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
+ Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
const titleView = !showTitle ? (null) :
<div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
position: this.headerMargin ? "relative" : "absolute",
@@ -1079,24 +1118,27 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
{captionView}
</div>;
}
+ isHovering = () => this._isHovering;
+ @observable _isHovering = false;
@observable _: string = "";
@computed get renderDoc() {
TraceMobx();
const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
const isButton = this.props.Document.type === DocumentType.FONTICON;
- if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
+ if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || (this.hidden && !this.props.treeViewDoc)) return null;
return this.docContents ??
<div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
id={this.props.Document[Id]}
+ onPointerEnter={action(() => this._isHovering = true)}
+ onPointerLeave={action(() => this._isHovering = false)}
style={{
background: isButton || thumb ? undefined : this.backgroundColor,
opacity: this.opacity,
color: StrCast(this.layoutDoc.color, "inherit"),
fontFamily: StrCast(this.Document._fontFamily, "inherit"),
fontSize: Cast(this.Document._fontSize, "string", null),
- transformOrigin: this._animateScalingTo ? "center center" : undefined,
transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
- transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
+ transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this._animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
}}>
{this.innards}
@@ -1106,9 +1148,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
render() {
TraceMobx();
- const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
- const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "yellow", "magenta", "cyan", "orange"][highlightIndex];
- const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex];
+ const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : Doc.DocBrushStatus.unbrushed) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
+ const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange", "lightBlue"][highlightIndex];
+ const highlightStyle = ["solid", "dashed", "solid", "solid", "solid"][highlightIndex];
const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
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
@@ -1119,14 +1161,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
// Return surrounding highlight
- return <div className={DocumentView.ROOT_DIV} ref={this._mainCont}
+ return <div className={`${DocumentView.ROOT_DIV} docView-hack`} ref={this._mainCont}
onContextMenu={this.onContextMenu}
onKeyDown={this.onKeyDown}
onPointerDown={this.onPointerDown}
onClick={this.onClick}
- onPointerEnter={e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document)}
- onPointerLeave={e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document)}
+ onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
+ onPointerLeave={action(e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document))}
style={{
+ display: this.hidden ? "inline" : undefined,
borderRadius: this.borderRounding,
pointerEvents: this.pointerEvents,
outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
@@ -1164,8 +1207,24 @@ export class DocumentView extends React.Component<DocumentViewProps> {
public ContentRef = React.createRef<HTMLDivElement>();
private _disposers: { [name: string]: IReactionDisposer } = {};
+ public static showBackLinks(linkSource: Doc) {
+ const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + "-pivotish";
+ DocServer.GetRefField(docid).then(docx => {
+ const rootAlias = () => {
+ const rootAlias = Doc.MakeAlias(linkSource);
+ rootAlias.x = rootAlias.y = 0;
+ return rootAlias;
+ };
+ const linkCollection = ((docx instanceof Doc) && docx) || Docs.Create.StackingDocument([/*rootAlias()*/], { title: linkSource.title + "-pivot", _width: 500, _height: 500, }, docid);
+ linkCollection.linkSource = linkSource;
+ if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript("updateLinkCollection(self)");
+ LightboxView.SetLightboxDoc(linkCollection);
+ });
+ }
+
@observable public docView: DocumentViewInternal | undefined | null;
+ showContextMenu(pageX:number, pageY:number) { return this.docView?.onContextMenu(undefined, pageX, pageY); }
get Document() { return this.props.Document; }
get topMost() { return this.props.renderDepth === 0; }
get rootDoc() { return this.docView?.rootDoc || this.Document; }
@@ -1181,13 +1240,17 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
@computed get nativeWidth() {
return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
- returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
+ returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
}
@computed get nativeHeight() {
return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
- returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
+ returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
+ }
+ @computed get shouldNotScale() {
+ return (this.fitWidth && !this.nativeWidth) ||
+ this.props.dontScaleFilter?.(this.Document) ||
+ this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
}
- @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || this.props.treeViewDoc || [CollectionViewType.Docking].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() {
@@ -1201,20 +1264,21 @@ export class DocumentView extends React.Component<DocumentViewProps> {
@computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); }
@computed get panelHeight() {
- if (this.effectiveNativeHeight) {
- return Math.min(this.props.PanelHeight(), Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight), this.effectiveNativeHeight) * this.nativeScaling);
+ if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) {
+ const scrollHeight = this.fitWidth ? Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight)) : 0;
+ return Math.min(this.props.PanelHeight(), Math.max(scrollHeight, this.effectiveNativeHeight) * this.nativeScaling);
}
return this.props.PanelHeight();
}
@computed get Xshift() { return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0; }
- @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 ? (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2 : 0; }
+ @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && !this.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0; }
@computed get centeringX() { return this.props.dontCenter?.includes("x") ? 0 : this.Xshift; }
- @computed get centeringY() { return this.fitWidth || this.props.dontCenter?.includes("y") ? 0 : this.Yshift; }
+ @computed get centeringY() { return this.props.dontCenter?.includes("y") ? 0 : this.Yshift; }
toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
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())) {
+ if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
return undefined;
}
const xf = (this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling)).inverse();
@@ -1226,15 +1290,17 @@ export class DocumentView extends React.Component<DocumentViewProps> {
return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) };
}
- public iconify() {
+ public iconify(finished?: () => void) {
+ this.ComponentView?.updateIcon?.();
const layoutKey = Cast(this.Document.layoutKey, "string", null);
if (layoutKey !== "layout_icon") {
- this.switchViews(true, "icon");
+ this.switchViews(true, "icon", finished);
if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.Document.deiconifyLayout = layoutKey.replace("layout_", "");
} else {
const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null);
- this.switchViews(deiconifyLayout ? true : false, deiconifyLayout);
+ this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finished);
this.Document.deiconifyLayout = undefined;
+ this.props.bringToFront(this.rootDoc);
}
}
@undoBatch
@@ -1243,13 +1309,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
Doc.setNativeView(this.props.Document);
custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
}
- switchViews = action((custom: boolean, view: string) => {
+ switchViews = action((custom: boolean, view: string, finished?: () => void) => {
this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc
setTimeout(action(() => {
this.setCustomView(custom, view);
this.docView && (this.docView._animateScalingTo = 1); // expand it
- setTimeout(action(() => this.docView && (this.docView._animateScalingTo = 0)), 400);
- }), 400);
+ setTimeout(action(() => {
+ this.docView && (this.docView._animateScalingTo = 0);
+ finished?.();
+ }), this.docView!._animateScaleTime - 10);
+ }), this.docView!._animateScaleTime - 10);
});
startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
@@ -1263,10 +1332,12 @@ export class DocumentView extends React.Component<DocumentViewProps> {
PanelHeight = () => this.panelHeight;
ContentScale = () => this.nativeScaling;
selfView = () => this;
- screenToLocalTransform = () => {
- return this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).scale(1 / this.nativeScaling);
- }
+ screenToLocalTransform = () =>this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).scale(1 / this.nativeScaling);
componentDidMount() {
+ this._disposers.reactionScript = reaction(
+ () => ScriptCast(this.rootDoc.reactionScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result,
+ output => output
+ );
this._disposers.height = reaction(
() => NumCast(this.layoutDoc._height),
action(height => {
@@ -1285,14 +1356,16 @@ export class DocumentView extends React.Component<DocumentViewProps> {
TraceMobx();
const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined);
const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
+ const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
return (<div className="contentFittingDocumentView">
{!this.props.Document || !this.props.PanelWidth() ? (null) : (
<div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
style={{
+ transition: this.props.dataTransition,
position: this.props.Document.isInkMask ? "absolute" : undefined,
transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
- width: isButton ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
+ width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
`${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
}}>
@@ -1308,14 +1381,42 @@ export class DocumentView extends React.Component<DocumentViewProps> {
ContentScaling={this.ContentScale}
ScreenToLocalTransform={this.screenToLocalTransform}
focus={this.props.focus || emptyFunction}
- bringToFront={emptyFunction}
ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} />
</div>)}
</div>);
}
}
+export function deiconifyViewFunc(documentView: DocumentView) {
+ documentView.iconify();
+ //StrCast(doc.layoutKey).split("_")[1] === "icon" && setNativeView(doc);
+}
+ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
+ documentView.iconify();
+ documentView.select(false);
+});
+
ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
else dv.switchViews(true, detailLayoutKeySuffix);
+});
+
+ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
+ const linkSource = Cast(linkCollection.linkSource, Doc, null);
+ const collectedLinks = DocListCast(Doc.GetProto(linkCollection).data);
+ let wid = linkSource[WidthSym]();
+ const links = DocListCast(linkSource.links);
+ links.forEach(link => {
+ const other = LinkManager.getOppositeAnchor(link, linkSource);
+ const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other;
+ if (otherdoc && !collectedLinks?.some(d => Doc.AreProtosEqual(d, otherdoc))) {
+ const alias = Doc.MakeAlias(otherdoc);
+ alias.x = wid;
+ alias.y = 0;
+ alias._lockedPosition = false;
+ wid += otherdoc[WidthSym]();
+ Doc.AddDocToList(Doc.GetProto(linkCollection), "data", alias);
+ }
+ });
+ return links;
}); \ No newline at end of file