diff options
Diffstat (limited to 'src/client/views/nodes/DocumentView.tsx')
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 131 |
1 files changed, 72 insertions, 59 deletions
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6f5235c4a..6c944c6b2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -33,7 +33,6 @@ import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { MainView } from '../MainView'; import { OverlayView } from '../OverlayView'; -import { PresentationView } from "../presentationview/PresentationView"; import { ScriptBox } from '../ScriptBox'; import { ScriptingRepl } from '../ScriptingRepl'; import { Template } from "./../Templates"; @@ -41,7 +40,7 @@ import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import { FormattedTextBox } from './FormattedTextBox'; import React = require("react"); -import { IDisposable } from '../../northstar/utils/IDisposable'; +import { DocumentType } from '../../documents/DocumentTypes'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? library.add(fa.faTrash); @@ -98,6 +97,7 @@ export interface DocumentViewProps { whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc, sendToBack?: boolean) => void; addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void; + pinToPres: (document: Doc) => void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; zoomToScale: (scale: number) => void; backgroundColor: (doc: Doc) => string | undefined; @@ -139,6 +139,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu private _lastTap: number = 0; private _doubleTap = false; private _hitExpander = false; + private _hitTemplateDrag = false; private _mainCont = React.createRef<HTMLDivElement>(); private _dropDisposer?: DragManager.DragDropDisposer; _animateToIconDisposer?: IReactionDisposer; @@ -228,7 +229,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } get dataDoc() { - if (this.props.DataDoc === undefined && this.props.Document.layout instanceof Doc) { + if (this.props.DataDoc === undefined && (this.props.Document.layout instanceof Doc || this.props.Document instanceof Promise)) { // if there is no dataDoc (ie, we're not rendering a temlplate layout), but this document // has a template layout document, then we will render the template layout but use // this document as the data document for the layout. @@ -236,7 +237,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } return this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined; } - startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean) { + startDragging(x: number, y: number, dropAction: dropActionType, dragSubBullets: boolean, applyAsTemplate?: boolean) { if (this._mainCont.current) { let allConnected = [this.props.Document, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; let alldataConnected = [this.dataDoc, ...(dragSubBullets ? DocListCast(this.props.Document.subBulletDocs) : [])]; @@ -247,6 +248,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu dragData.xOffset = xoff; dragData.yOffset = yoff; dragData.moveDocument = this.props.moveDocument; + dragData.applyAsTemplate = applyAsTemplate; DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { handlers: { dragComplete: action(emptyFunction) @@ -293,8 +295,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu onClick = async (e: React.MouseEvent) => { if (e.nativeEvent.cancelBubble) return; // needed because EditableView may stopPropagation which won't apparently stop this event from firing. - e.stopPropagation(); if (this.onClickHandler && this.onClickHandler.script) { + e.stopPropagation(); this.onClickHandler.script.run({ this: this.props.Document.isTemplate && this.props.DataDoc ? this.props.DataDoc : this.props.Document }); e.preventDefault(); return; @@ -302,6 +304,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu let altKey = e.altKey; let ctrlKey = e.ctrlKey; if (this._doubleTap && this.props.renderDepth) { + e.stopPropagation(); let fullScreenAlias = Doc.MakeAlias(this.props.Document); fullScreenAlias.templates = new List<string>(); Doc.UseDetailLayout(fullScreenAlias); @@ -313,10 +316,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu else if (CurrentUserUtils.MainDocId !== this.props.Document[Id] && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { + if (BoolCast(this.props.Document.ignoreClick)) { + return; + } + e.stopPropagation(); SelectionManager.SelectDoc(this, e.ctrlKey); let isExpander = (e.target as any).id === "isExpander"; - if (BoolCast(this.props.Document.isButton) || isExpander) { - SelectionManager.DeselectAll(); + if (BoolCast(this.props.Document.isButton) || this.props.Document.type === DocumentType.BUTTON || isExpander) { let subBulletDocs = await DocListCastAsync(this.props.Document.subBulletDocs); let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); @@ -327,6 +333,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs; // let expandedDocs = [...(subBulletDocs ? subBulletDocs : []), ...(maximizedDocs ? maximizedDocs : []), ...(summarizedDocs ? summarizedDocs : []),]; if (expandedDocs.length) { // bcz: need a better way to associate behaviors with click events on widget-documents + SelectionManager.DeselectAll(); let maxLocation = StrCast(this.props.Document.maximizeLocation, "inPlace"); let getDispDoc = (target: Doc) => Object.getOwnPropertyNames(target).indexOf("isPrototype") === -1 ? target : Doc.MakeDelegate(target); if (altKey || ctrlKey) { @@ -355,6 +362,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } } else if (linkedDocs.length) { + SelectionManager.DeselectAll(); let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document)); let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]]; @@ -377,29 +385,35 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } } } + + onPointerDown = (e: React.PointerEvent): void => { if (e.nativeEvent.cancelBubble) return; this._downX = e.clientX; this._downY = e.clientY; this._hitExpander = DocListCast(this.props.Document.subBulletDocs).length > 0; - // if (e.shiftKey && e.buttons === 1 && CollectionDockingView.Instance) { - // CollectionDockingView.Instance.StartOtherDrag(e, [Doc.MakeAlias(this.props.Document)], [this.dataDoc]); - // e.stopPropagation(); - // } else { + this._hitTemplateDrag = false; + for (let element = (e.target as any); element && !this._hitTemplateDrag; element = element.parentElement) { + if (element.className && element.className.toString() === "collectionViewBaseChrome-collapse") { + this._hitTemplateDrag = true; + } + } if (this.active) e.stopPropagation(); // events stop at the lowest document that is active. document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointermove", this.onPointerMove); document.addEventListener("pointerup", this.onPointerUp); - // } } onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble && this.active) { + if (e.cancelBubble && this.active) { + document.removeEventListener("pointermove", this.onPointerMove); + } + else if (!e.cancelBubble && this.active) { if (!this.props.Document.excludeFromLibrary && (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3)) { if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.props.Document.lockedPosition)) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); - this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander); + this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitExpander, this._hitTemplateDrag); } } 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 @@ -457,6 +471,10 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu DocUtils.MakeLink(annotationDoc, targetDoc, this.props.ContainingCollectionView!.props.Document, `Link from ${StrCast(annotationDoc.title)}`); } + if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) { + Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document, this.props.DataDoc); + e.stopPropagation(); + } if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.Document; @@ -573,23 +591,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); let existingMake = ContextMenu.Instance.findByDescription("Make..."); let makes: ContextMenuProps[] = existingMake && "subitems" in existingMake ? existingMake.subitems : []; - makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Make Background", event: this.makeBackground, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); - makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Make Button", event: this.makeBtnClicked, icon: "concierge-bell" }); - makes.push({ description: "Edit OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") }); + makes.push({ description: this.props.Document.isBackground ? "Remove Background" : "Into Background", event: this.makeBackground, icon: this.props.Document.lockedPosition ? "unlock" : "lock" }); + makes.push({ description: this.props.Document.isButton ? "Remove Button" : "Into Button", event: this.makeBtnClicked, icon: "concierge-bell" }); + makes.push({ description: "OnClick script", icon: "edit", event: () => ScriptBox.EditClickScript(this.props.Document, "onClick") }); makes.push({ - description: "Make Portal", event: () => { + description: "Into Portal", event: () => { let portal = Docs.Create.FreeformDocument([], { width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".portal" }); - //Doc.GetProto(this.props.Document).subBulletDocs = new List<Doc>([portal]); - //summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" - //Doc.GetProto(this.props.Document).templates = new List<string>([Templates.Bullet.Layout]); - //let coll = Docs.Create.StackingDocument([this.props.Document, portal], { x: NumCast(this.props.Document.x), y: NumCast(this.props.Document.y), width: this.props.Document[WidthSym]() + 10, height: this.props.Document[HeightSym](), title: this.props.Document.title + ".cont" }); - //this.props.addDocument && this.props.addDocument(coll); - //this.props.removeDocument && this.props.removeDocument(this.props.Document); DocUtils.MakeLink(this.props.Document, portal, undefined, this.props.Document.title + ".portal"); this.makeBtnClicked(); - }, icon: "window-restore" }); + makes.push({ description: this.props.Document.ignoreClick ? "Selectable" : "Unselectable", event: () => this.props.Document.ignoreClick = !this.props.Document.ignoreClick, icon: this.props.Document.ignoreClick ? "unlock" : "lock" }) !existingMake && cm.addItem({ description: "Make...", subitems: makes, icon: "hand-point-right" }); let existing = ContextMenu.Instance.findByDescription("Layout..."); let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; @@ -614,7 +626,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu let analyzers: ContextMenuProps[] = existingAnalyze && "subitems" in existingAnalyze ? existingAnalyze.subitems : []; analyzers.push({ description: "Transcribe Speech", event: this.listen, icon: "microphone" }); !existingAnalyze && cm.addItem({ description: "Analyzers...", subitems: analyzers, icon: "hand-point-right" }); - cm.addItem({ description: "Pin to Presentation", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); + cm.addItem({ description: "Pin to Presentation", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin" }); //I think this should work... and it does! A miracle! cm.addItem({ description: "Add Repl", icon: "laptop-code", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) }); cm.addItem({ description: "Download document", icon: "download", event: () => { @@ -626,42 +638,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } }); - cm.addItem({ - description: "Import document", icon: "upload", event: () => { - const input = document.createElement("input"); - input.type = "file"; - input.accept = ".zip"; - input.onchange = async _e => { - const files = input.files; - if (!files) return; - const file = files[0]; - let formData = new FormData(); - formData.append('file', file); - formData.append('remap', "true"); - const upload = Utils.prepend("/uploadDoc"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json === "error") { - return; - } - const doc = await DocServer.GetRefField(json); - if (!doc || !(doc instanceof Doc)) { - return; - } - const [x, y] = this.props.ScreenToLocalTransform().transformPoint(e.pageX, e.pageY); - doc.x = x, doc.y = y; - this.props.addDocument && this.props.addDocument(doc, false); - }; - input.click(); - } - }); cm.addItem({ description: "Delete", event: this.deleteClicked, icon: "trash" }); type User = { email: string, userDocumentId: string }; let usersMenu: ContextMenuProps[] = []; try { let stuff = await rp.get(Utils.prepend(RouteStore.getUsers)); const users: User[] = JSON.parse(stuff); - usersMenu = users.filter(({ email }) => email !== CurrentUserUtils.email).map(({ email, userDocumentId }) => ({ + usersMenu = users.filter(({ email }) => email !== Doc.CurrentUserEmail).map(({ email, userDocumentId }) => ({ description: email, event: async () => { const userDocument = await Cast(DocServer.GetRefField(userDocumentId), Doc); if (!userDocument) { @@ -684,6 +667,30 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu } runInAction(() => { cm.addItem({ description: "Share...", subitems: usersMenu, icon: "share" }); + if (!ClientUtils.RELEASE) { + let setWriteMode = (mode: DocServer.WriteMode) => { + DocServer.AclsMode = mode; + const mode1 = mode; + const mode2 = mode === DocServer.WriteMode.Default ? mode : DocServer.WriteMode.Playground; + DocServer.setFieldWriteMode("x", mode1); + DocServer.setFieldWriteMode("y", mode1); + DocServer.setFieldWriteMode("width", mode1); + DocServer.setFieldWriteMode("height", mode1); + + DocServer.setFieldWriteMode("panX", mode2); + DocServer.setFieldWriteMode("panY", mode2); + DocServer.setFieldWriteMode("scale", mode2); + DocServer.setFieldWriteMode("viewType", mode2); + }; + let aclsMenu: ContextMenuProps[] = []; + aclsMenu.push({ description: "Default (write/read all)", event: () => setWriteMode(DocServer.WriteMode.Default), icon: DocServer.AclsMode === DocServer.WriteMode.Default ? "check" : "exclamation" }); + aclsMenu.push({ description: "Playground (write own/no read)", event: () => setWriteMode(DocServer.WriteMode.Playground), icon: DocServer.AclsMode === DocServer.WriteMode.Playground ? "check" : "exclamation" }); + aclsMenu.push({ description: "Live Playground (write own/read others)", event: () => setWriteMode(DocServer.WriteMode.LivePlayground), icon: DocServer.AclsMode === DocServer.WriteMode.LivePlayground ? "check" : "exclamation" }); + aclsMenu.push({ description: "Live Readonly (no write/read others)", event: () => setWriteMode(DocServer.WriteMode.LiveReadonly), icon: DocServer.AclsMode === DocServer.WriteMode.LiveReadonly ? "check" : "exclamation" }); + cm.addItem({ description: "Collaboration ACLs...", subitems: aclsMenu, icon: "share" }); + cm.addItem({ description: "Undo Debug Test", event: () => UndoManager.TraceOpenBatches(), icon: "exclamation" }); + } + if (!this.topMost) { // DocumentViews should stop propagation of this event e.stopPropagation(); @@ -718,7 +725,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu chromeHeight = () => { let showOverlays = this.props.showOverlays ? this.props.showOverlays(this.layoutDoc) : undefined; let showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : StrCast(this.layoutDoc.showTitle); - return showTitle ? 25 : 0; + let templates = Cast(this.layoutDoc.templates, listSpec("string")); + if (!showOverlays && templates instanceof List) { + templates.map(str => { + if (!showTitle && str.indexOf("{props.Document.title}") !== -1) showTitle = "title"; + }); + } + return (showTitle ? 25 : 0) + 1;// bcz: why 8?? } get layoutDoc() { @@ -772,7 +785,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu {!showTitle && !showCaption ? this.contents : <div style={{ position: "absolute", display: "inline-block", width: "100%", height: "100%", pointerEvents: "none" }}> - <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 33px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}> + <div style={{ width: "100%", height: showTextTitle ? "calc(100% - 29px)" : "100%", display: "inline-block", position: "absolute", top: showTextTitle ? "29px" : undefined }}> {this.contents} </div> {!showTitle ? (null) : |