From e8fcbbf57b2a2f443d9c280ce10558cf9d51c632 Mon Sep 17 00:00:00 2001 From: vellichora Date: Sun, 9 Feb 2020 17:11:24 -0500 Subject: can upload collection from mobile to desktop --- src/client/views/collections/CollectionMasonryViewFieldRow.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 80752303c..26e0cc35a 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -73,6 +73,7 @@ export class CollectionMasonryViewFieldRow extends React.Component { + console.log("masronry row drop"); this._createAliasSelected = false; if (de.complete.docDragData) { (this.props.parent.Document.dropConverter instanceof ScriptField) && -- cgit v1.2.3-70-g09d2 From b42e1e1e2da941955d7751b6003f18fecd5f2f8d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 16 Feb 2020 12:22:10 -0500 Subject: collection sub view and google authentication manager cleanup, deleteAssets route added --- src/client/apis/GoogleAuthenticationManager.tsx | 46 ++-- .../views/collections/CollectionCarouselView.tsx | 28 +-- .../views/collections/CollectionLinearView.tsx | 2 +- .../collections/CollectionMasonryViewFieldRow.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 7 +- .../views/collections/CollectionStackingView.tsx | 12 +- .../CollectionStackingViewFieldColumn.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 264 +++++++++++---------- .../views/collections/CollectionTreeView.tsx | 6 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +- .../CollectionMulticolumnView.tsx | 6 +- .../CollectionMultirowView.tsx | 6 +- src/server/ApiManagers/DeleteManager.ts | 31 +-- 13 files changed, 213 insertions(+), 209 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx index ce1277667..417dc3c3b 100644 --- a/src/client/apis/GoogleAuthenticationManager.tsx +++ b/src/client/apis/GoogleAuthenticationManager.tsx @@ -12,8 +12,8 @@ const prompt = "Paste authorization code here..."; @observer export default class GoogleAuthenticationManager extends React.Component<{}> { public static Instance: GoogleAuthenticationManager; - @observable private openState = false; private authenticationLink: Opt = undefined; + @observable private openState = false; @observable private authenticationCode: Opt = undefined; @observable private clickedState = false; @observable private success: Opt = undefined; @@ -39,24 +39,18 @@ export default class GoogleAuthenticationManager extends React.Component<{}> { const disposer = reaction( () => this.authenticationCode, async authenticationCode => { - if (!authenticationCode) { - return; + if (authenticationCode) { + disposer(); + const { access_token, avatar, name } = await Networking.PostToServer("/writeGoogleAccessToken", { authenticationCode }); + runInAction(() => { + this.avatar = avatar; + this.username = name; + this.hasBeenClicked = false; + this.success = false; + }); + this.beginFadeout(); + resolve(access_token); } - const { access_token, avatar, name } = await Networking.PostToServer( - "/writeGoogleAccessToken", - { authenticationCode } - ); - runInAction(() => { - this.avatar = avatar; - this.username = name; - }); - this.beginFadeout(); - disposer(); - resolve(access_token); - action(() => { - this.hasBeenClicked = false; - this.success = false; - }); } ); }); @@ -86,26 +80,20 @@ export default class GoogleAuthenticationManager extends React.Component<{}> { GoogleAuthenticationManager.Instance = this; } - private handleClick = () => { - window.open(this.authenticationLink); - setTimeout(() => this.hasBeenClicked = true, 500); - } - - private handlePaste = action((e: React.ChangeEvent) => { - this.authenticationCode = e.currentTarget.value; - }); - private get renderPrompt() { return (
{this.displayLauncher ? : (null)} {this.clickedState ? this.authenticationCode = e.currentTarget.value)} placeholder={prompt} /> : (null)} {this.avatar ? { //used for stacking and masonry view this._dropDisposer?.(); if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); } } @@ -41,21 +41,21 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) } panelHeight = () => this.props.PanelHeight() - 50; - @computed get content() { + @computed get content() { const index = NumCast(this.layoutDoc._itemIndex); return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) : - <> -
- -
-
- -
- + <> +
+ +
+
+ +
+ ; } @computed get buttons() { return <> diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 7eb316cf0..9bbc9f1b6 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -67,7 +67,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view this._dropDisposer && this._dropDisposer(); if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); } } diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index e25a2f5eb..f3d512a97 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -80,7 +80,7 @@ export class CollectionMasonryViewFieldRow extends React.Component d[key] = castedValue); - this.props.parent.drop(e, de); + this.props.parent.onInternalDrop(e, de); e.stopPropagation(); } }); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index fa8be5177..c422c38f1 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -14,7 +14,6 @@ import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { ComputedField } from "../../../new_fields/ScriptField"; import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; import { Docs, DocumentOptions } from "../../documents/Documents"; -import { DocumentType } from "../../documents/DocumentTypes"; import { Gateway } from "../../northstar/manager/Gateway"; import { CompileScript, Transformer, ts } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; @@ -175,7 +174,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { moveDocument={this.props.moveDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} active={this.props.active} - onDrop={this.onDrop} + onDrop={this.onExternalDrop} addDocTab={this.props.addDocTab} pinToPres={this.props.pinToPres} isSelected={this.props.isSelected} @@ -199,7 +198,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { render() { return
-
this.props.active(true) && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}> +
this.props.active(true) && e.stopPropagation()} onDrop={e => this.onExternalDrop(e, {})} ref={this.createTarget}> {this.schemaTable}
{this.dividerDragger} @@ -692,7 +691,7 @@ export class SchemaTable extends React.Component { onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 // ContextMenu.Instance.addItem({ description: "Make DB", event: this.makeDB, icon: "table" }); - ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" }) + ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" }); } } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 055035b3e..a9cefae6a 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -189,7 +189,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } getDocHeight(d?: Doc) { if (!d) return 0; - let layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); + const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); const nw = NumCast(layoutDoc._nativeWidth); const nh = NumCast(layoutDoc._nativeHeight); let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); @@ -234,7 +234,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @undoBatch @action - drop = (e: Event, de: DragManager.DropEvent) => { + onInternalDrop = (e: Event, de: DragManager.DropEvent) => { const where = [de.x, de.y]; let targInd = -1; let plusOne = 0; @@ -248,7 +248,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { plusOne = where[axis] > (pos[axis] + pos1[axis]) / 2 ? 1 : 0; } }); - if (super.drop(e, de)) { + if (super.onInternalDrop(e, de)) { const newDoc = de.complete.docDragData.droppedDocuments[0]; const docs = this.childDocList; if (docs) { @@ -264,7 +264,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } @undoBatch @action - onDrop = async (e: React.DragEvent): Promise => { + onExternalDrop = async (e: React.DragEvent): Promise => { const where = [e.clientX, e.clientY]; let targInd = -1; this._docXfs.map((cd, i) => { @@ -274,7 +274,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { targInd = i; } }); - super.onDrop(e, {}, () => { + super.onExternalDrop(e, {}, () => { if (targInd !== -1) { const newDoc = this.childDocs[this.childDocs.length - 1]; const docs = this.childDocList; @@ -405,7 +405,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { transformOrigin: "top left", }} onScroll={action((e: React.UIEvent) => this._scroll = e.currentTarget.scrollTop)} - onDrop={this.onDrop.bind(this)} + onDrop={this.onExternalDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={e => this.props.active() && e.stopPropagation()} > {this.renderedSections} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 2ff477c57..87c35679f 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -71,7 +71,7 @@ export class CollectionStackingViewFieldColumn extends React.Component d[key] = undefined); } - this.props.parent.drop(e, de); + this.props.parent.onInternalDrop(e, de); e.stopPropagation(); } }); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index e80e1c802..042385dcd 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -57,7 +57,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this.gestureDisposer?.(); this.multiTouchDisposer?.(); if (ele) { - this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this)); this.multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this)); } @@ -156,7 +156,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @undoBatch @action - protected drop(e: Event, de: DragManager.DropEvent): boolean { + protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { const docDragData = de.complete.docDragData; (this.props.Document.dropConverter instanceof ScriptField) && this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this @@ -195,158 +195,172 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @undoBatch @action - protected async onDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) { + protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: () => void) { if (e.ctrlKey) { e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl return; } - const html = e.dataTransfer.getData("text/html"); - const text = e.dataTransfer.getData("text/plain"); + + const { dataTransfer } = e; + const html = dataTransfer.getData("text/html"); + const text = dataTransfer.getData("text/plain"); if (text && text.startsWith(" { - if (f instanceof Doc) { - if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView - (f instanceof Doc) && this.props.addDocument(f); - } - }); - } else { - this.props.addDocument && this.props.addDocument(Docs.Create.WebDocument(href, { ...options, title: href })); - } - } else if (text) { - this.props.addDocument && this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 })); - } + const { addDocument } = this.props; + if (!addDocument) { + alert("this.props.addDocument does not exist. Aborting drop operation."); return; } - if (html && !html.startsWith(" 1 && tags[1].startsWith("img") ? tags[1] : ""; - if (img) { - const split = img.split("src=\"")[1].split("\"")[0]; - let source = split; - if (split.startsWith("data:image") && split.includes("base64")) { - const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] }); - source = Utils.prepend(accessPaths.agnostic.client); + + if (html) { + if (FormattedTextBox.IsFragment(html)) { + const href = FormattedTextBox.GetHref(html); + if (href) { + const docid = FormattedTextBox.GetDocFromUrl(href); + if (docid) { // prosemirror text containing link to dash document + DocServer.GetRefField(docid).then(f => { + if (f instanceof Doc) { + if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + (f instanceof Doc) && addDocument(f); + } + }); + } else { + addDocument(Docs.Create.WebDocument(href, { ...options, title: href })); + } + } else if (text) { + addDocument(Docs.Create.TextDocument(text, { ...options, _width: 100, _height: 25 })); } - const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 }); - ImageUtils.ExtractExif(doc); - this.props.addDocument(doc); return; - } else { - const path = window.location.origin + "/doc/"; - if (text.startsWith(path)) { - const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0]; - DocServer.GetRefField(docid).then(f => { - if (f instanceof Doc) { - if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView - (f instanceof Doc) && this.props.addDocument(f); - } - }); + } + if (!html.startsWith(" 1 && tags[1].startsWith("img") ? tags[1] : ""; + if (img) { + const split = img.split("src=\"")[1].split("\"")[0]; + let source = split; + if (split.startsWith("data:image") && split.includes("base64")) { + const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] }); + source = Utils.prepend(accessPaths.agnostic.client); + } + const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 }); + ImageUtils.ExtractExif(doc); + addDocument(doc); + return; } else { - const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300 }); - Doc.GetProto(htmlDoc)["data-text"] = text; - this.props.addDocument(htmlDoc); + const path = window.location.origin + "/doc/"; + if (text.startsWith(path)) { + const docid = text.replace(Utils.prepend("/doc/"), "").split("?")[0]; + DocServer.GetRefField(docid).then(f => { + if (f instanceof Doc) { + if (options.x || options.y) { f.x = options.x; f.y = options.y; } // should be in CollectionFreeFormView + (f instanceof Doc) && this.props.addDocument(f); + } + }); + } else { + const htmlDoc = Docs.Create.HtmlDocument(html, { ...options, title: "-web page-", _width: 300, _height: 300 }); + Doc.GetProto(htmlDoc)["data-text"] = text; + this.props.addDocument(htmlDoc); + } + return; } - return; } } - if (text && text.indexOf("www.youtube.com/watch") !== -1) { - const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/"); - this.props.addDocument(Docs.Create.VideoDocument(url, { ...options, title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5 })); - return; - } - let matches: RegExpExecArray | null; - if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) { - const newBox = Docs.Create.TextDocument("", { ...options, _width: 400, _height: 200, title: "Awaiting title from Google Docs..." }); - const proto = newBox.proto!; - const documentId = matches[2]; - proto[GoogleRef] = documentId; - proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs..."; - proto.backgroundColor = "#eeeeff"; - this.props.addDocument(newBox); - // const parent = Docs.Create.StackingDocument([newBox], { title: `Google Doc Import (${documentId})` }); - // CollectionDockingView.Instance.AddRightSplit(parent, undefined); - // proto.height = parent[HeightSym](); - return; - } - if ((matches = /(https:\/\/)?photos\.google\.com\/(u\/3\/)?album\/([^\\]+)/g.exec(text)) !== null) { - const albums = await GooglePhotos.Transactions.ListAlbums(); - const albumId = matches[3]; - const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId); - console.log(mediaItems); - return; + + if (text) { + if (text.includes("www.youtube.com/watch")) { + const url = text.replace("youtube.com/watch?v=", "youtube.com/embed/"); + addDocument(Docs.Create.VideoDocument(url, { + ...options, + title: url, + _width: 400, + _height: 315, + _nativeWidth: 600, + _nativeHeight: 472.5 + })); + return; + } + let matches: RegExpExecArray | null; + if ((matches = /(https:\/\/)?docs\.google\.com\/document\/d\/([^\\]+)\/edit/g.exec(text)) !== null) { + const newBox = Docs.Create.TextDocument("", { ...options, _width: 400, _height: 200, title: "Awaiting title from Google Docs..." }); + const proto = newBox.proto!; + const documentId = matches[2]; + proto[GoogleRef] = documentId; + proto.data = "Please select this document and then click on its pull button to load its contents from from Google Docs..."; + proto.backgroundColor = "#eeeeff"; + addDocument(newBox); + return; + } + if ((matches = /(https:\/\/)?photos\.google\.com\/(u\/3\/)?album\/([^\\]+)/g.exec(text)) !== null) { + const albumId = matches[3]; + const mediaItems = await GooglePhotos.Query.AlbumSearch(albumId); + console.log(mediaItems); + return; + } } + const { items } = e.dataTransfer; const { length } = items; const files: File[] = []; - if (length) { - const batch = UndoManager.StartBatch("collection view drop"); - const promises: Promise[] = []; - // tslint:disable-next-line:prefer-for-of - for (let i = 0; i < length; i++) { - const item = e.dataTransfer.items[i]; - if (item.kind === "string" && item.type.indexOf("uri") !== -1) { - let str: string; - const prom = new Promise(resolve => item.getAsString(resolve)) - .then(action((s: string) => rp.head(Utils.CorsProxy(str = s)))) - .then(result => { - const type = result["content-type"]; - if (type) { - Docs.Get.DocumentFromType(type, str, options) - .then(doc => doc && this.props.addDocument(doc)); - } - }); - promises.push(prom); - } - if (item.kind === "file") { - const file = item.getAsFile(); - file && file.type && files.push(file); + const generatedDocuments: Doc[] = []; + if (!length) { + alert("No uploadable content found."); + return; + } + + const batch = UndoManager.StartBatch("collection view drop"); + for (let i = 0; i < length; i++) { + const item = e.dataTransfer.items[i]; + if (item.kind === "string" && item.type.includes("uri")) { + const stringContents = await new Promise(resolve => item.getAsString(resolve)); + const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"]; + if (type) { + const doc = await Docs.Get.DocumentFromType(type, stringContents, options); + doc && generatedDocuments.push(doc); } } - promises.push(Networking.UploadFilesToServer(files).then(responses => responses.forEach(({ source: { name, type }, result }) => { - if (result instanceof Error) { - alert(`Upload failed: ${result.message}`); - return; - } - const full = { ...options, _width: 300, title: name }; - const pathname = Utils.prepend(result.accessPaths.agnostic.client); - Docs.Get.DocumentFromType(type, pathname, full).then(doc => { - if (doc) { - const proto = Doc.GetProto(doc); - proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""); - if (Upload.isImageInformation(result)) { - proto["data-nativeWidth"] = result.nativeWidth; - proto["data-nativeHeight"] = result.nativeHeight; - proto.contentSize = result.contentSize; - } - this.props?.addDocument(doc); - } - }); - }))); - if (promises.length) { - Promise.all(promises).finally(() => { completed && completed(); batch.end(); }); - } else { - if (text && !text.includes("https://")) { - this.props.addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 })); - } - batch.end(); + if (item.kind === "file") { + const file = item.getAsFile(); + file && file.type && files.push(file); + } + } + for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) { + if (result instanceof Error) { + alert(`Upload failed: ${result.message}`); + return; } + const full = { ...options, _width: 300, title: name }; + const pathname = Utils.prepend(result.accessPaths.agnostic.client); + const doc = await Docs.Get.DocumentFromType(type, pathname, full); + if (!doc) { + continue; + } + const proto = Doc.GetProto(doc); + proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, ""); + if (Upload.isImageInformation(result)) { + proto["data-nativeWidth"] = result.nativeWidth; + proto["data-nativeHeight"] = result.nativeHeight; + proto.contentSize = result.contentSize; + } + generatedDocuments.push(doc); + } + if (generatedDocuments.length) { + generatedDocuments.forEach(addDocument); + completed && completed(); } else { - alert("No uploadable content found."); + if (text && !text.includes("https://")) { + addDocument(Docs.Create.TextDocument(text, { ...options, _width: 400, _height: 315 })); + } } + batch.end(); } } + return CollectionSubView; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 8720ce002..13ab7c1a4 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -210,7 +210,7 @@ class TreeView extends React.Component { } else { ContextMenu.Instance.addItem({ description: "Delete Workspace", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); ContextMenu.Instance.addItem({ description: "Create New Workspace", event: () => MainView.Instance.createNewWorkspace(), icon: "plus" }); - } + } ContextMenu.Instance.addItem({ description: "Toggle Theme Colors", event: () => this.props.document.darkScheme = !this.props.document.darkScheme, icon: "minus" }); ContextMenu.Instance.addItem({ description: "Open Fields", event: () => { const kvp = Docs.Create.KVPDocument(this.props.document, { _width: 300, _height: 300 }); this.props.addDocTab(kvp, this.props.dataDoc ? this.props.dataDoc : kvp, "onRight"); }, icon: "layer-group" }); ContextMenu.Instance.addItem({ description: "Publish", event: () => DocUtils.Publish(this.props.document, StrCast(this.props.document.title), () => { }, () => { }), icon: "file" }); @@ -594,7 +594,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { protected createTreeDropTarget = (ele: HTMLDivElement) => { this.treedropDisposer && this.treedropDisposer(); if (this._mainEle = ele) { - this.treedropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + this.treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this)); } } @@ -702,7 +702,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", subitems: onClicks, icon: "hand-point-right" }); } outerXf = () => Utils.GetScreenTransform(this._mainEle!); - onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {}); @computed get renderClearButton() { return
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 969d6b3c8..bdc5e03e3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -117,20 +117,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } @action - onDrop = (e: React.DragEvent): Promise => { + onExternalDrop = (e: React.DragEvent): Promise => { const pt = this.getTransform().transformPoint(e.pageX, e.pageY); - return super.onDrop(e, { x: pt[0], y: pt[1] }); + return super.onExternalDrop(e, { x: pt[0], y: pt[1] }); } @undoBatch @action - drop = (e: Event, de: DragManager.DropEvent) => { + onInternalDrop = (e: Event, de: DragManager.DropEvent) => { if (this.props.Document.isBackground) return false; const xf = this.getTransform(); const xfo = this.getTransformOverlay(); const [xp, yp] = xf.transformPoint(de.x, de.y); const [xpo, ypo] = xfo.transformPoint(de.x, de.y); - if (super.drop(e, de)) { + if (super.onInternalDrop(e, de)) { if (de.complete.docDragData) { if (de.complete.docDragData.droppedDocuments.length) { const firstDoc = de.complete.docDragData.droppedDocuments[0]; @@ -1079,7 +1079,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return
{ - if (super.drop(e, de)) { + onInternalDrop = (e: Event, de: DragManager.DropEvent) => { + if (super.onInternalDrop(e, de)) { de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { d.dimUnit = "*"; d.dimMagnitude = 1; @@ -214,7 +214,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu getTransform={dxf} onClick={this.onChildClickHandler} renderDepth={this.props.renderDepth + 1} - /> + />; } /** * @returns the resolved list of rendered child documents, displayed diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 630a178cf..5e59f8237 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -190,8 +190,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) @undoBatch @action - drop = (e: Event, de: DragManager.DropEvent) => { - if (super.drop(e, de)) { + onInternalDrop = (e: Event, de: DragManager.DropEvent) => { + if (super.onInternalDrop(e, de)) { de.complete.docDragData?.droppedDocuments.forEach(action((d: Doc) => { d.dimUnit = "*"; d.dimMagnitude = 1; @@ -215,7 +215,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) getTransform={dxf} onClick={this.onChildClickHandler} renderDepth={this.props.renderDepth + 1} - /> + />; } /** * @returns the resolved list of rendered child documents, displayed diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts index be452c0ff..9e70af2eb 100644 --- a/src/server/ApiManagers/DeleteManager.ts +++ b/src/server/ApiManagers/DeleteManager.ts @@ -2,6 +2,11 @@ import ApiManager, { Registration } from "./ApiManager"; import { Method, _permission_denied, PublicHandler } from "../RouteManager"; import { WebSocket } from "../Websocket/Websocket"; import { Database } from "../database"; +import rimraf = require("rimraf"); +import { pathToDirectory, Directory } from "./UploadManager"; +import { filesDirectory } from ".."; +import { DashUploadUtils } from "../DashUploadUtils"; +import { mkdirSync } from "fs"; export default class DeleteManager extends ApiManager { @@ -31,21 +36,19 @@ export default class DeleteManager extends ApiManager { } }); - const hi: PublicHandler = async ({ res, isRelease }) => { - if (isRelease) { - return _permission_denied(res, deletionPermissionError); + register({ + method: Method.GET, + subscription: "/deleteAssets", + secureHandler: async ({ res, isRelease }) => { + if (isRelease) { + return _permission_denied(res, deletionPermissionError); + } + rimraf.sync(filesDirectory); + mkdirSync(filesDirectory); + await DashUploadUtils.buildFileDirectories(); + res.redirect("/delete"); } - await Database.Instance.deleteAll('users'); - res.redirect("/home"); - }; - - // register({ - // method: Method.GET, - // subscription: "/deleteUsers", - // onValidation: hi, - // onUnauthenticated: hi - // }); - + }); register({ method: Method.GET, -- cgit v1.2.3-70-g09d2 From a451bce9b49b28434f5180044b11467c21615f06 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 16 Feb 2020 15:10:36 -0500 Subject: fixed a bunch of undo's in document deocrations. added a setup method for handling down/move/up/click events --- src/Utils.ts | 36 +++ src/client/views/DocumentDecorations.tsx | 260 ++++++--------------- .../collections/CollectionMasonryViewFieldRow.tsx | 6 +- .../CollectionStackingViewFieldColumn.tsx | 4 +- .../document_templates/image_card/ImageCard.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 12 +- 6 files changed, 122 insertions(+), 198 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index b564564be..6a0b3fad8 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -473,4 +473,40 @@ export function clearStyleSheetRules(sheet: any) { return true; } return false; +} + +export function setupMoveUpEvents( + target: object, + e: React.PointerEvent, + moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean, + upEvent: (e: PointerEvent) => void, + clickEvent: (e: PointerEvent) => void) { + (target as any)._downX = (target as any)._lastX = e.clientX; + (target as any)._downY = (target as any)._lastY = e.clientY; + + const _moveEvent = (e: PointerEvent): void => { + if (Math.abs(e.clientX - (target as any)._downX) > 4 || Math.abs(e.clientY - (target as any)._downY) > 4) { + if (moveEvent(e, [(target as any)._downX, (target as any)._downY], + [e.clientX - (target as any)._lastX, e.clientY - (target as any)._lastY])) { + document.removeEventListener("pointermove", _moveEvent); + document.removeEventListener("pointerup", _upEvent); + } + } + (target as any)._lastX = e.clientX; + (target as any)._lastY = e.clientY; + e.stopPropagation(); + } + const _upEvent = (e: PointerEvent): void => { + upEvent(e); + if (Math.abs(e.clientX - (target as any)._downX) < 4 || Math.abs(e.clientY - (target as any)._downY) < 4) { + clickEvent(e); + } + document.removeEventListener("pointermove", _moveEvent); + document.removeEventListener("pointerup", _upEvent); + } + e.stopPropagation(); + document.removeEventListener("pointermove", _moveEvent); + document.removeEventListener("pointerup", _upEvent); + document.addEventListener("pointermove", _moveEvent); + document.addEventListener("pointerup", _upEvent); } \ No newline at end of file diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3388ea46c..f516bb8fc 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -8,7 +8,7 @@ import { PositionDocument } from '../../new_fields/documentSchemas'; import { ScriptField } from '../../new_fields/ScriptField'; import { Cast, StrCast } from "../../new_fields/Types"; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; -import { Utils } from "../../Utils"; +import { Utils, setupMoveUpEvents } from "../../Utils"; import { DocUtils } from "../documents/Documents"; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from "../util/DragManager"; @@ -19,9 +19,6 @@ import './DocumentDecorations.scss'; import { DocumentView } from "./nodes/DocumentView"; import React = require("react"); import { Id } from '../../new_fields/FieldSymbols'; -const higflyout = require("@hig/flyout"); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; library.add(faCaretUp); library.add(faObjectGroup); @@ -44,16 +41,12 @@ export type CloseCall = (toBeDeleted: DocumentView[]) => void; @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { static Instance: DocumentDecorations; - private _isPointerDown = false; - private _resizing = ""; + private _resizeHdlId = ""; private _keyinput: React.RefObject; private _resizeBorderWidth = 16; private _linkBoxHeight = 20 + 3; // link button height + margin private _titleHeight = 20; - private _downX = 0; - private _downY = 0; private _resizeUndo?: UndoManager.Batch; - private _radiusDown = [0, 0]; @observable private _accumulatedTitle = ""; @observable private _titleControlString: string = "#title"; @observable private _edtingTitle = false; @@ -75,7 +68,29 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> reaction(() => SelectionManager.SelectedDocuments().slice(), docs => this.titleBlur(false)); } - @action titleChanged = (event: any) => this._accumulatedTitle = event.target.value; + @computed + get Bounds(): { x: number, y: number, b: number, r: number } { + return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => { + if (documentView.props.renderDepth === 0 || + Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) { + return bounds; + } + const transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse(); + var [sptX, sptY] = transform.transformPoint(0, 0); + let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight()); + if (documentView.props.Document.type === DocumentType.LINK) { + const rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect(); + sptX = rect.left; + sptY = rect.top; + bptX = rect.right; + bptY = rect.bottom; + } + return { + x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), + r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) + }; + }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + } addCloseCall = (handler: CloseCall) => { const currentOffset = this.addedCloseCalls.length - 1; @@ -85,7 +100,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> }); } - titleBlur = undoBatch(action((commit: boolean) => { + titleBlur = action((commit: boolean) => { this._edtingTitle = false; if (commit) { if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) { @@ -93,12 +108,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else if (this._titleControlString.startsWith("#")) { const selectionTitleFieldKey = this._titleControlString.substring(1); selectionTitleFieldKey === "title" && (SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._accumulatedTitle.startsWith("-")); - selectionTitleFieldKey && SelectionManager.SelectedDocuments().forEach(d => - Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle, true) - ); + let didAnything = false; + UndoManager.RunInBatch(() => selectionTitleFieldKey && SelectionManager.SelectedDocuments().forEach(d => { + const value = typeof d.props.Document[selectionTitleFieldKey] === "number" ? +this._accumulatedTitle : this._accumulatedTitle; + didAnything = didAnything || d.props.Document[selectionTitleFieldKey] !== value; + Doc.SetInPlace(d.props.Document, selectionTitleFieldKey, value, true) + }), "title blur"); + if (!didAnything) UndoManager.Undo(); } } - })); + }); @action titleEntered = (e: any) => { const key = e.keyCode || e.which; @@ -115,66 +134,20 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } } @action onTitleDown = (e: React.PointerEvent): void => { - this._downX = e.clientX; - this._downY = e.clientY; - e.stopPropagation(); - document.removeEventListener("pointermove", this.onTitleMove); - document.removeEventListener("pointerup", this.onTitleUp); - document.addEventListener("pointermove", this.onTitleMove); - document.addEventListener("pointerup", this.onTitleUp); - } - @action onTitleMove = (e: PointerEvent): void => { - if (Math.abs(e.clientX - this._downX) > 4 || Math.abs(e.clientY - this._downY) > 4) { - this.Interacting = true; - } - if (this.Interacting) this.onBackgroundMove(e); - e.stopPropagation(); - } - @action onTitleUp = (e: PointerEvent): void => { - if (Math.abs(e.clientX - this._downX) < 4 || Math.abs(e.clientY - this._downY) < 4) { - !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString); - this._edtingTitle = true; - setTimeout(() => this._keyinput.current!.focus(), 0); - } - document.removeEventListener("pointermove", this.onTitleMove); - document.removeEventListener("pointerup", this.onTitleUp); - this.onBackgroundUp(e); + setupMoveUpEvents(this, e, this.onBackgroundMove, (e) => { }, this.onTitleClick); } - - @computed - get Bounds(): { x: number, y: number, b: number, r: number } { - return SelectionManager.SelectedDocuments().reduce((bounds, documentView) => { - if (documentView.props.renderDepth === 0 || - Doc.AreProtosEqual(documentView.props.Document, CurrentUserUtils.UserDocument)) { - return bounds; - } - const transform = (documentView.props.ScreenToLocalTransform().scale(documentView.props.ContentScaling())).inverse(); - var [sptX, sptY] = transform.transformPoint(0, 0); - let [bptX, bptY] = transform.transformPoint(documentView.props.PanelWidth(), documentView.props.PanelHeight()); - if (documentView.props.Document.type === DocumentType.LINK) { - const rect = documentView.ContentDiv!.getElementsByClassName("docuLinkBox-cont")[0].getBoundingClientRect(); - sptX = rect.left; - sptY = rect.top; - bptX = rect.right; - bptY = rect.bottom; - } - return { - x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y), - r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b) - }; - }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE }); + @action onTitleClick = (e: PointerEvent): void => { + !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString); + this._edtingTitle = true; + setTimeout(() => this._keyinput.current!.focus(), 0); } onBackgroundDown = (e: React.PointerEvent): void => { - document.removeEventListener("pointermove", this.onBackgroundMove); - document.removeEventListener("pointerup", this.onBackgroundUp); - document.addEventListener("pointermove", this.onBackgroundMove); - document.addEventListener("pointerup", this.onBackgroundUp); - e.stopPropagation(); + setupMoveUpEvents(this, e, this.onBackgroundMove, (e) => { }, (e) => { }); } @action - onBackgroundMove = (e: PointerEvent): void => { + onBackgroundMove = (e: PointerEvent, down: number[]): boolean => { const dragDocView = SelectionManager.SelectedDocuments()[0]; const dragData = new DragManager.DocumentDragData(SelectionManager.SelectedDocuments().map(dv => dv.props.Document)); const [left, top] = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).inverse().transformPoint(0, 0); @@ -183,43 +156,19 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dragData.isSelectionMove = true; this.Interacting = true; this._hidden = true; - document.removeEventListener("pointermove", this.onBackgroundMove); - document.removeEventListener("pointerup", this.onBackgroundUp); - document.removeEventListener("pointermove", this.onTitleMove); - document.removeEventListener("pointerup", this.onTitleUp); DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(documentView => documentView.ContentDiv!), dragData, e.x, e.y, { dragComplete: action(e => this._hidden = this.Interacting = false), hideSource: true }); - e.stopPropagation(); - } - - @action - onBackgroundUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onBackgroundMove); - document.removeEventListener("pointerup", this.onBackgroundUp); - e.stopPropagation(); - e.preventDefault(); + return true; } onCloseDown = (e: React.PointerEvent): void => { - e.stopPropagation(); - if (e.button === 0) { - document.removeEventListener("pointermove", this.onCloseMove); - document.addEventListener("pointermove", this.onCloseMove); - document.removeEventListener("pointerup", this.onCloseUp); - document.addEventListener("pointerup", this.onCloseUp); - } - } - onCloseMove = (e: PointerEvent): void => { - e.stopPropagation(); - if (e.button === 0) { - } + setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onCloseClick); } @undoBatch @action - onCloseUp = async (e: PointerEvent) => { - e.stopPropagation(); + onCloseClick = async (e: PointerEvent) => { if (e.button === 0) { const recent = Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc) as Doc; const selected = SelectionManager.SelectedDocuments().slice(); @@ -230,37 +179,16 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true); dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); }); - document.removeEventListener("pointermove", this.onCloseMove); - document.removeEventListener("pointerup", this.onCloseUp); } } @action onMinimizeDown = (e: React.PointerEvent): void => { - e.stopPropagation(); - if (e.button === 0) { - document.removeEventListener("pointermove", this.onMinimizeMove); - document.addEventListener("pointermove", this.onMinimizeMove); - document.removeEventListener("pointerup", this.onMinimizeUp); - document.addEventListener("pointerup", this.onMinimizeUp); - } - } - - @action - onMinimizeMove = (e: PointerEvent): void => { - e.stopPropagation(); - if (Math.abs(e.pageX - this._downX) > Utils.DRAG_THRESHOLD || - Math.abs(e.pageY - this._downY) > Utils.DRAG_THRESHOLD) { - document.removeEventListener("pointermove", this.onMinimizeMove); - document.removeEventListener("pointerup", this.onMinimizeUp); - } + setupMoveUpEvents(this, e, (e, d) => false, (e) => { }, this.onMinimizeClick); } @undoBatch @action - onMinimizeUp = (e: PointerEvent): void => { - e.stopPropagation(); + onMinimizeClick = (e: PointerEvent): void => { if (e.button === 0) { - document.removeEventListener("pointermove", this.onMinimizeMove); - document.removeEventListener("pointerup", this.onMinimizeUp); const selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd); selectedDocs.map(dv => { const layoutKey = Cast(dv.props.Document.layoutKey, "string", null); @@ -280,106 +208,68 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> @action onRadiusDown = (e: React.PointerEvent): void => { - e.stopPropagation(); + setupMoveUpEvents(this, e, this.onRadiusMove, (e) => this._resizeUndo?.end(), (e) => { }); if (e.button === 0) { - this._radiusDown = [e.clientX, e.clientY]; - this._isPointerDown = true; this._resizeUndo = UndoManager.StartBatch("DocDecs set radius"); - document.removeEventListener("pointermove", this.onRadiusMove); - document.removeEventListener("pointerup", this.onRadiusUp); - document.addEventListener("pointermove", this.onRadiusMove); - document.addEventListener("pointerup", this.onRadiusUp); } } - onRadiusMove = (e: PointerEvent): void => { - let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1])); + onRadiusMove = (e: PointerEvent, down: number[]): boolean => { + let dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1])); dist = dist < 3 ? 0 : dist; - SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplateForField ? dv.props.Document : Doc.GetProto(dv.props.Document)). + SelectionManager.SelectedDocuments().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)). map(d => d.borderRounding = `${Math.min(100, dist)}%`); - e.stopPropagation(); - e.preventDefault(); + return false; } - onRadiusUp = (e: PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - this._isPointerDown = false; - this._resizeUndo && this._resizeUndo.end(); - document.removeEventListener("pointermove", this.onRadiusMove); - document.removeEventListener("pointerup", this.onRadiusUp); - } - - _lastX = 0; - _lastY = 0; @action onPointerDown = (e: React.PointerEvent): void => { - e.stopPropagation(); + setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, (e) => { }); if (e.button === 0) { - this._lastX = e.clientX; - this._lastY = e.clientY; - this._isPointerDown = true; - this._resizing = e.currentTarget.id; + this._resizeHdlId = e.currentTarget.id; this.Interacting = true; this._resizeUndo = UndoManager.StartBatch("DocDecs resize"); - document.removeEventListener("pointermove", this.onPointerMove); - document.addEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - document.addEventListener("pointerup", this.onPointerUp); } } - - onPointerMove = (e: PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - if (!this._isPointerDown) { - return; - } - + onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => { let dX = 0, dY = 0, dW = 0, dH = 0; - const moveX = e.clientX - this._lastX; // e.movementX; - const moveY = e.clientY - this._lastY; // e.movementY; - this._lastX = e.clientX; - this._lastY = e.clientY; - - switch (this._resizing) { - case "": - break; + switch (this._resizeHdlId) { + case "": break; case "documentDecorations-topLeftResizer": dX = -1; dY = -1; - dW = -moveX; - dH = -moveY; + dW = -move[0]; + dH = -move[1]; break; case "documentDecorations-topRightResizer": - dW = moveX; + dW = move[0]; dY = -1; - dH = -moveY; + dH = -move[1]; break; case "documentDecorations-topResizer": dY = -1; - dH = -moveY; + dH = -move[1]; break; case "documentDecorations-bottomLeftResizer": dX = -1; - dW = -moveX; - dH = moveY; + dW = -move[0]; + dH = move[1]; break; case "documentDecorations-bottomRightResizer": - dW = moveX; - dH = moveY; + dW = move[0]; + dH = move[1]; break; case "documentDecorations-bottomResizer": - dH = moveY; + dH = move[1]; break; case "documentDecorations-leftResizer": dX = -1; - dW = -moveX; + dW = -move[0]; break; case "documentDecorations-rightResizer": - dW = moveX; + dW = move[0]; break; } @@ -430,20 +320,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } } })); + return false; } @action onPointerUp = (e: PointerEvent): void => { - e.stopPropagation(); - this._resizing = ""; + this._resizeHdlId = ""; this.Interacting = false; - if (e.button === 0) { - e.preventDefault(); - this._isPointerDown = false; - this._resizeUndo && this._resizeUndo.end(); - document.removeEventListener("pointermove", this.onPointerMove); - document.removeEventListener("pointerup", this.onPointerUp); - } + (e.button === 0) && this._resizeUndo?.end(); } @computed @@ -494,7 +378,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const titleArea = this._edtingTitle ? <> this.titleBlur(true)} onChange={this.titleChanged} onKeyPress={this.titleEntered} /> + onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} />
DocUtils.Publish(seldoc.props.Document, this._accumulatedTitle, seldoc.props.addDocument, seldoc.props.removeDocument)}> diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index f3d512a97..f3dd7c733 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faPalette } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable, computed } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import Measure from "react-measure"; import { Doc } from "../../../new_fields/Doc"; @@ -16,10 +16,12 @@ import { CompileScript } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; -import { anchorPoints, Flyout } from "../DocumentDecorations"; import { EditableView } from "../EditableView"; import { CollectionStackingView } from "./CollectionStackingView"; import "./CollectionStackingView.scss"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; library.add(faPalette); diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 87c35679f..2e3467d90 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -18,10 +18,12 @@ import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { anchorPoints, Flyout } from "../DocumentDecorations"; import { EditableView } from "../EditableView"; import { CollectionStackingView } from "./CollectionStackingView"; import "./CollectionStackingView.scss"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; library.add(faPalette); diff --git a/src/client/views/document_templates/image_card/ImageCard.tsx b/src/client/views/document_templates/image_card/ImageCard.tsx index 868afc423..3ac90ffe7 100644 --- a/src/client/views/document_templates/image_card/ImageCard.tsx +++ b/src/client/views/document_templates/image_card/ImageCard.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +dimport * as React from 'react'; import { FieldViewProps } from '../../nodes/FieldView'; import { ImageBox } from '../../nodes/ImageBox'; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 3447f0380..4efaef408 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -249,7 +249,7 @@ export class DocumentView extends DocComponent(Docu } } - onClick = undoBatch((e: React.MouseEvent | React.PointerEvent) => { + onClick = (e: React.MouseEvent | React.PointerEvent) => { if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && CurrentUserUtils.MainDocId !== this.props.Document[Id] && (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) { e.stopPropagation(); @@ -259,24 +259,24 @@ export class DocumentView extends DocComponent(Docu if (StrCast(fullScreenAlias.layoutKey) !== "layout_fullScreen" && fullScreenAlias.layout_fullScreen) { fullScreenAlias.layoutKey = "layout_fullScreen"; } - this.props.addDocTab(fullScreenAlias, undefined, "inTab"); + UndoManager.RunInBatch(() => this.props.addDocTab(fullScreenAlias, undefined, "inTab"), "double tap"); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } else if (this.onClickHandler && this.onClickHandler.script) { SelectionManager.DeselectAll(); - this.onClickHandler.script.run({ this: this.Document.isTemplateForField && this.props.DataDoc ? this.props.DataDoc : this.props.Document, containingCollection: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey }, console.log); + UndoManager.RunInBatch(() => this.onClickHandler!.script.run({ this: this.Document.isTemplateForField && this.props.DataDoc ? this.props.DataDoc : this.props.Document, containingCollection: this.props.ContainingCollectionDoc, shiftKey: e.shiftKey }, console.log), "on click"); } else if (this.Document.type === DocumentType.BUTTON) { - ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY); + UndoManager.RunInBatch(() => ScriptBox.EditButtonScript("On Button Clicked ...", this.props.Document, "onClick", e.clientX, e.clientY), "on button click"); } else if (this.Document.isButton) { SelectionManager.SelectDoc(this, e.ctrlKey); // don't think this should happen if a button action is actually triggered. - this.buttonClick(e.altKey, e.ctrlKey); + UndoManager.RunInBatch(() => this.buttonClick(e.altKey, e.ctrlKey), "on link button follow"); } else { SelectionManager.SelectDoc(this, e.ctrlKey); preventDefault = false; } preventDefault && e.preventDefault(); } - }); + }; buttonClick = async (altKey: boolean, ctrlKey: boolean) => { const linkDocs = DocListCast(this.props.Document.links); -- cgit v1.2.3-70-g09d2 From 2ae91501a3195da9dcbc02e180b108983cd1d8af Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 16 Feb 2020 21:47:28 -0500 Subject: got rid of unneeded ignoreAspect field. other cleanup. --- src/client/util/RichTextSchema.tsx | 2 +- src/client/views/DocumentDecorations.tsx | 9 ++------- src/client/views/collections/CollectionDockingView.tsx | 8 ++++---- .../views/collections/CollectionMasonryViewFieldRow.tsx | 3 ++- src/client/views/collections/CollectionStackingView.tsx | 6 +++--- src/client/views/collections/CollectionView.tsx | 17 ----------------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 15 +++++---------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 ++-- src/new_fields/Doc.ts | 3 +-- src/new_fields/documentSchemas.ts | 1 - 12 files changed, 22 insertions(+), 50 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 3a3bcd1ad..656215368 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -723,7 +723,7 @@ export class DashDocView { const { scale, translateX, translateY } = Utils.GetScreenTransform(this._outer); return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale); } - contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 && !this._dashDoc!.ignoreAspect ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1; + contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1; outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { this._textBox = tbox; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 5fc14e716..cc7388a61 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -283,17 +283,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> const actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); - const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); - if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { - layoutDoc.ignoreAspect = false; - layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; - layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; - } + const fixedAspect = e.ctrlKey || (nwidth && nheight); if (fixedAspect && (!nwidth || !nheight)) { layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; } - if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { + if (nwidth > 0 && nheight > 0) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6abfb59c3..bf0333d55 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -678,8 +678,8 @@ export class DockedFrameRenderer extends React.Component { panelWidth = () => this.layoutDoc && this.layoutDoc.maxWidth ? Math.min(Math.max(NumCast(this.layoutDoc._width), NumCast(this.layoutDoc._nativeWidth)), this._panelWidth) : this._panelWidth; panelHeight = () => this._panelHeight; - nativeWidth = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0; - nativeHeight = () => !this.layoutDoc!.ignoreAspect && !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0; + nativeWidth = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeWidth) || this._panelWidth : 0; + nativeHeight = () => !this.layoutDoc!._fitWidth ? NumCast(this.layoutDoc!._nativeHeight) || this._panelHeight : 0; contentScaling = () => { if (this.layoutDoc!.type === DocumentType.PDF) { @@ -705,8 +705,8 @@ export class DockedFrameRenderer extends React.Component { } return Transform.Identity(); } - get previewPanelCenteringOffset() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } - get widthpercent() { return this.nativeWidth() && !this.layoutDoc!.ignoreAspect ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; } + get previewPanelCenteringOffset() { return this.nativeWidth() ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } + get widthpercent() { return this.nativeWidth() ? `${(this.nativeWidth() * this.contentScaling()) / this.panelWidth() * 100}%` : undefined; } addDocTab = (doc: Doc, dataDoc: Opt, location: string, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index f3dd7c733..c9b7ca221 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -259,7 +259,8 @@ export class CollectionMasonryViewFieldRow extends React.Component "", diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index a9cefae6a..ad9dc8f7c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -185,7 +185,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (!d) return 0; const layoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.()); const nw = NumCast(layoutDoc._nativeWidth); - return Math.min(nw && !d.ignoreAspect && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); + return Math.min(nw && !this.props.Document.fillColumn ? d[WidthSym]() : Number.MAX_VALUE, this.columnWidth / this.numGroupColumns); } getDocHeight(d?: Doc) { if (!d) return 0; @@ -193,9 +193,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const nw = NumCast(layoutDoc._nativeWidth); const nh = NumCast(layoutDoc._nativeHeight); let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); - if (!layoutDoc.ignoreAspect && !layoutDoc._fitWidth && nw && nh) { + if (!layoutDoc._fitWidth && nw && nh) { const aspect = nw && nh ? nh / nw : 1; - if (!(!layoutDoc.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid); + if (!(this.props.Document.fillColumn)) wid = Math.min(layoutDoc[WidthSym](), wid); return wid * aspect; } return layoutDoc._fitWidth ? !nh ? this.props.PanelHeight() - 2 * this.yMargin : diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 48294f9c2..52a18fb99 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -90,11 +90,9 @@ export interface CollectionRenderProps { export class CollectionView extends Touchable { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } - private _reactionDisposer: IReactionDisposer | undefined; private _isChildActive = false; //TODO should this be observable? @observable private _isLightboxOpen = false; @observable private _curLightboxImg = 0; - @observable private _collapsed = true; @observable private static _safeMode = false; public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } @@ -111,20 +109,6 @@ export class CollectionView extends Touchable { return viewField; } - componentDidMount = () => { - this._reactionDisposer = reaction(() => StrCast(this.props.Document._chromeStatus), - () => { - // chrome status is one of disabled, collapsed, or visible. this determines initial state from document - // chrome status may also be view-mode, in reference to stacking view's toggle mode. it is essentially disabled mode, but prevents the toggle button from showing up on the left sidebar. - const chromeStatus = this.props.Document._chromeStatus; - if (chromeStatus && (chromeStatus === "disabled" || chromeStatus === "collapsed" || chromeStatus === "replaced")) { - runInAction(() => this._collapsed = true); - } - }); - } - - componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer(); - // bcz: Argh? What's the height of the collection chromes?? chromeHeight = () => (this.props.Document._chromeStatus === "enabled" ? -60 : 0); @@ -198,7 +182,6 @@ export class CollectionView extends Touchable { @action private collapse = (value: boolean) => { - this._collapsed = value; this.props.Document._chromeStatus = value ? "collapsed" : "enabled"; } diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 53d17b580..eaab4086c 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -60,7 +60,7 @@ export class CollectionFreeFormDocumentView extends DocComponent this.nativeWidth > 0 && !this.props.Document.ignoreAspect && !this.props.fitToBox ? this.width / this.nativeWidth : 1; + contentScaling = () => this.nativeWidth > 0 && !this.props.fitToBox ? this.width / this.nativeWidth : 1; panelWidth = () => (this.dataProvider?.width || this.props.PanelWidth()); panelHeight = () => (this.dataProvider?.height || this.props.PanelHeight()); getTransform = (): Transform => this.props.ScreenToLocalTransform() diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b4ec09aee..330410a92 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -364,18 +364,12 @@ export class DocumentView extends DocComponent(Docu const actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); - const fixedAspect = e.ctrlKey || (!layoutDoc.ignoreAspect && nwidth && nheight); - if (fixedAspect && e.ctrlKey && layoutDoc.ignoreAspect) { - layoutDoc.ignoreAspect = false; - - layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; - layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; - } + const fixedAspect = e.ctrlKey || (nwidth && nheight); if (fixedAspect && (!nwidth || !nheight)) { layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0; layoutDoc._nativeHeight = nheight = layoutDoc._height || 0; } - if (nwidth > 0 && nheight > 0 && !layoutDoc.ignoreAspect) { + if (nwidth > 0 && nheight > 0) { if (Math.abs(dW) > Math.abs(dH)) { if (!fixedAspect) { layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0); @@ -546,10 +540,11 @@ export class DocumentView extends DocComponent(Docu } @undoBatch - public static unfreezeNativeDimensions = action((layoutDoc: Doc): void => { + @action + public static unfreezeNativeDimensions(layoutDoc: Doc) { layoutDoc._nativeWidth = undefined; layoutDoc._nativeHeight = undefined; - }); + } toggleNativeDimensions = () => { if (this.Document._nativeWidth || this.Document._nativeHeight) { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 364bce7a8..5b5768d54 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -375,7 +375,7 @@ export class ImageBox extends DocAnnotatableComponent
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e1c5fd27f..a2aec699f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -52,7 +52,7 @@ export class PDFBox extends DocAnnotatableComponent this._initialScale = this.props.ScreenToLocalTransform().Scale; const nw = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 927)); const nh = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, 1200)); - !this.Document._fitWidth && !this.Document.ignoreAspect && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); + !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); const backup = "oldPath"; const { Document } = this.props; @@ -93,7 +93,7 @@ export class PDFBox extends DocAnnotatableComponent this.dataDoc[this.props.fieldKey + "-numPages"] = np; this.dataDoc[this.props.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = nw * 96 / 72; this.dataDoc[this.props.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = nh * 96 / 72; - !this.Document._fitWidth && !this.Document.ignoreAspect && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); + !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw)); } public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index a722f552e..48890ac8d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -840,8 +840,7 @@ export namespace Doc { export function freezeNativeDimensions(layoutDoc: Doc, width: number, height: number): void { layoutDoc._autoHeight = false; - layoutDoc.ignoreAspect = false; - if (!layoutDoc.ignoreAspect && !layoutDoc._nativeWidth) { + if (!layoutDoc._nativeWidth) { layoutDoc._nativeWidth = NumCast(layoutDoc._width, width); layoutDoc._nativeHeight = NumCast(layoutDoc._height, height); } diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts index 3cc05d3d5..f3726cb5a 100644 --- a/src/new_fields/documentSchemas.ts +++ b/src/new_fields/documentSchemas.ts @@ -34,7 +34,6 @@ export const documentSchema = createSchema({ onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped - ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document isTemplateForField: "string",// when specifies a field key, then the containing document is a template that renders the specified field isBackground: "boolean", // whether document is a background element and ignores input events (can only selet with marquee) treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden -- cgit v1.2.3-70-g09d2 From d4fd33ea337b1136344b73b92e9d5494ff94f090 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 3 Mar 2020 10:49:22 -0500 Subject: fixes to allow navigation from hashtag tag to pivot/schema/stacking views --- src/client/documents/Documents.ts | 2 +- src/client/util/RichTextRules.ts | 10 +++++ src/client/util/RichTextSchema.tsx | 52 +++++++++++++++------- .../collections/CollectionMasonryViewFieldRow.tsx | 12 ++--- .../views/collections/CollectionStackingView.tsx | 30 ++++++------- .../CollectionStackingViewFieldColumn.tsx | 15 +++---- .../views/collections/CollectionViewChromes.scss | 16 +++---- .../views/collections/CollectionViewChromes.tsx | 20 ++++----- .../collectionFreeForm/CollectionFreeFormView.tsx | 24 +++++----- .../authentication/models/current_user_utils.ts | 2 +- 10 files changed, 105 insertions(+), 78 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4df90ceb8..49e7520f2 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -122,7 +122,7 @@ export interface DocumentOptions { displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) borderRounding?: string; boxShadow?: string; - sectionFilter?: string; // field key used to determine headings for sections in stacking and masonry views + _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views schemaColumns?: List; dockingConfig?: string; annotationOn?: Doc; diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index af3b1a81e..70a1a5154 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -100,6 +100,16 @@ export class RichTextRules { const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid }); return state.tr.deleteRange(start, end).insert(start, fieldView); }), + // create an inline view of a tag stored under the '#' field + new InputRule( + new RegExp(/#([a-zA-Z_\-0-9]+)\s$/), + (state, match, start, end) => { + const tag = match[1]; + if (!tag) return state.tr; + this.Document[DataSym]["#"] = tag; + const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" }); + return state.tr.deleteRange(start, end).insert(start, fieldView); + }), // create an inline view of a document {{ : }} // {{:Doc}} => show default view of document {{}} => show layout for this doc {{ : Doc}} => show layout for another doc new InputRule( new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(:[a-zA-Z_ \-0-9]+)?\}\}$/), diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index b2ee7320a..c67b42766 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -25,7 +25,11 @@ import { CollectionSchemaBooleanCell } from "../views/collections/CollectionSche import { ContextMenu } from "../views/ContextMenu"; import { ContextMenuProps } from "../views/ContextMenuItem"; import { Docs } from "../documents/Documents"; -import { CollectionView } from "../views/collections/CollectionView"; +import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { toBlob } from "html-to-image"; +import { listSpec } from "../../new_fields/Schema"; +import { List } from "../../new_fields/List"; +import { SchemaHeaderField } from "../../new_fields/SchemaHeaderField"; const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; @@ -880,7 +884,7 @@ export class DashFieldView { this._fieldSpan.contentEditable = "true"; this._fieldSpan.style.position = "relative"; this._fieldSpan.style.display = "inline-block"; - this._fieldSpan.style.minWidth = "50px"; + this._fieldSpan.style.minWidth = "5px"; this._fieldSpan.style.backgroundColor = "rgba(155, 155, 155, 0.24)"; this._fieldSpan.onkeypress = function (e: any) { e.stopPropagation(); }; this._fieldSpan.onkeyup = function (e: any) { e.stopPropagation(); }; @@ -893,6 +897,21 @@ export class DashFieldView { }, icon: "expand-arrows-alt" }); }; + this._fieldSpan.onblur = function (e: any) { + let newText = self._fieldSpan.innerText.startsWith(":=") ? ":=-computed-" : self._fieldSpan.innerText; + // look for a document whose id === the fieldKey being displayed. If there's a match, then that document + // holds the different enumerated values for the field in the titles of its collected documents. + // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down. + + // alternatively, if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key + DocServer.GetRefField(node.attrs.fieldKey).then(options => { + (options instanceof Doc) && DocListCast(options.data).forEach(opt => StrCast(opt.title).startsWith(newText) && (newText = StrCast(opt.title))); + self._fieldSpan.innerHTML = self._dashDoc![self._fieldKey] = newText; + if (newText.startsWith(":=") && self._dashDoc && e.data === null && !e.inputType.includes("delete")) { + Doc.Layout(tbox.props.Document)[self._fieldKey] = ComputedField.MakeFunction(self._fieldSpan.innerText.substring(2)); + } + }); + } const setDashDoc = (doc: Doc) => { self._dashDoc = doc; @@ -916,19 +935,7 @@ export class DashFieldView { if (e.ctrlKey) { Doc.addEnumerationToTextField(self._textBoxDoc, node.attrs.fieldKey, [Docs.Create.TextDocument(self._fieldSpan.innerText, { title: self._fieldSpan.innerText })]); } - let newText = self._fieldSpan.innerText.startsWith(":=") ? ":=-computed-" : self._fieldSpan.innerText; - // look for a document whose id === the fieldKey being displayed. If there's a match, then that document - // holds the different enumerated values for the field in the titles of its collected documents. - // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down. - - // alternatively, if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key - DocServer.GetRefField(node.attrs.fieldKey).then(options => { - (options instanceof Doc) && DocListCast(options.data).forEach(opt => StrCast(opt.title).startsWith(newText) && (newText = StrCast(opt.title))); - self._fieldSpan.innerHTML = self._dashDoc![self._fieldKey] = newText; - if (newText.startsWith(":=") && self._dashDoc && e.data === null && !e.inputType.includes("delete")) { - Doc.Layout(tbox.props.Document)[self._fieldKey] = ComputedField.MakeFunction(self._fieldSpan.innerText.substring(2)); - } - }); + self._fieldSpan.onblur?.(undefined as any); } }; @@ -937,6 +944,21 @@ export class DashFieldView { this._labelSpan.style.display = "inline"; this._labelSpan.style.fontWeight = "bold"; this._labelSpan.style.fontSize = "larger"; + this._labelSpan.onpointerdown = function (e: any) { + e.stopPropagation(); + if (tbox.props.ContainingCollectionDoc) { + const alias = Doc.MakeAlias(tbox.props.ContainingCollectionDoc); + alias.viewType = CollectionViewType.Time; + let list = Cast(alias.schemaColumns, listSpec(SchemaHeaderField)); + if (!list) { + alias.schemaColumns = list = new List(); + } + list.map(c => c.heading).indexOf("#") === -1 && list.push(new SchemaHeaderField("#", "#f1efeb")); + list.map(c => c.heading).indexOf("text") === -1 && list.push(new SchemaHeaderField("text", "#f1efeb")); + alias._pivotField = "#"; + tbox.props.addDocTab(alias, "onRight"); + } + } this._labelSpan.innerHTML = `${node.attrs.fieldKey}: `; if (node.attrs.docid) { DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && runInAction(() => setDashDoc(dashDoc))); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 3c2cbb5b0..6ebd3194d 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -80,7 +80,7 @@ export class CollectionMasonryViewFieldRow extends React.Component d[key] = castedValue); this.props.parent.onInternalDrop(e, de); @@ -99,7 +99,7 @@ export class CollectionMasonryViewFieldRow extends React.Component { this._createAliasSelected = false; - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); const castedValue = this.getValue(value); if (castedValue) { if (this.props.parent.sectionHeaders) { @@ -138,7 +138,7 @@ export class CollectionMasonryViewFieldRow extends React.Component { this._createAliasSelected = false; - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); const newDoc = Docs.Create.TextDocument("", { _height: 18, _width: 200, title: value }); newDoc[key] = this.getValue(this.props.heading); return this.props.parent.props.addDocument(newDoc); @@ -146,7 +146,7 @@ export class CollectionMasonryViewFieldRow extends React.Component { this._createAliasSelected = false; - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); this.props.docList.forEach(d => d[key] = undefined); if (this.props.parent.sectionHeaders && this.props.headingObject) { const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject); @@ -168,7 +168,7 @@ export class CollectionMasonryViewFieldRow extends React.Component this._sensitivity) { const alias = Doc.MakeAlias(this.props.parent.props.Document); - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); let value = this.getValue(this._heading); value = typeof value === "string" ? `"${value}"` : value; const script = `return doc.${key} === ${value}`; @@ -296,7 +296,7 @@ export class CollectionMasonryViewFieldRow extends React.Component evContents, diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index d1f45af90..f84b0af20 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -31,21 +31,21 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef(); _heightDisposer?: IReactionDisposer; - _sectionFilterDisposer?: IReactionDisposer; + _pivotFieldDisposer?: IReactionDisposer; _docXfs: any[] = []; _columnStart: number = 0; @observable _heightMap = new Map(); @observable _cursor: CursorProperty = "grab"; @observable _scroll = 0; // used to force the document decoration to update when scrolling @computed get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); } - @computed get sectionFilter() { return StrCast(this.props.Document.sectionFilter); } + @computed get pivotField() { return StrCast(this.props.Document._pivotField); } @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); } @computed get xMargin() { return NumCast(this.props.Document._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); } @computed get yMargin() { return Math.max(this.props.Document._showTitle && !this.props.Document._showTitleHover ? 30 : 0, NumCast(this.props.Document._yMargin, 0)); } // 2 * this.gridGap)); } @computed get gridGap() { return NumCast(this.props.Document._gridGap, 10); } @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); } @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; } - @computed get showAddAGroup() { return (this.sectionFilter && (this.props.Document._chromeStatus !== 'view-mode' && this.props.Document._chromeStatus !== 'disabled')); } + @computed get showAddAGroup() { return (this.pivotField && (this.props.Document._chromeStatus !== 'view-mode' && this.props.Document._chromeStatus !== 'disabled')); } @computed get columnWidth() { return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250)); @@ -73,7 +73,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } get Sections() { - if (!this.sectionFilter || this.sectionHeaders instanceof Promise) return new Map(); + if (!this.pivotField || this.sectionHeaders instanceof Promise) return new Map(); if (this.sectionHeaders === undefined) { setTimeout(() => this.props.Document.sectionHeaders = new List(), 0); @@ -83,18 +83,18 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const fields = new Map(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); let changed = false; this.filteredChildren.map(d => { - const sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object; + const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object; // the next five lines ensures that floating point rounding errors don't create more than one section -syip const parsed = parseInt(sectionValue.toString()); const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue; // look for if header exists already - const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`)); + const existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`)); if (existingHeader) { fields.get(existingHeader)!.push(d); } else { - const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`); + const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`); fields.set(newSchemaHeader, [d]); sectionHeaders.push(newSchemaHeader); changed = true; @@ -134,15 +134,15 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { ); // reset section headers when a new filter is inputted - this._sectionFilterDisposer = reaction( - () => this.sectionFilter, + this._pivotFieldDisposer = reaction( + () => this.pivotField, () => this.props.Document.sectionHeaders = new List() ); } componentWillUnmount() { super.componentWillUnmount(); - this._heightDisposer && this._heightDisposer(); - this._sectionFilterDisposer && this._sectionFilterDisposer(); + this._heightDisposer?.(); + this._pivotFieldDisposer?.(); } @action @@ -278,7 +278,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } headings = () => Array.from(this.Sections.keys()); sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { - const key = this.sectionFilter; + const key = this.pivotField; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]); if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) { @@ -313,7 +313,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { - const key = this.sectionFilter; + const key = this.pivotField; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]); if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) { @@ -341,7 +341,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (value && this.sectionHeaders) { const schemaHdrField = new SchemaHeaderField(value); this.sectionHeaders.push(schemaHdrField); - Doc.addEnumerationToTextField(undefined, this.sectionFilter, [Docs.Create.TextDocument(value, { title: value, _backgroundColor: schemaHdrField.color })]); + Doc.addEnumerationToTextField(undefined, this.pivotField, [Docs.Create.TextDocument(value, { title: value, _backgroundColor: schemaHdrField.color })]); return true; } return false; @@ -370,7 +370,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get renderedSections() { TraceMobx(); let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]]; - if (this.sectionFilter) { + if (this.pivotField) { const entries = Array.from(this.Sections.entries()); sections = entries.sort(this.sortFunc); } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 516e583d4..646b433bf 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -23,7 +23,6 @@ import { CollectionStackingView } from "./CollectionStackingView"; import { setupMoveUpEvents, emptyFunction } from "../../../Utils"; import "./CollectionStackingView.scss"; import { listSpec } from "../../../new_fields/Schema"; -import { Schema } from "prosemirror-model"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -62,7 +61,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { if (de.complete.docDragData) { - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); const castedValue = this.getValue(this._heading); de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, false)); this.props.parent.onInternalDrop(e, de); @@ -85,7 +84,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); const castedValue = this.getValue(value); if (castedValue) { if (this.props.parent.sectionHeaders) { @@ -126,7 +125,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { if (!value) return false; - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, title: value, _autoHeight: true }); newDoc[key] = this.getValue(this.props.heading); const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0); @@ -137,7 +136,7 @@ export class CollectionStackingViewFieldColumn extends React.Component { - const key = StrCast(this.props.parent.props.Document.sectionFilter); + const key = StrCast(this.props.parent.props.Document._pivotField); this.props.docList.forEach(d => d[key] = undefined); if (this.props.parent.sectionHeaders && this.props.headingObject) { const index = this.props.parent.sectionHeaders.indexOf(this.props.headingObject); @@ -161,8 +160,8 @@ export class CollectionStackingViewFieldColumn extends React.Component { const alias = Doc.MakeAlias(this.props.parent.props.Document); alias._width = this.props.parent.props.PanelWidth() / (Cast(this.props.parent.props.Document.sectionHeaders, listSpec(SchemaHeaderField))?.length || 1); - alias.sectionFilter = undefined; - const key = StrCast(this.props.parent.props.Document.sectionFilter); + alias._pivotField = undefined; + const key = StrCast(this.props.parent.props.Document._pivotField); let value = this.getValue(this._heading); value = typeof value === "string" ? `"${value}"` : value; alias.viewSpecScript = ScriptField.MakeFunction(`doc.${key} === ${value}`, { doc: Doc.name }); @@ -277,7 +276,7 @@ export class CollectionStackingViewFieldColumn extends React.Component c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData ?.draggedDocuments || [])); + this._buttonizableCommands.filter(c => c.title === this._currentKey).map(c => c.immediate(de.complete.docDragData?.draggedDocuments || [])); e.stopPropagation(); } return true; @@ -472,7 +472,7 @@ export class CollectionStackingViewChrome extends React.Component => { value = value.toLowerCase(); @@ -510,26 +510,26 @@ export class CollectionStackingViewChrome extends React.Component { - this.props.CollectionView.props.Document.sectionFilter = value; + this.props.CollectionView.props.Document._pivotField = value; return true; } @action toggleSort = () => { this.props.CollectionView.props.Document.stackingHeadersSortDescending = !this.props.CollectionView.props.Document.stackingHeadersSortDescending; }; - @action resetValue = () => { this._currentKey = this.sectionFilter; }; + @action resetValue = () => { this._currentKey = this.pivotField; }; render() { return (
-
-
+
+
GROUP ITEMS BY:
-
+
this.sectionFilter} + GetValue={() => this.pivotField} autosuggestProps={ { resetValue: this.resetValue, @@ -551,7 +551,7 @@ export class CollectionStackingViewChrome extends React.Component
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 055be7f86..ea86bff99 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,20 +1,22 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; -import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload, faTextHeight } from "@fortawesome/free-solid-svg-icons"; -import { action, computed, observable, ObservableMap, reaction, runInAction, IReactionDisposer } from "mobx"; +import { faBraille, faChalkboard, faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faFileUpload, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from "../../../../new_fields/Doc"; +import { Doc, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc"; import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas"; import { Id } from "../../../../new_fields/FieldSymbols"; -import { InkTool, InkField, InkData } from "../../../../new_fields/InkField"; +import { InkData, InkField, InkTool } from "../../../../new_fields/InkField"; +import { List } from "../../../../new_fields/List"; +import { RichTextField } from "../../../../new_fields/RichTextField"; import { createSchema, listSpec, makeInterface } from "../../../../new_fields/Schema"; import { ScriptField } from "../../../../new_fields/ScriptField"; -import { Cast, NumCast, ScriptCast, BoolCast, StrCast, FieldValue } from "../../../../new_fields/Types"; +import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../new_fields/Types"; import { TraceMobx } from "../../../../new_fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; -import { CurrentUserUtils } from "../../../../server/authentication/models/current_user_utils"; import { aggregateBounds, intersectRect, returnOne, Utils } from "../../../../Utils"; +import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; import { Docs } from "../../../documents/Documents"; import { DocumentManager } from "../../../util/DocumentManager"; @@ -29,10 +31,11 @@ import { ContextMenu } from "../../ContextMenu"; import { ContextMenuProps } from "../../ContextMenuItem"; import { InkingControl } from "../../InkingControl"; import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; -import { DocumentContentsView } from "../../nodes/DocumentContentsView"; +import { DocumentViewProps } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; import PDFMenu from "../../pdf/PDFMenu"; +import { CollectionDockingView } from "../CollectionDockingView"; import { CollectionSubView } from "../CollectionSubView"; import { computePivotLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines"; import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors"; @@ -40,13 +43,6 @@ import "./CollectionFreeFormView.scss"; import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; -import { RichTextField } from "../../../../new_fields/RichTextField"; -import { List } from "../../../../new_fields/List"; -import { DocumentViewProps } from "../../nodes/DocumentView"; -import { CollectionDockingView } from "../CollectionDockingView"; -import { MainView } from "../../MainView"; -import { TouchScrollableMenuItem } from "../../TouchScrollableMenu"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 6216ab7e6..0f8d8fec8 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -254,7 +254,7 @@ export class CurrentUserUtils { // Finally, setup the list of buttons to display in the sidebar doc.sidebarButtons = Docs.Create.StackingDocument([doc.SearchBtn as Doc, doc.LibraryBtn as Doc, doc.ToolsBtn as Doc], { - _width: 500, _height: 80, boxShadow: "0 0", sectionFilter: "title", hideHeadings: true, ignoreClick: true, + _width: 500, _height: 80, boxShadow: "0 0", _pivotField: "title", hideHeadings: true, ignoreClick: true, _chromeStatus: "disabled", title: "library stack", backgroundColor: "dimGray", }); } -- cgit v1.2.3-70-g09d2 From 288e4d24b61d281819b7f0b5bb697edbcc9ed173 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 5 Mar 2020 10:34:20 -0500 Subject: masonry view fix to add notes to the top. fixed uploadURI to use image resizer better --- .../collections/CollectionMasonryViewFieldRow.tsx | 24 +++++++++++++--------- src/server/ApiManagers/UploadManager.ts | 7 +++---- 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 6ebd3194d..af3e18a4b 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -8,7 +8,7 @@ import Measure from "react-measure"; import { Doc } from "../../../new_fields/Doc"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { ScriptField } from "../../../new_fields/ScriptField"; -import { StrCast } from "../../../new_fields/Types"; +import { StrCast, NumCast } from "../../../new_fields/Types"; import { numberRange } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; @@ -139,9 +139,10 @@ export class CollectionMasonryViewFieldRow extends React.Component { this._createAliasSelected = false; const key = StrCast(this.props.parent.props.Document._pivotField); - const newDoc = Docs.Create.TextDocument("", { _height: 18, _width: 200, title: value }); + const newDoc = Docs.Create.TextDocument(value, { _autoHeight: true, _width: 200, title: value }); newDoc[key] = this.getValue(this.props.heading); - return this.props.parent.props.addDocument(newDoc); + const docs = this.props.parent.childDocList; + return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this.props.parent.props.addDocument(newDoc); } deleteRow = undoBatch(action(() => { @@ -274,6 +275,15 @@ export class CollectionMasonryViewFieldRow extends React.Component + {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? +
+ +
: null + }
- {(chromeStatus !== 'view-mode' && chromeStatus !== 'disabled') ? -
- -
: null - }
; } @@ -317,7 +321,7 @@ export class CollectionMasonryViewFieldRow extends React.Component + style={{ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this._color : "lightgrey" }}> {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 08a374fa9..50a759c9d 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -7,7 +7,7 @@ import { extname, basename, dirname } from 'path'; import { createReadStream, createWriteStream, unlink } from "fs"; import { publicDirectory, filesDirectory } from ".."; import { Database } from "../database"; -import { DashUploadUtils } from "../DashUploadUtils"; +import { DashUploadUtils, InjectSize, SizeSuffix } from "../DashUploadUtils"; import * as sharp from 'sharp'; import { AcceptibleMedia, Upload } from "../SharedMediaTypes"; import { normalize } from "path"; @@ -199,14 +199,13 @@ export default class UploadManager extends ApiManager { res.status(401).send("incorrect parameters specified"); return; } - return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, filename)).then((savedName: string) => { + return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, SizeSuffix.Original))).then((savedName: string) => { const ext = extname(savedName).toLowerCase(); const { pngs, jpgs } = AcceptibleMedia; const resizers = [ { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: "_s" }, { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" }, { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: "_l" }, - { resizer: sharp().resize(1200, undefined, { withoutEnlargement: true }), suffix: "_o" }, // bcz: this should just be the original image, not a resized version ]; let isImage = false; if (pngs.includes(ext)) { @@ -225,7 +224,7 @@ export default class UploadManager extends ApiManager { const path = serverPathToFile(Directory.images, filename + resizer.suffix + ext); createReadStream(savedName).pipe(resizer.resizer).pipe(createWriteStream(path)); }); - + } res.send(clientPathToFile(Directory.images, filename + ext)); }); -- cgit v1.2.3-70-g09d2 From 490584f292e1da100bbb26ea3be1d06b5f63232d Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 6 Apr 2020 14:04:39 -0400 Subject: fixed autoHeight for masonry & removing observers when switching out --- .../collections/CollectionMasonryViewFieldRow.tsx | 12 ++++++++++-- .../views/collections/CollectionStackingView.tsx | 20 +++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index af3e18a4b..b272151c1 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -36,6 +36,9 @@ interface CMVFieldRowProps { createDropTarget: (ele: HTMLDivElement) => void; screenToLocalTransform: () => Transform; setDocHeight: (key: string, thisHeight: number) => void; + observeHeight: (myref: any) => void; + unobserveHeight: (myref: any) => void; + showHandle: boolean; } @observer @@ -53,14 +56,19 @@ export class CollectionMasonryViewFieldRow extends React.Component = React.createRef(); private _sensitivity: number = 16; private _counter: number = 0; - + private _ele: any; createRowDropRef = (ele: HTMLDivElement | null) => { this._dropDisposer && this._dropDisposer(); if (ele) { + this._ele = ele; + this.props.observeHeight(ele); this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this)); } } + componentWillUnmount() { + this.props.unobserveHeight(this._ele); + } getTrueHeight = () => { if (this._collapsed) { @@ -293,7 +301,7 @@ export class CollectionMasonryViewFieldRow extends React.Component list + ` ${this.props.parent.columnWidth}px`, ""), }}> {this.props.parent.children(this.props.docList)} - {this.props.parent.columnDragger} + {this.props.showHandle && this.props.parent.props.active() ? this.props.parent.columnDragger : (null)}
; } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 84e6ecced..b066f2be3 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -295,7 +295,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { this.refList.push(ref); const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; this.observer = new _global.ResizeObserver(action((entries: any) => { - if (this.props.Document._autoHeight && ref && !SelectionManager.GetIsDragging()) { + if (this.props.Document._autoHeight && ref && this.refList.length && !SelectionManager.GetIsDragging()) { Doc.Layout(doc)._height = Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))); } })); @@ -325,7 +325,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return this.props.ScreenToLocalTransform().translate(offset[0], offset[1] + offsety); } - sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { + sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => { const key = this.pivotField; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]); @@ -335,6 +335,20 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))); return this.refList.splice(this.refList.indexOf(ref), 1)} + observeHeight={(ref) => { + if (ref) { + this.refList.push(ref); + const doc = this.props.DataDoc && this.props.DataDoc.layout === this.layoutDoc ? this.props.DataDoc : this.layoutDoc; + this.observer = new _global.ResizeObserver(action((entries: any) => { + if (this.props.Document._autoHeight && ref && this.refList.length && !SelectionManager.GetIsDragging()) { + Doc.Layout(doc)._height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0) + } + })); + this.observer.observe(ref); + } + }} key={heading ? heading.heading : ""} rows={rows} headings={this.headings} @@ -387,7 +401,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { const entries = Array.from(this.Sections.entries()); sections = entries.sort(this.sortFunc); } - return sections.map(section => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1])); + return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0)); } -- cgit v1.2.3-70-g09d2 From 7f6bc06f72e070bd8b45eba8b4c0669ee398387b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 27 Apr 2020 02:55:59 -0400 Subject: fixed fontsize inheritance. fixed masonry view with a columnWidth of -1 to fit width. cleaned up masonry and editableViews --- src/client/documents/Documents.ts | 3 +- src/client/util/RichTextRules.ts | 2 +- src/client/views/EditableView.tsx | 2 - .../collections/CollectionMasonryViewFieldRow.tsx | 53 ++++++++-------------- .../views/collections/CollectionStackingView.tsx | 4 +- .../CollectionStackingViewFieldColumn.tsx | 5 -- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/FormattedTextBox.tsx | 4 +- src/client/views/nodes/LabelBox.tsx | 3 +- src/client/views/nodes/SliderBox.tsx | 2 +- src/new_fields/documentSchemas.ts | 2 + 11 files changed, 35 insertions(+), 49 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 5e0890e76..c64916897 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -110,7 +110,8 @@ export interface DocumentOptions { isBackground?: boolean; isLinkButton?: boolean; columnWidth?: number; - fontSize?: number; + _fontSize?: number; + _fontFamily?: string; curPage?: number; currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) value is in seconds displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 3746199ba..8b11be6fb 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -143,7 +143,7 @@ export class RichTextRules { textDoc.inlineTextCount = numInlines + 1; const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation - const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); + const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: 9, title: "inline comment" }); textDocInline.title = inlineFieldKey; // give the annotation its own title textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 2219966e5..c51173ad3 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -46,7 +46,6 @@ export interface EditableProps { menuCallback?: (x: number, y: number) => void; showMenuOnLoad?: boolean; HeadingObject?: SchemaHeaderField | undefined; - HeadingsHack?: number; toggle?: () => void; color?: string | undefined; } @@ -60,7 +59,6 @@ export interface EditableProps { export class EditableView extends React.Component { public static loadId = ""; @observable _editing: boolean = false; - @observable _headingsHack: number = 1; constructor(props: EditableProps) { super(props); diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index b272151c1..42f0c4311 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -4,7 +4,6 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import Measure from "react-measure"; import { Doc } from "../../../new_fields/Doc"; import { PastelSchemaPalette, SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; import { ScriptField } from "../../../new_fields/ScriptField"; @@ -46,7 +45,6 @@ export class CollectionMasonryViewFieldRow extends React.Component = React.createRef(); private _sensitivity: number = 16; - private _counter: number = 0; private _ele: any; createRowDropRef = (ele: HTMLDivElement | null) => { @@ -167,7 +164,6 @@ export class CollectionMasonryViewFieldRow extends React.Component { this._createAliasSelected = false; if (this.props.headingObject) { - this._headingsHack++; this.props.headingObject.setCollapsed(!this.props.headingObject.collapsed); this.toggleVisibility(); } @@ -197,6 +193,10 @@ export class CollectionMasonryViewFieldRow extends React.Component); } - handleResize = (size: any) => { - if (++this._counter !== 1) { - this.getTrueHeight(); - } - } - @computed get contentLayout() { const rows = Math.max(1, Math.min(this.props.docList.length, Math.floor((this.props.parent.props.PanelWidth() - 2 * this.props.parent.xMargin) / (this.props.parent.columnWidth + this.props.parent.gridGap)))); const style = this.props.parent; @@ -277,7 +271,6 @@ export class CollectionMasonryViewFieldRow extends React.Component : !this.props.headingObject ? (null) :
-
- {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) : + {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
} - - {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) : + } + {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
} {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
@@ -349,17 +316,15 @@ export class CollectionMasonryViewFieldRow extends React.Component; } render() { - const background = this._background; //to account for observables in Measure - const contentlayout = this.contentLayout; - const headingview = this.headingView; + const background = this._background; return
- {headingview} - {contentlayout} + {this.headingView} + {this.contentLayout}
; } } \ No newline at end of file diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index c05594bbc..7f12aea29 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -218,7 +218,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp public hyperlinkTerms = (terms: string[], target: Doc) => { if (this._editorView && (this._editorView as any).docView && terms.some(t => t)) { const res = terms.filter(t => t).map(term => this.findInNode(this._editorView!, this._editorView!.state.doc, term)); - let tr = this._editorView.state.tr; + const tr = this._editorView.state.tr; const flattened: TextSelection[] = []; res.map(r => r.map(h => flattened.push(h))); const lastSel = Math.min(flattened.length - 1, this._searchIndex); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index b5a44a8bc..1d41c3570 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -20,6 +20,7 @@ import { PrefetchProxy } from "../../../new_fields/Proxy"; import { FormattedTextBox } from "../../../client/views/nodes/FormattedTextBox"; import { MainView } from "../../../client/views/MainView"; import { DocumentType } from "../../../client/documents/DocumentTypes"; +import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; export class CurrentUserUtils { private static curr_id: string; @@ -108,10 +109,15 @@ export class CurrentUserUtils { const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: 12 }; const descriptionWrapperOpts = { title: "descriptions", _height: 300, columnWidth: -1, treeViewHideTitle: true, _pivotField: "title" }; - const descriptionWrapper = MasonryDocument([short, long], { ...shared, ...descriptionWrapperOpts }); - const detailView = Docs.Create.StackingDocument([carousel, details, descriptionWrapper], { ...shared, ...detailViewOpts }); + const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); + descriptionWrapper.sectionHeaders = new List([ + new SchemaHeaderField("[Long Description]", "LemonChiffon", undefined, undefined, undefined, true), + new SchemaHeaderField("[Details]", "lightBlue", undefined, undefined, undefined, true), + ]); + const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts }); detailView.isTemplateDoc = makeTemplate(detailView); + details.title = "Details"; short.title = "A Short Description"; long.title = "Long Description"; -- cgit v1.2.3-70-g09d2 From 6b2896756c55727ed397c223187cb03fe8a51a59 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 28 Apr 2020 15:51:34 -0400 Subject: ignore rightlick events on masonry header to allow for context menu from doc decorations --- src/client/views/collections/CollectionMasonryViewFieldRow.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/client/views/collections/CollectionMasonryViewFieldRow.tsx') diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 3bada43f0..7ad15ef41 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -183,8 +183,10 @@ export class CollectionMasonryViewFieldRow extends React.Component) => { - setupMoveUpEvents(this, e, this.headerMove, emptyFunction, () => (this.props.parent.props.Document._chromeStatus === "disabled") && this.collapseSection(e)); - this._createAliasSelected = false; + if (e.button === 0 && !e.ctrlKey) { + setupMoveUpEvents(this, e, this.headerMove, emptyFunction, () => (this.props.parent.props.Document._chromeStatus === "disabled") && this.collapseSection(e)); + this._createAliasSelected = false; + } } renderColorPicker = () => { -- cgit v1.2.3-70-g09d2