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.tsx99
1 files changed, 53 insertions, 46 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 091a6b050..e24bf35f5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -12,7 +12,7 @@ import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { AudioField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { DocServer } from '../../DocServer';
import { Docs, DocUtils } from '../../documents/Documents';
@@ -108,6 +108,7 @@ export type StyleProviderFunc = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, p
export interface DocComponentView {
updateIcon?: () => void; // updates the icon representation of the document
getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => 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)
+ restoreView?: (viewSpec: Doc) => boolean;
scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt<number>; // returns the duration of the focus
brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void;
getView?: (doc: Doc) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
@@ -512,7 +513,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.props.isDocumentActive?.() &&
!this.props.onBrowseClick?.() &&
!this.Document.ignoreClick &&
- !e.ctrlKey &&
e.button === 0 &&
this.pointerEvents !== 'none' &&
!DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)
@@ -618,22 +618,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@undoBatch
@action
- drop = async (e: Event, de: DragManager.DropEvent) => {
+ drop = (e: Event, de: DragManager.DropEvent) => {
if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return;
if (this.props.Document === Doc.ActiveDashboard) {
alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
return;
}
const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
- if (linkdrag) linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
- if (linkdrag?.linkSourceDoc) {
- e.stopPropagation();
- if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
- de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
- }
- if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
- const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.props.Document;
- de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
+ if (linkdrag) {
+ linkdrag.linkSourceDoc = linkdrag.linkSourceGetAnchor();
+ if (linkdrag.linkSourceDoc) {
+ e.stopPropagation();
+ if (de.complete.annoDragData && !de.complete.annoDragData.dropDocument) {
+ de.complete.annoDragData.dropDocument = de.complete.annoDragData.dropDocCreator(undefined);
+ }
+ if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
+ const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.rootDoc;
+ de.complete.linkDocument = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, undefined, [de.x, de.y - 50]);
+ }
}
}
};
@@ -644,9 +646,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document && d.linkRelationship === 'portal to:portal from');
if (!portalLink) {
DocUtils.MakeLink(
- { doc: this.props.Document },
- { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) },
- 'portal to:portal from'
+ this.props.Document,
+ Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }),
+ { linkRelationship: 'portal to:portal from' }
);
}
this.Document.followLinkLocation = OpenWhere.lightbox;
@@ -654,6 +656,24 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
this.Document._isLinkButton = true;
};
+ importDocument = () => {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.zip';
+ input.onchange = _e => {
+ if (input.files) {
+ const batch = UndoManager.StartBatch('importing');
+ Doc.importDocument(input.files[0]).then(doc => {
+ if (doc instanceof Doc) {
+ this.props.addDocTab(doc, OpenWhere.addRight);
+ batch.end();
+ }
+ });
+ }
+ };
+ input.click();
+ };
+
@action
onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
@@ -705,25 +725,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
const appearance = cm.findByDescription('UI Controls...');
const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
!Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this.props.addDocTab(templateDoc, OpenWhere.addRight), 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._height = 35;
- alias._width = 100;
- alias.syncLayoutFieldWithTitle = true;
- alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width);
- alias.y = NumCast(this.rootDoc.y);
- this.props.addDocument?.(alias);
- },
- icon: 'eye',
- });
- LinkManager.Links(this.Document).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' });
+ !appearance && appearanceItems.length && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
const existingOnClick = cm.findByDescription('OnClick...');
@@ -791,16 +793,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' });
}
- moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
-
- (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
}
- if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) {
+ !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
+ }
+ const constantItems: ContextMenuProps[] = [];
+
+ if (!Doc.IsSystem(this.rootDoc)) {
+ constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
+ constantItems.push({ description: 'Import Zipped file', icon: 'upload', event: ({ x, y }) => this.importDocument() });
+ (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
+ if (this.props.removeDocument && Doc.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' });
+ constantItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
}
- !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
+ cm.addItem({ description: 'General...', noexpand: false, subitems: constantItems, icon: 'question' });
}
const help = cm.findByDescription('Help...');
const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
@@ -851,7 +858,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
icon: 'book',
});
}
- cm.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'question' });
+ if (!help) cm.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'question' });
+ else cm.moveAfter(help);
e?.stopPropagation(); // DocumentViews should stop propagation of this event
cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
@@ -863,13 +871,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
setHeight = (height: number) => (this.layoutDoc._height = height);
setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
- isContentActive = (outsideReaction?: boolean) => {
+ isContentActive = (outsideReaction?: boolean): boolean | undefined => {
return this.props.isContentActive() === false
? false
: Doc.ActiveTool !== InkTool.None ||
SnappingManager.GetIsDragging() ||
this.rootSelected() ||
- this.props.Document.forceActive ||
+ this.rootDoc.forceActive ||
this.props.isSelected(outsideReaction) ||
this._componentView?.isAnyChildContentActive?.() ||
this.props.isContentActive()
@@ -1064,7 +1072,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
};
- captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
+ captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
@computed get innards() {
TraceMobx();
const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1;
@@ -1079,7 +1087,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
maxHeight: `max(100%, ${20 * ffscale()}px)`,
}}>
<FormattedTextBox
- {...OmitKeys(this.props, ['children']).omit}
+ {...this.props}
yPadding={10}
xPadding={10}
fieldKey={this.showCaption}
@@ -1090,7 +1098,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
dontScale={true}
renderDepth={this.props.renderDepth}
isContentActive={this.isContentActive}
- onClick={this.onClickFunc}
/>
</div>
);