From aad42660123c227cbe2152fbbbc159f6a38fca17 Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Tue, 15 Oct 2019 16:29:08 -0400 Subject: library flyout changes --- src/client/views/MainView.scss | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index e61494e71..851818099 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -4,8 +4,9 @@ .mainView-tabButtons { position: relative; - width:100%; + width: 100%; } + // add nodes menu. Note that the + button is actually an input label, not an actual button. .mainView-docButtons { position: absolute; @@ -22,22 +23,26 @@ overflow: auto; z-index: 1; } + .mainView-mainContent { - width:100%; - height:100%; - position:absolute; + width: 100%; + height: 100%; + position: absolute; } -.mainView-flyoutContainer{ - display:flex; + +.mainView-flyoutContainer { + display: flex; flex-direction: column; position: absolute; - width:100%; - height:100%; + width: 100%; + height: 100%; border: black 1px solid; + .documentView-node-topmost { background: lightgrey; } } + .mainView-mainDiv { width: 100%; height: 100%; @@ -63,7 +68,7 @@ .mainView-expandFlyoutButton { position: absolute; - top: 30px; + top: 100px; right: 30px; cursor: pointer; } @@ -76,6 +81,7 @@ border-radius: 5px; position: absolute; z-index: 1; + touch-action: none; } .mainView-workspace { -- cgit v1.2.3-70-g09d2 From 462cd06a1bff6e510132a4434a72fd7b4245cfdf Mon Sep 17 00:00:00 2001 From: Stanley Yip Date: Tue, 15 Oct 2019 17:29:57 -0400 Subject: good enough for stanley to start texting i think --- src/client/views/DocComponent.tsx | 7 +-- src/client/views/Main.scss | 2 +- src/client/views/MainView.scss | 11 ---- .../views/collections/CollectionDockingView.scss | 69 ++++++++++++++++++++-- .../views/collections/CollectionDockingView.tsx | 20 ++++--- .../collections/collectionFreeForm/MarqueeView.tsx | 4 +- 6 files changed, 79 insertions(+), 34 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 9d7c06750..94717352e 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -7,18 +7,13 @@ import { listSpec } from '../../new_fields/Schema'; import { InkingControl } from './InkingControl'; import { InkTool } from '../../new_fields/InkField'; -<<<<<<< HEAD -export function DocComponent

(schemaCtor: (doc: Doc) => T) { - class Component extends Touchable

{ -======= /// DocComponents returns a generic base class for React views of document fields that are not interactive interface DocComponentProps { Document: Doc; } export function DocComponent

(schemaCtor: (doc: Doc) => T) { - class Component extends React.Component

{ ->>>>>>> 33811c112c7e479813908ba10f72813954a3e289 + class Component extends Touchable

{ //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @computed get Document(): T { diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 0335e12a5..4a174f8c7 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -36,7 +36,7 @@ p { ::-webkit-scrollbar { -webkit-appearance: none; height: 8px; - width: 8px; + width: 20px; } ::-webkit-scrollbar-thumb { diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index a6b28f488..25af67161 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -34,15 +34,9 @@ display: flex; flex-direction: column; position: absolute; -<<<<<<< HEAD width: 100%; height: 100%; - border: black 1px solid; -======= - width:100%; - height:100%; ->>>>>>> 33811c112c7e479813908ba10f72813954a3e289 .documentView-node-topmost { background: lightgrey; } @@ -73,13 +67,8 @@ .mainView-expandFlyoutButton { position: absolute; -<<<<<<< HEAD top: 100px; right: 30px; -======= - top: 5px; - right: 5px; ->>>>>>> 33811c112c7e479813908ba10f72813954a3e289 cursor: pointer; } diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 6f5abd05b..b4ab8d550 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -1,12 +1,13 @@ @import "../../views/globalCssVariables.scss"; -.lm_active .messageCounter{ - color:white; +.lm_active .messageCounter { + color: white; background: #999999; } + .messageCounter { - width:18px; - height:20px; + width: 18px; + height: 20px; text-align: center; border-radius: 20px; margin-left: 5px; @@ -18,22 +19,29 @@ .collectiondockingview-container { width: 100%; - height:100%; + height: 100%; border-style: solid; border-width: $COLLECTION_BORDER_WIDTH; position: absolute; top: 0; left: 0; overflow: hidden; + + .collectionDockingView-dragAsDocument { + touch-action: none; + } + .lm_controls>li { opacity: 0.6; transform: scale(1.2); } + .lm_maximised .lm_controls .lm_maximise { opacity: 1; transform: scale(0.8); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAKElEQVR4nGP8////fwYCgImQAgYGBgYWKM2IR81/okwajIpgvsMbVgAwgQYRVakEKQAAAABJRU5ErkJggg==) !important; } + .flexlayout__layout { left: 0; top: 0; @@ -42,17 +50,21 @@ position: absolute; overflow: hidden; } + .flexlayout__splitter { background-color: black; } + .flexlayout__splitter:hover { background-color: #333; } + .flexlayout__splitter_drag { border-radius: 5px; background-color: #444; z-index: 1000; } + .flexlayout__outline_rect { position: absolute; cursor: move; @@ -62,6 +74,7 @@ z-index: 1000; box-sizing: border-box; } + .flexlayout__outline_rect_edge { cursor: move; border: 2px solid #b7d1b5; @@ -70,12 +83,14 @@ z-index: 1000; box-sizing: border-box; } + .flexlayout__edge_rect { position: absolute; z-index: 1000; box-shadow: inset 0 0 5px rgba(0, 0, 0, .2); background-color: lightgray; } + .flexlayout__drag_rect { position: absolute; cursor: move; @@ -94,11 +109,13 @@ padding: 10px; word-wrap: break-word; } + .flexlayout__tabset { overflow: hidden; background-color: #222; box-sizing: border-box; } + .flexlayout__tab { overflow: auto; position: absolute; @@ -106,6 +123,7 @@ background-color: #222; color: black; } + .flexlayout__tab_button { cursor: pointer; padding: 2px 8px 3px 8px; @@ -117,28 +135,35 @@ vertical-align: top; box-sizing: border-box; } + .flexlayout__tab_button--selected { color: #ddd; background-color: #222; } + .flexlayout__tab_button--unselected { color: gray; } + .flexlayout__tab_button_leading { display: inline-block; } + .flexlayout__tab_button_content { display: inline-block; } + .flexlayout__tab_button_textbox { float: left; border: none; color: lightgreen; background-color: #222; } + .flexlayout__tab_button_textbox:focus { outline: none; } + .flexlayout__tab_button_trailing { display: inline-block; margin-left: 5px; @@ -146,10 +171,12 @@ width: 8px; height: 8px; } + .flexlayout__tab_button:hover .flexlayout__tab_button_trailing, .flexlayout__tab_button--selected .flexlayout__tab_button_trailing { background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center; } + .flexlayout__tab_button_overflow { float: left; width: 20px; @@ -162,6 +189,7 @@ font-family: Arial, sans-serif; background: transparent url("../../../../node_modules/flexlayout-react/images/more.png") no-repeat left; } + .flexlayout__tabset_header { position: absolute; left: 0; @@ -172,6 +200,7 @@ /*box-shadow: inset 0px 0px 3px 0px rgba(136, 136, 136, 0.54);*/ box-sizing: border-box; } + .flexlayout__tab_header_inner { position: absolute; left: 0; @@ -179,6 +208,7 @@ bottom: 0; width: 10000px; } + .flexlayout__tab_header_outer { background-color: black; position: absolute; @@ -188,12 +218,15 @@ /*height: 100px;*/ overflow: hidden; } + .flexlayout__tabset-selected { background-image: linear-gradient(#111, #444); } + .flexlayout__tabset-maximized { background-image: linear-gradient(#666, #333); } + .flexlayout__tab_toolbar { position: absolute; display: flex; @@ -203,6 +236,7 @@ bottom: 0; right: 0; } + .flexlayout__tab_toolbar_button-min { width: 20px; height: 20px; @@ -210,6 +244,7 @@ outline-width: 0; background: transparent url("../../../../node_modules/flexlayout-react/images/maximize.png") no-repeat center; } + .flexlayout__tab_toolbar_button-max { width: 20px; height: 20px; @@ -217,14 +252,18 @@ outline-width: 0; background: transparent url("../../../../node_modules/flexlayout-react/images/restore.png") no-repeat center; } + .flexlayout__popup_menu {} + .flexlayout__popup_menu_item { padding: 2px 10px 2px 10px; color: #ddd; } + .flexlayout__popup_menu_item:hover { background-color: #444444; } + .flexlayout__popup_menu_container { box-shadow: inset 0 0 5px rgba(0, 0, 0, .15); border: 1px solid #555; @@ -233,33 +272,39 @@ position: absolute; z-index: 1000; } + .flexlayout__border_top { background-color: black; border-bottom: 1px solid #ddd; box-sizing: border-box; overflow: hidden; } + .flexlayout__border_bottom { background-color: black; border-top: 1px solid #333; box-sizing: border-box; overflow: hidden; } + .flexlayout__border_left { background-color: black; border-right: 1px solid #333; box-sizing: border-box; overflow: hidden; } + .flexlayout__border_right { background-color: black; border-left: 1px solid #333; box-sizing: border-box; overflow: hidden; } + .flexlayout__border_inner_bottom { display: flex; } + .flexlayout__border_inner_left { position: absolute; white-space: nowrap; @@ -267,6 +312,7 @@ transform-origin: top right; transform: rotate(-90deg); } + .flexlayout__border_inner_right { position: absolute; white-space: nowrap; @@ -274,6 +320,7 @@ transform-origin: top left; transform: rotate(90deg); } + .flexlayout__border_button { background-color: #222; color: white; @@ -285,29 +332,36 @@ vertical-align: top; box-sizing: border-box; } + .flexlayout__border_button--selected { color: #ddd; background-color: #222; } + .flexlayout__border_button--unselected { color: gray; } + .flexlayout__border_button_leading { float: left; display: inline; } + .flexlayout__border_button_content { display: inline-block; } + .flexlayout__border_button_textbox { float: left; border: none; color: green; background-color: #ddd; } + .flexlayout__border_button_textbox:focus { outline: none; } + .flexlayout__border_button_trailing { display: inline-block; margin-left: 5px; @@ -315,10 +369,12 @@ width: 8px; height: 8px; } + .flexlayout__border_button:hover .flexlayout__border_button_trailing, .flexlayout__border_button--selected .flexlayout__border_button_trailing { background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center; } + .flexlayout__border_toolbar_left { position: absolute; display: flex; @@ -328,6 +384,7 @@ left: 0; right: 0; } + .flexlayout__border_toolbar_right { position: absolute; display: flex; @@ -337,6 +394,7 @@ left: 0; right: 0; } + .flexlayout__border_toolbar_top { position: absolute; display: flex; @@ -346,6 +404,7 @@ bottom: 0; right: 0; } + .flexlayout__border_toolbar_bottom { position: absolute; display: flex; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1f78c8c97..5e00fda36 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -426,15 +426,17 @@ export class CollectionDockingView extends React.Component { - e.preventDefault(); - e.stopPropagation(); - DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, { - handlers: { dragComplete: emptyFunction }, - hideSource: false - }); - }}>, dragSpan); + ReactDOM.render( { + e.preventDefault(); + e.stopPropagation(); + DragManager.StartDocumentDrag([dragSpan], new DragManager.DocumentDragData([doc]), e.clientX, e.clientY, { + handlers: { dragComplete: emptyFunction }, + hideSource: false + }); + }}>, dragSpan); ReactDOM.render(, gearSpan); // ReactDOM.render( { // where === "onRight" ? CollectionDockingView.AddRightSplit(doc, dataDoc) : CollectionDockingView.Instance.AddTab(stack, doc, dataDoc); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index e9f4c40a6..07db6354f 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -198,7 +198,7 @@ export class MarqueeView extends React.Component } this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]); } - if (!this._commandExecuted) { + if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100)) { MarqueeOptionsMenu.Instance.createCollection = this.collection; MarqueeOptionsMenu.Instance.delete = this.delete; MarqueeOptionsMenu.Instance.summarize = this.summary; @@ -213,7 +213,7 @@ export class MarqueeView extends React.Component MarqueeOptionsMenu.Instance.fadeOut(true); document.removeEventListener("pointerdown", hideMarquee); } - document.addEventListener("pointerdown", hideMarquee) + document.addEventListener("pointerdown", hideMarquee); if (e.altKey) { e.preventDefault(); -- cgit v1.2.3-70-g09d2 From d7150995d62c498ab8435de986b90d98bdca020c Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 12 Nov 2019 15:07:03 -0500 Subject: a bunch of cleanup and bug fixes to text links and doculink buttons --- src/client/documents/Documents.ts | 5 +- src/client/util/LinkManager.ts | 2 - src/client/util/RichTextSchema.tsx | 4 +- src/client/util/TooltipTextMenu.tsx | 23 +++--- src/client/views/DocumentDecorations.tsx | 10 ++- src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/Main.tsx | 24 ------- src/client/views/MainView.scss | 2 +- src/client/views/MainView.tsx | 8 +-- src/client/views/linking/LinkMenuItem.tsx | 1 - src/client/views/nodes/FormattedTextBox.tsx | 9 ++- src/client/views/nodes/FormattedTextBoxComment.tsx | 83 ++++++++++++---------- src/client/views/nodes/PDFBox.scss | 2 + src/client/views/nodes/PDFBox.tsx | 76 +++++++++++--------- src/client/views/pdf/Annotation.tsx | 6 +- src/client/views/pdf/PDFViewer.tsx | 14 +--- src/new_fields/Doc.ts | 2 +- .../authentication/models/current_user_utils.ts | 1 - 18 files changed, 127 insertions(+), 147 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d1fcabc4a..ba9f87025 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -257,6 +257,9 @@ export namespace Docs { return PrototypeMap.get(type)!; } + /** + * A collection of all links in the database. Ideally, this would be a search, but for now all links are cached here. + */ export function MainLinkDocument() { return Prototypes.get(DocumentType.LINKDOC); } @@ -703,6 +706,7 @@ export namespace DocUtils { linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title; linkDocProto.linkDescription = description; + linkDocProto.isPrototype = true; linkDocProto.anchor1 = source.doc; linkDocProto.anchor2 = target.doc; @@ -714,7 +718,6 @@ export namespace DocUtils { linkDocProto.anchor2Timecode = target.doc.currentTimecode; linkDocProto.layoutKey1 = DocuLinkBox.LayoutString("anchor1"); linkDocProto.layoutKey2 = DocuLinkBox.LayoutString("anchor2"); - linkDocProto.borderRounding = "20"; linkDocProto.width = linkDocProto.height = 0; linkDocProto.isBackground = true; linkDocProto.isButton = true; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index ee2f2dadc..eedc4967d 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -34,8 +34,6 @@ export class LinkManager { // the linkmanagerdoc stores a list of docs representing all linkdocs in 'allLinks' and a list of strings representing all group types in 'allGroupTypes' // lists of strings representing the metadata keys for each group type is stored under a key that is the same as the group type public get LinkManagerDoc(): Doc | undefined { - // return FieldValue(Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc)); - return Docs.Prototypes.MainLinkDocument(); } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a5e0ca720..76b8aeaa1 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -305,8 +305,8 @@ export const marks: { [index: string]: MarkSpec } = { }], toDOM(node: any) { return node.attrs.docref && node.attrs.title ? - ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution" }, node.attrs.title], ["br"]] : - ["a", { ...node.attrs }, 0]; + ["div", ["span", `"`], ["span", 0], ["span", `"`], ["br"], ["a", { ...node.attrs, class: "prosemirror-attribution", title: `${node.attrs.title}` }, node.attrs.title], ["br"]] : + ["a", { ...node.attrs, title: `${node.attrs.title}` }, 0]; } }, diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 9ce7acec8..38471a955 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -17,7 +17,7 @@ import { DragManager } from "./DragManager"; import { LinkManager } from "./LinkManager"; import { schema } from "./RichTextSchema"; import "./TooltipTextMenu.scss"; -import { Cast, NumCast } from '../../new_fields/Types'; +import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { updateBullets } from './ProsemirrorExampleTransfer'; import { DocumentDecorations } from '../views/DocumentDecorations'; const { toggleMark, setBlockType } = require("prosemirror-commands"); @@ -284,7 +284,7 @@ export class TooltipTextMenu { if (proto && docView) { proto.sourceContext = docView.props.ContainingCollectionDoc; } - let text = this.makeLink(linkDoc, ctrlKey ? "onRight" : "inTab"); + let text = this.makeLink(linkDoc, StrCast(linkDoc.anchor2.title), ctrlKey ? "onRight" : "inTab"); if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) { proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link } @@ -374,25 +374,20 @@ export class TooltipTextMenu { // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); // } - makeLink = (targetDoc: Doc, location: string): string => { - let target = Utils.prepend("/doc/" + targetDoc[Id]); + makeLink = (targetDoc: Doc, title: string, location: string): string => { + let link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + targetDoc[Id]), title: title, location: location }); + this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link). + addMark(this.view.state.selection.from, this.view.state.selection.to, link)); let node = this.view.state.selection.$from.nodeAfter; - let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: targetDoc[Id] }); - this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); - this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); - node = this.view.state.selection.$from.nodeAfter; - link = node && node.marks.find(m => m.type.name === "link"); - if (node) { - if (node.text) { - return node.text; - } + if (node && node.text) { + return node.text; } return ""; } deleteLink = () => { let node = this.view.state.selection.$from.nodeAfter; - let link = node && node.marks.find(m => m.type.name === "link"); + let link = node && node.marks.find(m => m.type === this.view.state.schema.marks.link); let href = link!.attrs.href; if (href) { if (href.indexOf(Utils.prepend("/doc/")) === 0) { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 0336440d5..55c211d1d 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -24,6 +24,7 @@ import { DocumentView } from "./nodes/DocumentView"; import { FieldView } from "./nodes/FieldView"; import { IconBox } from "./nodes/IconBox"; import React = require("react"); +import { DocumentType } from '../documents/DocumentTypes'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -112,7 +113,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } else { if (SelectionManager.SelectedDocuments().length > 0) { - SelectionManager.SelectedDocuments()[0].props.Document.customTitle = true; + SelectionManager.SelectedDocuments()[0].props.Document.customTitle = !this._title.startsWith("-"); let field = SelectionManager.SelectedDocuments()[0].props.Document[this._fieldKey]; if (typeof field === "number") { SelectionManager.SelectedDocuments().forEach(d => { @@ -174,6 +175,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> 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) { + let 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) diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 9ca9fc163..8f397e331 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -165,7 +165,7 @@ export default class KeyManager { } } break; - case "c": + case "t": PromiseValue(Cast(CurrentUserUtils.UserDocument.Create, Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv })); if (MainView.Instance.flyoutWidth === 240) { MainView.Instance.flyoutWidth = 0; diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index a91a2b69e..b21eb9c8f 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -3,34 +3,11 @@ import { Docs } from "../documents/Documents"; import { CurrentUserUtils } from "../../server/authentication/models/current_user_utils"; import * as ReactDOM from 'react-dom'; import * as React from 'react'; -import { Cast } from "../../new_fields/Types"; -import { Doc, DocListCastAsync } from "../../new_fields/Doc"; -import { List } from "../../new_fields/List"; import { DocServer } from "../DocServer"; import { AssignAllExtensions } from "../../extensions/General/Extensions"; AssignAllExtensions(); -let swapDocs = async () => { - let oldDoc = await Cast(CurrentUserUtils.UserDocument.linkManagerDoc, Doc); - // Docs.Prototypes.MainLinkDocument().allLinks = new List(); - if (oldDoc) { - let links = await DocListCastAsync(oldDoc.allLinks); - // if (links && DocListCast(links)) { - if (links && links.length) { - let data = await DocListCastAsync(Docs.Prototypes.MainLinkDocument().allLinks); - if (data) { - data.push(...links.filter(i => data!.indexOf(i) === -1)); - Docs.Prototypes.MainLinkDocument().allLinks = new List(data.filter((i, idx) => data!.indexOf(i) === idx)); - } - else { - Docs.Prototypes.MainLinkDocument().allLinks = new List(links); - } - } - CurrentUserUtils.UserDocument.linkManagerDoc = undefined; - } -}; - (async () => { const info = await CurrentUserUtils.loadCurrentUser(); DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email); @@ -38,7 +15,6 @@ let swapDocs = async () => { if (info.id !== "__guest__") { // a guest will not have an id registered await CurrentUserUtils.loadUserDocument(info); - await swapDocs(); } document.getElementById('root')!.addEventListener('wheel', event => { if (event.ctrlKey) { diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 21b135c49..a858a73c7 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -13,7 +13,7 @@ left: 250px; } -.mainView-container { +#mainView-container { width: 100%; height: 100%; position: absolute; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 773da05df..83dbb433b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -37,7 +37,6 @@ import { OverlayView } from './OverlayView'; import PDFMenu from './pdf/PDFMenu'; import { PreviewCursor } from './PreviewCursor'; import { Scripting } from '../util/Scripting'; -import { LinkManager } from '../util/LinkManager'; import { AudioBox } from './nodes/AudioBox'; @observer @@ -197,11 +196,6 @@ export class MainView extends React.Component { var dockingLayout = { content: [{ type: 'row', content: [CollectionDockingView.makeDocumentConfig(freeformDoc, freeformDoc, 600)] }] }; let mainDoc = Docs.Create.DockDocument([freeformDoc], JSON.stringify(dockingLayout), {}, id); if (this.userDoc && ((workspaces = Cast(this.userDoc.workspaces, Doc)) instanceof Doc)) { - if (!this.userDoc.linkManagerDoc) { - let linkManagerDoc = new Doc(); - linkManagerDoc.allLinks = new List([]); - this.userDoc.linkManagerDoc = linkManagerDoc; - } Doc.AddDocToList(workspaces, "data", mainDoc); mainDoc.title = `Workspace ${DocListCast(workspaces.data).length}`; } @@ -504,7 +498,7 @@ export class MainView extends React.Component { } render() { - return (

+ return (
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index a6ee9c2c6..238660de3 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -8,7 +8,6 @@ import { Cast, StrCast } from '../../../new_fields/Types'; import { DragLinkAsDocument } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { ContextMenu } from '../ContextMenu'; -import { MainView } from '../MainView'; import { LinkFollowBox } from './LinkFollowBox'; import './LinkMenu.scss'; import React = require("react"); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index abf26826c..a0f8523a2 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -842,6 +842,7 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F this._editorView && this._editorView.destroy(); } onPointerDown = (e: React.PointerEvent): void => { + FormattedTextBoxComment.textBox = this; let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY }); pos && (this._nodeClicked = this._editorView!.state.doc.nodeAt(pos.pos)); if (this.props.onClick && e.button === 0) { @@ -1018,11 +1019,13 @@ export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & F } render() { + trace(); let rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; - let interactive: "all" | "none" = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground - ? "none" : "all"; + let interactive = InkingControl.Instance.selectedTool || this.layoutDoc.isBackground; if (this.props.isSelected()) { FormattedTextBox._toolTipTextMenu!.updateFromDash(this._editorView!, undefined, this.props); + } else if (FormattedTextBoxComment.textBox === this) { + FormattedTextBoxComment.Hide(); } return (
{ let keep = e.target && (e.target as any).type === "checkbox" ? true : false; @@ -91,62 +93,67 @@ export class FormattedTextBoxComment { let state = view.state; // Don't do anything if the document/selection didn't change if (lastState && lastState.doc.eq(state.doc) && - lastState.selection.eq(state.selection)) return; + lastState.selection.eq(state.selection)) { + return; + } - if (!FormattedTextBoxComment.textBox || !FormattedTextBoxComment.textBox.props || !FormattedTextBoxComment.textBox.props.isSelected()) return; + const textBox = FormattedTextBoxComment.textBox; + if (!textBox || !textBox.props) { + return; + } let set = "none"; - if (FormattedTextBoxComment.textBox && state.selection.$from) { - let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark); + let nbef = 0; + // this section checks to see if the insertion point is over text entered by a different user. If so, it sets ths comment text to indicate the user and the modification date + if (state.selection.$from) { + nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark); let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark); - const spos = state.selection.$from.pos - nbef; - const epos = state.selection.$from.pos + naft; - let child = state.selection.$from.nodeBefore; - let mark = child && findOtherUserMark(child.marks); let noselection = view.state.selection.$from === view.state.selection.$to; + let child: any = null; + state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node)); + let mark = child && findOtherUserMark(child.marks); if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) { - FormattedTextBoxComment.SetState(this, mark.attrs.opened, spos, epos, mark); + FormattedTextBoxComment.SetState(FormattedTextBoxComment.textBox, mark.attrs.opened, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark); } - if (mark && child && nbef && naft) { - FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified; - // These are in screen coordinates - // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to); - let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef); - // The box in which the tooltip is positioned, to use as base - let box = (document.getElementById("main-div") as any).getBoundingClientRect(); - // Find a center-ish x position from the selection endpoints (when - // crossing lines, end may be more to the left) - let left = Math.max((start.left + end.left) / 2, start.left + 3); - FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px"; - FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px"; + if (mark && child && ((nbef && naft) || !noselection)) { + FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " date=" + (new Date(mark.attrs.modified * 5000)).toDateString(); set = ""; } } + // this checks if the selection is a hyperlink. If so, it displays the target doc's text for internal links, and the url of the target for external links. if (set === "none" && state.selection.$from) { - FormattedTextBoxComment.textBox = undefined; - let nbef = findStartOfMark(state.selection.$from, view, findLinkMark); + nbef = findStartOfMark(state.selection.$from, view, findLinkMark); let naft = findEndOfMark(state.selection.$from, view, findLinkMark); - let child = state.selection.$from.nodeBefore; + let child: any = null; + state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node)); let mark = child && findLinkMark(child.marks); if (mark && child && nbef && naft) { - FormattedTextBoxComment.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href); + FormattedTextBoxComment.tooltipText.textContent = "external => " + mark.attrs.href; if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) { let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - docTarget && DocServer.GetRefField(docTarget).then(linkDoc => - (linkDoc as Doc) && (FormattedTextBoxComment.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc).title))); + docTarget && DocServer.GetRefField(docTarget).then(linkDoc => { + if (linkDoc instanceof Doc) { + let target = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.props.Document) ? Cast(linkDoc.anchor2, Doc) : Cast(linkDoc.anchor1, Doc)); + let ext = (target && Doc.fieldExtensionDoc(target, "data")) || target; // try guessing that the target doc's data is in the 'data' field. probably need an 'overviewLayout' and then just display the target Document .... + let text = ext && StrCast(ext.text); + ext && (FormattedTextBoxComment.tooltipText.textContent = "=> " + (text || StrCast(ext.title))); + } + }); } - // These are in screen coordinates - // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to); - let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef); - // The box in which the tooltip is positioned, to use as base - let box = (document.getElementById("main-div") as any).getBoundingClientRect(); - // Find a center-ish x position from the selection endpoints (when - // crossing lines, end may be more to the left) - let left = Math.max((start.left + end.left) / 2, start.left + 3); - FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px"; - FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px"; set = ""; } } + if (set !== "none") { + // These are in screen coordinates + // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to); + let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef); + // The box in which the tooltip is positioned, to use as base + let box = (document.getElementById("mainView-container") as any).getBoundingClientRect(); + // Find a center-ish x position from the selection endpoints (when + // crossing lines, end may be more to the left) + let left = Math.max((start.left + end.left) / 2, start.left + 3); + FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px"; + FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px"; + } FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set); } diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 963205206..2d92c9581 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -48,6 +48,7 @@ } .pdfViewer-text { .textLayer { + will-change: transform; span { user-select: none; } @@ -59,6 +60,7 @@ pointer-events: all; .pdfViewer-text { .textLayer { + will-change: transform; span { user-select: text; } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3baa6eb09..8e0515f8a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, runInAction, reaction, IReactionDisposer, trace } from 'mobx'; +import { action, observable, runInAction, reaction, IReactionDisposer, trace, untracked, computed } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; @@ -32,7 +32,7 @@ export class PDFBox extends DocAnnotatableComponent private _valueValue: string = ""; private _scriptValue: string = ""; private _searchString: string = ""; - private _initialScale: number | undefined; // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not. Getting bigger after startup won't make it automatically be live. + private _initialScale: number = 0; // the initial scale of the PDF when first rendered which determines whether the document will be live on startup or not. Getting bigger after startup won't make it automatically be live. private _everActive = false; // has this box ever had its contents activated -- if so, stop drawing the overlay title private _pdfViewer: PDFViewer | undefined; private _searchRef = React.createRef(); @@ -46,6 +46,11 @@ export class PDFBox extends DocAnnotatableComponent @observable private _pdf: Opt; @observable private _pageControls = false; + constructor(props: any) { + super(props); + this._initialScale = this.props.ScreenToLocalTransform().Scale; + } + componentWillUnmount() { this._selectReactionDisposer && this._selectReactionDisposer(); } @@ -190,39 +195,46 @@ export class PDFBox extends DocAnnotatableComponent ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } - render() { + @computed get renderTitleBox() { + let classname = "pdfBox-cont" + (this.active() ? "-interactive" : ""); + return
+
+ {` ${this.props.Document.title}`} +
+
; + } + + @computed get renderPdfView() { trace(); const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (this.active() ? "-interactive" : ""); - let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf; - if (this._initialScale === undefined) this._initialScale = this.props.ScreenToLocalTransform().Scale; + return
{ + let hit = document.elementFromPoint(e.clientX, e.clientY); + if (hit && hit.localName === "span" && this.props.isSelected()) { // drag selecting text stops propagation + e.button === 0 && e.stopPropagation(); + } + }}> + + {this.settingsPanel()} +
; + } + + render() { + const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null); if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true; - return (!this.extensionDoc || noPdf || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ? -
-
- {` ${this.props.Document.title}`} -
-
: -
{ - let hit = document.elementFromPoint(e.clientX, e.clientY); - if (hit && hit.localName === "span" && this.props.isSelected()) { // drag selecting text stops propagation - e.button === 0 && e.stopPropagation(); - } - }}> - - {this.settingsPanel()} -
); + return !pdfUrl || !this._pdf || !this.extensionDoc || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ? + this.renderTitleBox : this.renderPdfView; } } \ No newline at end of file diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 2d8f47666..936af9ab8 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -52,11 +52,7 @@ class RegionAnnotation extends React.Component { this._brushDisposer = reaction( () => FieldValue(Cast(this.props.document.group, Doc)) && Doc.isBrushedHighlightedDegree(FieldValue(Cast(this.props.document.group, Doc))!), - (brushed) => { - if (brushed !== undefined) { - runInAction(() => this._brushed = brushed !== 0); - } - } + brushed => brushed !== undefined && runInAction(() => this._brushed = brushed !== 0) ); } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 060ba8613..0cb671156 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -199,7 +199,7 @@ export class PDFViewer extends DocAnnotatableComponent this.extensionDoc && DocListCast(this.extensionDoc.annotations), - annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), + annotations => annotations && annotations.length && (this._annotations = annotations), { fireImmediately: true }); this._filterReactionDisposer = reaction( @@ -297,18 +297,6 @@ export class PDFViewer extends DocAnnotatableComponent { - if (removeOldAnnotations) { - this._annotations = annotations; - } - else { - this._annotations.push(...annotations); - this._annotations = new Array(...this._annotations); - } - } - @action prevAnnotation = () => { this.Index = Math.max(this.Index - 1, 0); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 6aad4a6be..3bf1129b5 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -669,8 +669,8 @@ export namespace Doc { return doc; } - export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; } + export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) { return Doc.AreProtosEqual(anchorDoc, Cast(linkDoc.anchor1, Doc) as Doc) ? "layoutKey1" : "layoutKey2"; } export function linkFollowUnhighlight() { Doc.UnhighlightAll(); document.removeEventListener("pointerdown", linkFollowUnhighlight); diff --git a/src/server/authentication/models/current_user_utils.ts b/src/server/authentication/models/current_user_utils.ts index 56ea5bfe1..833e44bf6 100644 --- a/src/server/authentication/models/current_user_utils.ts +++ b/src/server/authentication/models/current_user_utils.ts @@ -15,7 +15,6 @@ import { RouteStore } from "../../RouteStore"; import { InkingControl } from "../../../client/views/InkingControl"; import { DragManager } from "../../../client/util/DragManager"; import { nullAudio } from "../../../new_fields/URLField"; -import { LinkManager } from "../../../client/util/LinkManager"; export class CurrentUserUtils { private static curr_id: string; -- cgit v1.2.3-70-g09d2 From 63129c244fc2b9a5c60e6a94b864895641b86f57 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 20 Nov 2019 16:59:40 -0500 Subject: lots of changes to make rendering more efficient (fewer mobx invalidations). made selection and collecitonviews use an observableMap that works now. --- src/client/util/SelectionManager.ts | 41 ++-- src/client/views/DocComponent.tsx | 14 +- src/client/views/DocumentButtonBar.scss | 106 +++------ src/client/views/DocumentButtonBar.tsx | 264 ++++++++------------- src/client/views/Main.scss | 6 +- src/client/views/MainView.scss | 1 - src/client/views/TemplateMenu.scss | 50 ++++ src/client/views/TemplateMenu.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 14 +- .../views/collections/CollectionTreeView.tsx | 6 +- src/client/views/collections/CollectionView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 87 ++++--- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 5 +- .../views/nodes/ContentFittingDocumentView.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 27 ++- src/client/views/nodes/FieldView.tsx | 4 +- src/client/views/nodes/FormattedTextBox.tsx | 10 +- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 7 +- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 14 +- src/new_fields/Doc.ts | 16 +- 25 files changed, 326 insertions(+), 364 deletions(-) create mode 100644 src/client/views/TemplateMenu.scss (limited to 'src/client/views/MainView.scss') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index ca61f9014..e01216e0f 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,45 +1,44 @@ -import { observable, action, runInAction, IReactionDisposer, reaction, autorun } from "mobx"; -import { Doc, Opt } from "../../new_fields/Doc"; +import { observable, action, runInAction, ObservableMap } from "mobx"; +import { Doc } from "../../new_fields/Doc"; import { DocumentView } from "../views/nodes/DocumentView"; -import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; -import { NumCast, StrCast } from "../../new_fields/Types"; +import { computedFn } from "mobx-utils"; export namespace SelectionManager { class Manager { @observable IsDragging: boolean = false; - @observable SelectedDocuments: Array = []; + SelectedDocuments: ObservableMap = new ObservableMap(); @action SelectDoc(docView: DocumentView, ctrlPressed: boolean): void { // if doc is not in SelectedDocuments, add it - if (manager.SelectedDocuments.indexOf(docView) === -1) { + if (!manager.SelectedDocuments.get(docView)) { if (!ctrlPressed) { this.DeselectAll(); } - manager.SelectedDocuments.push(docView); + manager.SelectedDocuments.set(docView, true); // console.log(manager.SelectedDocuments); docView.props.whenActiveChanged(true); - } else if (!ctrlPressed && manager.SelectedDocuments.length > 1) { - manager.SelectedDocuments.map(dv => dv !== docView && dv.props.whenActiveChanged(false)); - manager.SelectedDocuments = [docView]; + } else if (!ctrlPressed && Array.from(manager.SelectedDocuments.entries()).length > 1) { + Array.from(manager.SelectedDocuments.keys()).map(dv => dv !== docView && dv.props.whenActiveChanged(false)); + manager.SelectedDocuments.clear(); + manager.SelectedDocuments.set(docView, true); } } @action DeselectDoc(docView: DocumentView): void { - let ind = manager.SelectedDocuments.indexOf(docView); - if (ind !== -1) { - manager.SelectedDocuments.splice(ind, 1); + if (manager.SelectedDocuments.get(docView)) { + manager.SelectedDocuments.delete(docView); docView.props.whenActiveChanged(false); } } @action DeselectAll(): void { - manager.SelectedDocuments.map(dv => dv.props.whenActiveChanged(false)); - manager.SelectedDocuments = []; + Array.from(manager.SelectedDocuments.keys()).map(dv => dv.props.whenActiveChanged(false)); + manager.SelectedDocuments.clear(); } } @@ -52,14 +51,18 @@ export namespace SelectionManager { manager.SelectDoc(docView, ctrlPressed); } - export function IsSelected(doc: DocumentView): boolean { - return manager.SelectedDocuments.indexOf(doc) !== -1; + export function IsSelected(doc: DocumentView, outsideReaction?: boolean): boolean { + return outsideReaction ? + manager.SelectedDocuments.get(doc) ? true : false : + computedFn(function isSelected(doc: DocumentView) { + return manager.SelectedDocuments.get(doc) ? true : false; + })(doc); } export function DeselectAll(except?: Doc): void { let found: DocumentView | undefined = undefined; if (except) { - for (const view of manager.SelectedDocuments) { + for (const view of Array.from(manager.SelectedDocuments.keys())) { if (view.props.Document === except) found = view; } } @@ -72,6 +75,6 @@ export namespace SelectionManager { export function GetIsDragging() { return manager.IsDragging; } export function SelectedDocuments(): Array { - return manager.SelectedDocuments.slice(); + return Array.from(manager.SelectedDocuments.keys()); } } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 286a77f81..1bd1006a8 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -27,7 +27,7 @@ interface DocExtendableProps { Document: Doc; DataDoc?: Doc; fieldKey: string; - isSelected: () => boolean; + isSelected: (outsideReaction?: boolean) => boolean; renderDepth: number; } export function DocExtendableComponent

(schemaCtor: (doc: Doc) => T) { @@ -37,7 +37,7 @@ export function DocExtendableComponent

(schemaCt @computed get layoutDoc() { return Doc.Layout(this.props.Document); } @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - active = () => !this.props.Document.isBackground && (this.props.Document.forceActive || this.props.isSelected() || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools + active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools } return Component; } @@ -49,7 +49,7 @@ interface DocAnnotatableProps { DataDoc?: Doc; fieldKey: string; whenActiveChanged: (isActive: boolean) => void; - isSelected: () => boolean; + isSelected: (outsideReaction?: boolean) => boolean; renderDepth: number; } export function DocAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { @@ -82,10 +82,10 @@ export function DocAnnotatableComponent

(schema } whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); - active = () => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) && - (this.props.Document.forceActive || this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0) ? true : false) - annotationsActive = () => (InkingControl.Instance.selectedTool !== InkTool.None || - (this.props.Document.forceActive || this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0) ? true : false) + active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) && + (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) + annotationsActive = (outsideReaction?: boolean) => (InkingControl.Instance.selectedTool !== InkTool.None || + (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) } return Component; } \ No newline at end of file diff --git a/src/client/views/DocumentButtonBar.scss b/src/client/views/DocumentButtonBar.scss index 8cd419bbe..db6bf2ba0 100644 --- a/src/client/views/DocumentButtonBar.scss +++ b/src/client/views/DocumentButtonBar.scss @@ -2,54 +2,23 @@ $linkGap : 3px; -.linkFlyout { +.documentButtonBar-linkFlyout { grid-column: 2/4; } -.linkButton-empty:hover { +.documentButtonBar-linkButton-empty:hover { background: $main-accent; transform: scale(1.05); cursor: pointer; } -.linkButton-nonempty:hover { +.documentButtonBar-linkButton-nonempty:hover { background: $main-accent; transform: scale(1.05); cursor: pointer; } - -.documentButtonBar { - margin-top: $linkGap; - grid-column: 1/4; - width: max-content; - height: auto; - display: flex; - flex-direction: row; -} - -.linkButtonWrapper { - pointer-events: auto; - padding-right: 5px; - width: 25px; -} - -.linkButton-linker { - height: 20px; - width: 20px; - text-align: center; - border-radius: 50%; - pointer-events: auto; - color: $dark-color; - border: $dark-color 1px solid; -} - -.linkButton-linker:hover { - cursor: pointer; - transform: scale(1.05); -} - -.linkButton-empty, -.linkButton-nonempty { +.documentButtonBar-linkButton-empty, +.documentButtonBar-linkButton-nonempty { height: 20px; width: 20px; border-radius: 50%; @@ -73,57 +42,38 @@ $linkGap : 3px; } } -.templating-menu { - position: absolute; - pointer-events: auto; - text-transform: uppercase; - letter-spacing: 2px; - font-size: 75%; - transition: transform 0.2s; - text-align: center; +.documentButtonBar { + margin-top: $linkGap; + grid-column: 1/4; + width: max-content; + height: auto; display: flex; - justify-content: center; - align-items: center; + flex-direction: row; } -.templating-button, -.docDecs-tagButton { - width: 20px; +.documentButtonBar-button { + pointer-events: auto; + padding-right: 5px; + width: 25px; +} + +.documentButtonBar-linker { height: 20px; - border-radius: 50%; - opacity: 0.9; - font-size: 14; - background-color: $dark-color; - color: $light-color; + width: 20px; text-align: center; - cursor: pointer; - - &:hover { - background: $main-accent; - transform: scale(1.05); - } + border-radius: 50%; + pointer-events: auto; + color: $dark-color; + border: $dark-color 1px solid; + transition: 0.2s ease all; } -#template-list { - position: absolute; - top: 25px; - left: 0px; - width: max-content; - font-family: $sans-serif; - font-size: 12px; - background-color: $light-color-secondary; - padding: 2px 12px; - list-style: none; - - .templateToggle, .chromeToggle { - text-align: left; - } - - input { - margin-right: 10px; - } +.documentButtonBar-linker:hover { + cursor: pointer; + transform: scale(1.05); } + @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } } @-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } \ No newline at end of file diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index c7ee413c9..1fefc70f1 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -1,7 +1,7 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faArrowAltCircleDown, faArrowAltCircleUp, faCheckCircle, faCloudUploadAlt, faLink, faShare, faStopCircle, faSyncAlt, faTag, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable, runInAction } from "mobx"; +import { action, observable, runInAction, computed } from "mobx"; import { observer } from "mobx-react"; import { Doc } from "../../new_fields/Doc"; import { RichTextField } from '../../new_fields/RichTextField'; @@ -42,52 +42,53 @@ const fetch: IconProp = "sync-alt"; @observer export class DocumentButtonBar extends React.Component<{ views: DocumentView[], stack?: any }, {}> { private _linkButton = React.createRef(); - private _aliasButton = React.createRef(); - private _tooltipoff = React.createRef(); - private _textDoc?: Doc; + private _downX = 0; + private _downY = 0; + private _pullAnimating = false; + private _pushAnimating = false; + private _pullColorAnimating = false; + + @observable private pushIcon: IconProp = "arrow-alt-circle-up"; + @observable private pullIcon: IconProp = "arrow-alt-circle-down"; + @observable private pullColor: string = "white"; + @observable private isAnimatingFetch = false; + @observable private openHover = false; + public static Instance: DocumentButtonBar; + public static hasPushedHack = false; + public static hasPulledHack = false; constructor(props: { views: DocumentView[] }) { super(props); DocumentButtonBar.Instance = this; } - @observable public pushIcon: IconProp = "arrow-alt-circle-up"; - @observable public pullIcon: IconProp = "arrow-alt-circle-down"; - @observable public pullColor: string = "white"; - @observable public isAnimatingFetch = false; - @observable public openHover = false; - public pullColorAnimating = false; - - private pullAnimating = false; - private pushAnimating = false; - public startPullOutcome = action((success: boolean) => { - if (!this.pullAnimating) { - this.pullAnimating = true; + if (!this._pullAnimating) { + this._pullAnimating = true; this.pullIcon = success ? "check-circle" : "stop-circle"; setTimeout(() => runInAction(() => { this.pullIcon = "arrow-alt-circle-down"; - this.pullAnimating = false; + this._pullAnimating = false; }), 1000); } }); public startPushOutcome = action((success: boolean) => { - if (!this.pushAnimating) { - this.pushAnimating = true; + if (!this._pushAnimating) { + this._pushAnimating = true; this.pushIcon = success ? "check-circle" : "stop-circle"; setTimeout(() => runInAction(() => { this.pushIcon = "arrow-alt-circle-up"; - this.pushAnimating = false; + this._pushAnimating = false; }), 1000); } }); public setPullState = action((unchanged: boolean) => { this.isAnimatingFetch = false; - if (!this.pullColorAnimating) { - this.pullColorAnimating = true; + if (!this._pullColorAnimating) { + this._pullColorAnimating = true; this.pullColor = unchanged ? "lawngreen" : "red"; setTimeout(this.clearPullColor, 1000); } @@ -95,33 +96,17 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[], private clearPullColor = action(() => { this.pullColor = "white"; - this.pullColorAnimating = false; + this._pullColorAnimating = false; }); - onLinkerButtonDown = (e: React.PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - document.removeEventListener("pointermove", this.onLinkerButtonMoved); - document.addEventListener("pointermove", this.onLinkerButtonMoved); - document.removeEventListener("pointerup", this.onLinkerButtonUp); - document.addEventListener("pointerup", this.onLinkerButtonUp); - } - - - onLinkerButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onLinkerButtonMoved); - document.removeEventListener("pointerup", this.onLinkerButtonUp); - e.stopPropagation(); - } - @action - onLinkerButtonMoved = (e: PointerEvent): void => { - if (this._linkButton.current !== null) { - document.removeEventListener("pointermove", this.onLinkerButtonMoved); + onLinkButtonMoved = (e: PointerEvent): void => { + if (this._linkButton.current !== null && (Math.abs(e.clientX - this._downX) > 3 || Math.abs(e.clientY - this._downY) > 3)) { + document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); let docView = this.props.views[0]; - let container = docView.props.ContainingCollectionDoc ? docView.props.ContainingCollectionDoc.proto : undefined; + let container = docView.props.ContainingCollectionDoc?.proto; let dragData = new DragManager.LinkDragData(docView.props.Document, container ? [container] : []); let linkDrag = UndoManager.StartBatch("Drag Link"); DragManager.StartLinkDrag(this._linkButton.current, dragData, e.pageX, e.pageY, { @@ -150,151 +135,106 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[], onLinkButtonDown = (e: React.PointerEvent): void => { - e.stopPropagation(); - e.preventDefault(); - document.removeEventListener("pointermove", this.onLinkerButtonMoved); - document.addEventListener("pointermove", this.onLinkerButtonMoved); + this._downX = e.clientX; + this._downY = e.clientY; + document.removeEventListener("pointermove", this.onLinkButtonMoved); + document.addEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); document.addEventListener("pointerup", this.onLinkButtonUp); + e.stopPropagation(); } onLinkButtonUp = (e: PointerEvent): void => { - document.removeEventListener("pointermove", this.onLinkerButtonMoved); + document.removeEventListener("pointermove", this.onLinkButtonMoved); document.removeEventListener("pointerup", this.onLinkButtonUp); e.stopPropagation(); } - private get targetDoc() { - return this.props.views[0].props.Document; - } - - considerGoogleDocsPush = () => { - let canPush = this.targetDoc.data && this.targetDoc.data instanceof RichTextField; - if (!canPush) return (null); - let published = Doc.GetProto(this.targetDoc)[GoogleRef] !== undefined; - let icon: IconProp = published ? (this.pushIcon as any) : cloud; - return ( -

-
{ - DocumentButtonBar.hasPushedHack = false; - this.targetDoc[Pushes] = NumCast(this.targetDoc[Pushes]) + 1; - }}> - -
-
- ); + @computed + get considerGoogleDocsPush() { + let targetDoc = this.props.views[0].props.Document; + let published = Doc.GetProto(targetDoc)[GoogleRef] !== undefined; + return
{ + DocumentButtonBar.hasPushedHack = false; + targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1; + }}> + +
; } - considerGoogleDocsPull = () => { - let canPull = this.targetDoc.data && this.targetDoc.data instanceof RichTextField; - let dataDoc = Doc.GetProto(this.targetDoc); - if (!canPull || !dataDoc[GoogleRef]) return (null); - let icon = dataDoc.unchanged === false ? (this.pullIcon as any) : fetch; - icon = this.openHover ? "share" : icon; + @computed + get considerGoogleDocsPull() { + let targetDoc = this.props.views[0].props.Document; + let dataDoc = Doc.GetProto(targetDoc); let animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none"; - let title = `${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`; - return ( -
-
e.altKey && runInAction(() => this.openHover = true)} - onPointerLeave={() => runInAction(() => this.openHover = false)} - onClick={e => { - if (e.altKey) { - e.preventDefault(); - window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`); - } else { - this.clearPullColor(); - DocumentButtonBar.hasPulledHack = false; - this.targetDoc[Pulls] = NumCast(this.targetDoc[Pulls]) + 1; - dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true); - } - }}> - -
-
- ); + return !dataDoc[GoogleRef] ? (null) :
e.altKey && runInAction(() => this.openHover = true)} + onPointerLeave={action(() => this.openHover = false)} + onClick={e => { + if (e.altKey) { + e.preventDefault(); + window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`); + } else { + this.clearPullColor(); + DocumentButtonBar.hasPulledHack = false; + targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1; + dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true); + } + }}> + +
; } - public static hasPushedHack = false; - public static hasPulledHack = false; - - considerTooltip = () => { - let thisDoc = this.props.views[0].props.Document; - let isTextDoc = thisDoc.data && thisDoc.data instanceof RichTextField; - if (!isTextDoc) return null; - this._textDoc = thisDoc; - return ( -
-
- {/* */} + @computed + get linkButton() { + let linkCount = LinkManager.Instance.getAllRelatedLinks(this.props.views[0].props.Document).length; + return
+ }> +
+ {linkCount ? linkCount : }
-
- - ); + +
; } - onTooltipOff = (e: React.PointerEvent): void => { - e.stopPropagation(); - if (this._textDoc) { - if (this._tooltipoff.current) { - if (this._tooltipoff.current.title === "Hide Tooltip") { - this._tooltipoff.current.title = "Show Tooltip"; - this._textDoc.tooltip = "hi"; - } - else { - this._tooltipoff.current.title = "Hide Tooltip"; - } - } - } + @computed + get contextButton() { + return { + where === "onRight" ? CollectionDockingView.AddRightSplit(doc, data) : + this.props.stack ? CollectionDockingView.Instance.AddTab(this.props.stack, doc, data) : + this.props.views[0].props.addDocTab(doc, data, "onRight"); + return true; + }} />; } render() { - let linkButton = null; - if (this.props.views.length > 0) { - let selFirst = this.props.views[0]; - - let linkCount = LinkManager.Instance.getAllRelatedLinks(selFirst.props.Document).length; - linkButton = }> -
- {linkCount ? linkCount : } -
-
; - } - let templates: Map = new Map(); Array.from(Object.values(Templates.TemplateList)).map(template => templates.set(template, this.props.views.reduce((checked, doc) => checked || doc.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean))); - return (
-
-
{linkButton}
+ let isText = this.props.views[0].props.Document.data instanceof RichTextField; // bcz: Todo - can't assume layout is using the 'data' field. need to add fieldKey to DocumentView + let considerPull = isText && this.considerGoogleDocsPull; + let considerPush = isText && this.considerGoogleDocsPush; + return
+
+ {this.linkButton}
-
+
- {this.considerGoogleDocsPush()} - {this.considerGoogleDocsPull()} - { - where === "onRight" ? CollectionDockingView.AddRightSplit(doc, data) : this.props.stack ? CollectionDockingView.Instance.AddTab(this.props.stack, doc, data) : this.props.views[0].props.addDocTab(doc, data, "onRight"); - return true; - }} /> - {/* {this.considerTooltip()} */} -
- ); +
+ {this.considerGoogleDocsPush} +
+
+ {this.considerGoogleDocsPull} +
+ {this.contextButton} +
; } } \ No newline at end of file diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss index 465527468..3b66160fb 100644 --- a/src/client/views/Main.scss +++ b/src/client/views/Main.scss @@ -5,7 +5,7 @@ html, body { width: 100%; height: 100%; - overflow: auto; + overflow: hidden; font-family: $sans-serif; margin: 0; position: absolute; @@ -65,10 +65,6 @@ button:hover { cursor: pointer; } -#root { - overflow: visible; -} - .svg-inline--fa { vertical-align: unset; } \ No newline at end of file diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index b3fff081e..0ee30f117 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -20,7 +20,6 @@ position: absolute; top: 0; left: 0; - overflow: auto; z-index: 1; } diff --git a/src/client/views/TemplateMenu.scss b/src/client/views/TemplateMenu.scss new file mode 100644 index 000000000..186d3ab0d --- /dev/null +++ b/src/client/views/TemplateMenu.scss @@ -0,0 +1,50 @@ +@import "globalCssVariables"; +.templating-menu { + position: absolute; + pointer-events: auto; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + transition: transform 0.2s; + text-align: center; + display: flex; + justify-content: center; + align-items: center; +} + +.templating-button { + width: 20px; + height: 20px; + border-radius: 50%; + opacity: 0.9; + font-size: 14; + background-color: $dark-color; + color: $light-color; + text-align: center; + cursor: pointer; + + &:hover { + background: $main-accent; + transform: scale(1.05); + } +} + +.template-list { + position: absolute; + top: 25px; + left: 0px; + width: max-content; + font-family: $sans-serif; + font-size: 12px; + background-color: $light-color-secondary; + padding: 2px 12px; + list-style: none; + + .templateToggle, .chromeToggle { + text-align: left; + } + + input { + margin-right: 10px; + } +} \ No newline at end of file diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 1df5f49c4..c65b338b4 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -4,7 +4,7 @@ import { DocumentManager } from "../util/DocumentManager"; import { DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; -import './DocumentDecorations.scss'; +import './TemplateMenu.scss'; import { DocumentView } from "./nodes/DocumentView"; import { Template, Templates } from "./Templates"; import React = require("react"); @@ -175,7 +175,7 @@ export class TemplateMenu extends React.Component { return (
this.toggleTemplateActivity()}>+
-
    +
      {templateMenu} {}
    diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 3ff99b9f4..75d92105b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -448,7 +448,7 @@ export class CollectionDockingView extends React.Component [doc.title, Doc.IsBrushedDegree(doc)], () => { tab.titleElement[0].textContent = doc.title, { fireImmediately: true }; - tab.titleElement[0].style.outline = `${["transparent", "white", "white"][Doc.IsBrushedDegree(doc)]} ${["none", "dashed", "solid"][Doc.IsBrushedDegree(doc)]} 1px`; + tab.titleElement[0].style.outline = `${["transparent", "white", "white"][Doc.IsBrushedDegreeUnmemoized(doc)]} ${["none", "dashed", "solid"][Doc.IsBrushedDegreeUnmemoized(doc)]} 1px`; }); //TODO why can't this just be doc instead of the id? tab.titleElement[0].DashDocId = tab.contentItem.config.props.documentId; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index ebd47fd19..65856cad3 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -112,7 +112,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { onPointerDown = (e: React.PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { - if (this.props.isSelected()) e.stopPropagation(); + if (this.props.isSelected(true)) e.stopPropagation(); else { this.props.select(false); } @@ -201,7 +201,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { render() { return
    -
    this.props.active() && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}> +
    this.props.active(true) && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}> {this.schemaTable}
    {this.dividerDragger} @@ -225,11 +225,11 @@ export interface SchemaTableProps { addDocument: (document: Doc) => boolean; moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; - active: () => boolean; + active: (outsideReaction: boolean) => boolean; onDrop: (e: React.DragEvent, options: DocumentOptions, completed?: (() => void) | undefined) => void; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; - isSelected: () => boolean; + isSelected: (outsideReaction?: boolean) => boolean; isFocused: (document: Doc) => boolean; setFocused: (document: Doc) => void; setPreviewDoc: (document: Doc) => void; @@ -442,14 +442,14 @@ export class SchemaTable extends React.Component { onPointerDown = (e: React.PointerEvent): void => { this.props.setFocused(this.props.Document); - if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey && this.props.isSelected()) { + if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey && this.props.isSelected(true)) { e.stopPropagation(); } } @action onKeyDown = (e: KeyboardEvent): void => { - if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected()) { + if (!this._cellIsEditing && !this._headerIsEditing && this.props.isFocused(this.props.Document)) {// && this.props.isSelected(true)) { let direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : ""; this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col); @@ -778,7 +778,7 @@ export class SchemaTable extends React.Component { } render() { - return
    this.props.active() && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} > + return
    this.props.active(true) && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} > {this.reactTable}
    this.createRow()}>+ new
    ; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 0e3f0d1a9..8b993820b 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -49,7 +49,7 @@ export interface TreeViewProps { outerXf: () => { translateX: number, translateY: number }; treeViewId: string; parentKey: string; - active: () => boolean; + active: (outsideReaction?: boolean) => boolean; showHeaderFields: () => boolean; preventTreeViewOpen: boolean; renderedIds: string[]; @@ -130,7 +130,7 @@ class TreeView extends React.Component { onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); onPointerEnter = (e: React.PointerEvent): void => { - this.props.active() && Doc.BrushDoc(this.dataDoc); + this.props.active(true) && Doc.BrushDoc(this.dataDoc); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { this._header!.current!.className = "treeViewItem-header"; document.addEventListener("pointermove", this.onDragMove, true); @@ -412,7 +412,7 @@ class TreeView extends React.Component { pinToPres: (document: Doc) => void, screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, - active: () => boolean, + active: (outsideReaction?: boolean) => boolean, panelWidth: () => number, renderDepth: number, showHeaderFields: () => boolean, diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 347fa7d0d..8387e95df 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -113,7 +113,7 @@ export class CollectionView extends Touchable { // bcz: Argh? What's the height of the collection chromes?? chromeHeight = () => (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0); - active = () => this.props.isSelected() || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0; + active = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0; whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2a63a3074..256ceb8f8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,7 +1,7 @@ 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 } from "@fortawesome/free-solid-svg-icons"; -import { action, computed, observable, trace } from "mobx"; +import { action, computed, observable, trace, ObservableMap, untracked, reaction, runInAction, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../new_fields/Doc"; import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas"; @@ -39,6 +39,7 @@ import "./CollectionFreeFormView.scss"; import MarqueeOptionsMenu from "./MarqueeOptionsMenu"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); +import { computedFn, keepAlive } from "mobx-utils"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -68,11 +69,16 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _lastY: number = 0; private _clusterDistance: number = 75; private _hitCluster = false; + private _layoutComputeReaction: IReactionDisposer | undefined; + private _layoutPoolData = new ObservableMap(); + + public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive + @observable _layoutElements: ViewDefResult[] = []; @observable _clusterSets: (Doc[])[] = []; @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return this.props.ContentScaling && this.fitToContent && !this.isAnnotationOverlay ? this.props.ContentScaling() : 1; } - @computed get contentBounds() { return aggregateBounds(this.elements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); } + @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!)); } @computed get nativeWidth() { return this.Document.fitToContent ? 0 : this.Document.nativeWidth || 0; } @computed get nativeHeight() { return this.fitToContent ? 0 : this.Document.nativeHeight || 0; } private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } @@ -271,7 +277,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { onPointerDown = (e: React.PointerEvent): void => { if (e.nativeEvent.cancelBubble) return; this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false; - if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active()) { + if (e.button === 0 && !e.shiftKey && !e.altKey && !e.ctrlKey && this.props.active(true)) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -359,7 +365,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { if (InteractionUtils.IsType(e, InteractionUtils.TOUCH)) { - if (this.props.active()) { + if (this.props.active(true)) { e.stopPropagation(); } return; @@ -493,7 +499,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } - else if (this.props.active()) { + else if (this.props.active(true)) { e.stopPropagation(); this.zoom(e.clientX, e.clientY, e.deltaY); } @@ -645,30 +651,14 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - lookupLayout = (doc: Doc, dataDoc?: Doc) => { - let data: any = undefined; - let computedElementData: { map: Map<{ layout: Doc, data?: Doc | undefined }, any>, elements: ViewDefResult[] }; - switch (this.Document.freeformLayoutEngine) { - case "pivot": computedElementData = this.doPivotLayout; break; - default: computedElementData = this.doFreeformLayout; break; - } - computedElementData.map.forEach((value: any, key: { layout: Doc, data?: Doc }) => { - if (key.layout === doc && key.data === dataDoc) { - data = value; - } - }); - return data && { x: data.x, y: data.y, z: data.z, width: data.width, height: data.height, transition: data.transition }; - } + childDataProvider = computedFn((doc: Doc) => this._layoutPoolData.get(doc[Id])); - @computed get doPivotLayout() { return computePivotLayout(this.props.Document, this.childDocs, this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)), this.viewDefsToJSX); } - @computed get doFreeformLayout() { - let layoutPoolData: Map<{ layout: Doc, data?: Doc }, any> = new Map(); let layoutDocs = this.childLayoutPairs.map(pair => pair.layout); const initResult = this.Document.arrangeInit && this.Document.arrangeInit.script.run({ docs: layoutDocs, collection: this.Document }, console.log); let state = initResult && initResult.success ? initResult.result.scriptState : undefined; @@ -677,32 +667,41 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => { const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: layoutDocs, state }); state = pos.state === undefined ? state : pos.state; - layoutPoolData.set(pair, pos); + let data = this._layoutPoolData.get(pair.layout[Id]); + if (!data || pos.x !== data.x || pos.y !== data.y || pos.z !== data.z || pos.width !== data.width || pos.height !== data.height || pos.transition !== data.transition) { + runInAction(() => this._layoutPoolData.set(pair.layout[Id], pos)); + } }); - return { map: layoutPoolData, elements: elements }; + return { elements: elements }; } - @computed get doLayoutComputation() { - let computedElementData: { map: Map<{ layout: Doc, data?: Doc | undefined }, any>, elements: ViewDefResult[] }; + let computedElementData: { elements: ViewDefResult[] }; switch (this.Document.freeformLayoutEngine) { case "pivot": computedElementData = this.doPivotLayout; break; default: computedElementData = this.doFreeformLayout; break; } this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair => computedElementData.elements.push({ - ele: , - bounds: this.lookupLayout(pair.layout, pair.data) + bounds: this.childDataProvider(pair.layout) })); return computedElementData; } - @computed.struct get elements() { return this.doLayoutComputation.elements; } - @computed.struct get views() { return this.elements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } - @computed.struct get overlayViews() { return this.elements.filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele); } + componentDidMount() { + this._layoutComputeReaction = reaction(() => this.doLayoutComputation, + action((computation: { elements: ViewDefResult[] }) => computation && (this._layoutElements = computation.elements)), + { fireImmediately: true }); + } + componentWillUnmount() { + this._layoutComputeReaction && this._layoutComputeReaction(); + } + @computed.struct get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } + elementFunc = () => this._layoutElements; @action onCursorMove = (e: React.PointerEvent) => { @@ -846,15 +845,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { render() { trace(); // update the actual dimensions of the collection so that they can inquired (e.g., by a minimap) - this.Document.fitX = this.contentBounds && this.contentBounds.x; - this.Document.fitY = this.contentBounds && this.contentBounds.y; - this.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x); - this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y); + // this.Document.fitX = this.contentBounds && this.contentBounds.x; + // this.Document.fitY = this.contentBounds && this.contentBounds.y; + // this.Document.fitW = this.contentBounds && (this.contentBounds.r - this.contentBounds.x); + // this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y); // if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey. // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document return !this.extensionDoc ? (null) : -
    @@ -863,11 +862,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { {this.children} - {this.overlayViews} +
    ; } } +interface CollectionFreeFormOverlayViewProps { + elements: () => ViewDefResult[]; +} + +@observer +class CollectionFreeFormOverlayView extends React.Component{ + @computed.struct get overlayViews() { return this.props.elements().filter(ele => ele.bounds && ele.bounds.z).map(ele => ele.ele); } + render() { + return this.overlayViews; + } +} + interface CollectionFreeFormViewPannableContentsProps { centeringShiftX: () => number; centeringShiftY: () => number; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 1066f4f8d..5ed3fecb5 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -191,7 +191,7 @@ export class MarqueeView extends React.Component { - if (!this.props.active()) this.props.selectDocuments([this.props.Document], []); + if (!this.props.active(true)) this.props.selectDocuments([this.props.Document], []); if (this._visible) { let mselect = this.marqueeSelect(); if (!e.shiftKey) { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index d0e1d1922..0badbd3fe 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -12,7 +12,7 @@ import React = require("react"); import { PositionDocument } from "../../../new_fields/documentSchemas"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { - dataProvider?: (doc: Doc, dataDoc?: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined; + dataProvider?: (doc: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined; x?: number; y?: number; width?: number; @@ -24,6 +24,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent(PositionDocument) { _disposer: IReactionDisposer | undefined = undefined; + get displayName() { return "CollectionFreeFormDocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; } get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); } get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); } @@ -32,7 +33,7 @@ export class CollectionFreeFormDocumentView extends DocComponent boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; removeDocument: (document: Doc) => boolean; - active: () => boolean; + active: (outsideReaction: boolean) => boolean; whenActiveChanged: (isActive: boolean) => void; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 12ae5b6e5..b9b84d5ce 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -50,7 +50,7 @@ const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any; @observer export class DocumentContentsView extends React.Component boolean, + isSelected: (outsideReaction: boolean) => boolean, select: (ctrl: boolean) => void, onClick?: ScriptField, layoutKey: string, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 411d6bdea..cf5a94f01 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -11,13 +11,12 @@ import { ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { ImageField } from '../../../new_fields/URLField'; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; -import { emptyFunction, returnTransparent, returnTrue, Utils } from "../../../Utils"; +import { emptyFunction, returnTransparent, returnTrue, Utils, returnOne } from "../../../Utils"; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; import { DocumentType } from '../../documents/DocumentTypes'; import { ClientUtils } from '../../util/ClientUtils'; -import { DictationManager } from '../../util/DictationManager'; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, dropActionType } from "../../util/DragManager"; import { LinkManager } from '../../util/LinkManager'; @@ -65,7 +64,7 @@ export interface DocumentViewProps { PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean) => void; - parentActive: () => boolean; + parentActive: (outsideReaction: boolean) => boolean; whenActiveChanged: (isActive: boolean) => void; bringToFront: (doc: Doc, sendToBack?: boolean) => void; addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => boolean; @@ -90,12 +89,13 @@ export class DocumentView extends DocComponent(Docu private _mainCont = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; + public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive public get ContentDiv() { return this._mainCont.current; } - @computed get active() { return SelectionManager.IsSelected(this) || this.props.parentActive(); } + @computed get active() { return SelectionManager.IsSelected(this, true) || this.props.parentActive(true); } @computed get topMost() { return this.props.renderDepth === 0; } @computed get nativeWidth() { return this.layoutDoc.nativeWidth || 0; } @computed get nativeHeight() { return this.layoutDoc.nativeHeight || 0; } - @computed get onClickHandler() { trace(); console.log("this.props.doc = " + this.props.Document.title); return this.props.onClick ? this.props.onClick : this.Document.onClick; } + @computed get onClickHandler() { return this.props.onClick ? this.props.onClick : this.Document.onClick; } @action componentDidMount() { @@ -215,7 +215,7 @@ export class DocumentView extends DocComponent(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive() || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.Document.onDragStart || this.Document.onClick) && !this.Document.lockedPosition && !this.Document.inOverlay) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { if (!e.altKey && (!this.topMost || this.Document.onDragStart || this.Document.onClick) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCH))) { document.removeEventListener("pointermove", this.onPointerMove); @@ -516,7 +516,7 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); } ContextMenu.Instance.displayMenu(e.pageX - 15, e.pageY - 15); - if (!SelectionManager.IsSelected(this)) { + if (!SelectionManager.IsSelected(this, true)) { SelectionManager.SelectDoc(this, false); } }); @@ -528,7 +528,7 @@ export class DocumentView extends DocComponent(Docu getLayoutPropStr = (prop: string) => StrCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]); getLayoutPropNum = (prop: string) => NumCast(this.setsLayoutProp(prop) ? this.props.Document[prop] : this.layoutDoc[prop]); - isSelected = () => SelectionManager.IsSelected(this); + isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction); select = (ctrlPressed: boolean) => { SelectionManager.SelectDoc(this, ctrlPressed); }; chromeHeight = () => { @@ -616,7 +616,7 @@ export class DocumentView extends DocComponent(Docu return <> {this.Document.links && DocListCast(this.Document.links).filter((d) => !DocListCast(this.layoutDoc.hiddenLinks).some(hidden => Doc.AreProtosEqual(hidden, d))).filter(this.isNonTemporalLink).map((d, i) =>
    - Doc.AddDocToList(this.layoutDoc, "hiddenLinks", doc))} /> + Doc.AddDocToList(this.layoutDoc, "hiddenLinks", doc))} />
    )} {!showTitle && !showCaption ? this.Document.searchFields ? @@ -638,9 +638,12 @@ export class DocumentView extends DocComponent(Docu } ; } + @computed get ignorePointerEvents() { + return this.Document.isBackground && !this.isSelected(); + } + render() { if (!this.props.Document) return (null); - trace(); const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined; const ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; const colorSet = this.setsLayoutProp("backgroundColor"); @@ -662,10 +665,10 @@ export class DocumentView extends DocComponent(Docu let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear; return
    Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)} + onPointerEnter={e => Doc.BrushDoc(this.props.Document)} onPointerLeave={e => Doc.UnBrushDoc(this.props.Document)} style={{ transition: this.Document.isAnimating !== undefined ? ".5s linear" : StrCast(this.Document.transition), - pointerEvents: this.Document.isBackground && !this.isSelected() ? "none" : "all", + pointerEvents: this.ignorePointerEvents ? "none" : "all", color: StrCast(this.Document.color), outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px", border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined, diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 5108954bb..c93746773 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -31,7 +31,7 @@ export interface FieldViewProps { Document: Doc; DataDoc?: Doc; onClick?: ScriptField; - isSelected: () => boolean; + isSelected: (outsideReaction?: boolean) => boolean; select: (isCtrlPressed: boolean) => void; renderDepth: number; addDocument?: (document: Doc) => boolean; @@ -40,7 +40,7 @@ export interface FieldViewProps { removeDocument?: (document: Doc) => boolean; moveDocument?: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; ScreenToLocalTransform: () => Transform; - active: () => boolean; + active: (outsideReaction?: boolean) => boolean; whenActiveChanged: (isActive: boolean) => void; focus: (doc: Doc) => void; PanelWidth: () => number; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 31919f192..5201f9bdc 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -839,7 +839,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & if (this.props.onClick && e.button === 0) { e.preventDefault(); } - if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.button === 0 && this.props.isSelected(true) && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); } if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { @@ -854,7 +854,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & } (e.nativeEvent as any).formattedHandled = true; - if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { + if (e.buttons === 1 && this.props.isSelected(true) && !e.altKey) { e.stopPropagation(); } } @@ -867,7 +867,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & } onPointerWheel = (e: React.WheelEvent): void => { // if a text note is not selected and scrollable, this prevents us from being able to scroll and zoom out at the same time - if (this.props.isSelected() || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) { + if (this.props.isSelected(true) || e.currentTarget.scrollHeight > e.currentTarget.clientHeight) { e.stopPropagation(); } } @@ -878,7 +878,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & onClick = (e: React.MouseEvent): void => { if ((e.nativeEvent as any).formattedHandled) { e.stopPropagation(); return; } (e.nativeEvent as any).formattedHandled = true; - // if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) { + // if (e.button === 0 && ((!this.props.isSelected(true) && !e.ctrlKey) || (this.props.isSelected(true) && e.ctrlKey)) && !e.metaKey && e.target) { // let href = (e.target as any).href; // let location: string; // if ((e.target as any).attributes.location) { @@ -918,7 +918,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & // this hackiness handles clicking on the list item bullets to do expand/collapse. the bullets are ::before pseudo elements so there's no real way to hit test against them. hitBulletTargets(x: number, y: number, offsetX: number, select: boolean = false) { clearStyleSheetRules(FormattedTextBox._bulletStyleSheet); - if (this.props.isSelected() && offsetX < 40) { + if (this.props.isSelected(true) && offsetX < 40) { let pos = this._editorView!.posAtCoords({ left: x, top: y }); if (pos && pos.pos > 0) { let node = this._editorView!.state.doc.nodeAt(pos.pos); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 35e9e4862..aa6e135fe 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -95,7 +95,7 @@ export class KeyValueBox extends React.Component { } onPointerDown = (e: React.PointerEvent): void => { - if (e.buttons === 1 && this.props.isSelected()) { + if (e.buttons === 1 && this.props.isSelected(true)) { e.stopPropagation(); } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5cfd4b019..e7d8ac46c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -21,6 +21,7 @@ import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); import { documentSchema } from '../../../new_fields/documentSchemas'; +import { SelectionManager } from '../../util/SelectionManager'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -62,7 +63,7 @@ export class PDFBox extends DocAnnotatableComponent this._selectReactionDisposer = reaction(() => this.props.isSelected(), () => { document.removeEventListener("keydown", this.onKeyDown); - this.props.isSelected() && document.addEventListener("keydown", this.onKeyDown); + this.props.isSelected(true) && document.addEventListener("keydown", this.onKeyDown); }, { fireImmediately: true }); } @@ -202,16 +203,16 @@ export class PDFBox extends DocAnnotatableComponent
    ; } - isChildActive = () => this._isChildActive; + isChildActive = (outsideReaction?: boolean) => this._isChildActive; @computed get renderPdfView() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); return
    this.Document.currentTimecode, () => !this._playing && this.Seek(this.Document.currentTimecode || 0)); this._youtubeReactionDisposer = reaction(() => [this.props.isSelected(), DocumentDecorations.Instance.Interacting, InkingControl.Instance.selectedTool], () => { - let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected() && !DocumentDecorations.Instance.Interacting; + let interactive = InkingControl.Instance.selectedTool === InkTool.None && this.props.isSelected(true) && !DocumentDecorations.Instance.Interacting; iframe.style.pointerEvents = interactive ? "all" : "none"; }, { fireImmediately: true }); }; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 77790a708..7d8f39e41 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -59,10 +59,10 @@ interface IViewerProps { startupLive: boolean; renderDepth: number; focus: (doc: Doc) => void; - isSelected: () => boolean; + isSelected: (outsideReaction?: boolean) => boolean; loaded: (nw: number, nh: number, np: number) => void; - active: () => boolean; - isChildActive: () => boolean; + active: (outsideReaction?: boolean) => boolean; + isChildActive: (outsideReaction?: boolean) => boolean; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; pinToPres: (document: Doc) => void; addDocument?: (doc: Doc) => boolean; @@ -166,7 +166,7 @@ export class PDFViewer extends DocAnnotatableComponent { - if (this.props.active() && e.clipboardData) { + if (this.props.active(true) && e.clipboardData) { let annoDoc = this.makeAnnotationDocument("rgba(3,144,152,0.3)"); // copied text markup color (blueish) if (annoDoc) { e.clipboardData.setData("text/plain", this._selectionText); @@ -397,7 +397,7 @@ export class PDFViewer extends DocAnnotatableComponent { let hit = document.elementFromPoint(e.clientX, e.clientY); - if (hit && hit.localName === "span" && this.props.isSelected()) { // drag selecting text stops propagation + if (hit && hit.localName === "span" && this.props.isSelected(true)) { // drag selecting text stops propagation e.button === 0 && e.stopPropagation(); } // if alt+left click, drag and annotate @@ -405,11 +405,11 @@ export class PDFViewer extends DocAnnotatableComponent = new ObservableMap(); + BrushedDoc: ObservableMap = new ObservableMap(); } const brushManager = new DocBrush(); export class DocData { @observable _user_doc: Doc = undefined!; - @observable BrushedDoc: ObservableMap = new ObservableMap(); } // the document containing the view layout information - will be the Document itself unless the Document has @@ -654,11 +654,19 @@ export namespace Doc { export function UserDoc(): Doc { return manager._user_doc; } export function SetUserDoc(doc: Doc) { manager._user_doc = doc; } export function IsBrushed(doc: Doc) { - return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)); + return computedFn(function IsBrushed(doc: Doc) { + return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)); + })(doc); } - export function IsBrushedDegree(doc: Doc) { + // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message) + export function IsBrushedDegreeUnmemoized(doc: Doc) { return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 1 : 0; } + export function IsBrushedDegree(doc: Doc) { + return computedFn(function IsBrushDegree(doc: Doc) { + return brushManager.BrushedDoc.has(doc) ? 2 : brushManager.BrushedDoc.has(Doc.GetDataDoc(doc)) ? 1 : 0; + })(doc); + } export function BrushDoc(doc: Doc) { brushManager.BrushedDoc.set(doc, true); brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true); -- cgit v1.2.3-70-g09d2 From 71ebece4a97547ac9bec32cd30957d38c96fb755 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 6 Dec 2019 21:19:25 -0500 Subject: performance fixes for sidebar. stop re-rendering docking view when sidebar expands. fixed lightbox. --- src/client/views/MainView.scss | 17 ++++- src/client/views/MainView.tsx | 77 +++++++++++----------- .../views/collections/CollectionDockingView.tsx | 2 + src/client/views/collections/CollectionView.tsx | 14 +++- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../views/nodes/ContentFittingDocumentView.tsx | 1 + 6 files changed, 68 insertions(+), 45 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 0ee30f117..a8924c6b1 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -7,11 +7,18 @@ width: 100%; } +.mainContent-div { + position: relative; + width:100%; + height:100%; +} + // add nodes menu. Note that the + button is actually an input label, not an actual button. .mainView-docButtons { position: absolute; bottom: 20px; - left: 250px; + left: calc(100% + 5px); + z-index: 1; } #mainView-container { @@ -27,13 +34,13 @@ width: 100%; height: 100%; position: absolute; + display: flex; } .mainView-flyoutContainer { display: flex; flex-direction: column; - position: absolute; - width: 100%; + position: relative; height: 100%; .documentView-node-topmost { @@ -59,9 +66,11 @@ .mainView-libraryFlyout { height: 100%; + width:100%; position: absolute; display: flex; flex-direction: column; + z-index: 1; } .mainView-expandFlyoutButton { @@ -73,6 +82,7 @@ .mainView-libraryHandle { width: 20px; + left: calc(100% - 10px); height: 40px; top: 50%; border: 1px solid black; @@ -80,6 +90,7 @@ position: absolute; z-index: 1; touch-action: none; + cursor: ew-resize; } .mainView-workspace { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 29719a6eb..175af1abc 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -39,6 +39,7 @@ import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsM import InkSelectDecorations from './InkSelectDecorations'; import { Scripting } from '../util/Scripting'; import { AudioBox } from './nodes/AudioBox'; +import { TraceMobx } from '../../new_fields/util'; @observer export class MainView extends React.Component { @@ -260,38 +261,41 @@ export class MainView extends React.Component { getPHeight = () => this._panelHeight; getContentsHeight = () => this._panelHeight - this._buttonBarHeight; + _emptyPath = []; + @computed get mainDocView() { + return + } @computed get dockingContent() { + TraceMobx(); const mainContainer = this.mainContainer; - const flyoutWidth = this.flyoutWidth; // bcz: need to be here because Measure messes with observables. - const flyoutTranslate = this._flyoutTranslate; + const width = this.flyoutWidth; return {({ measureRef }) => -
    - {!mainContainer ? (null) : - } +
    + {!mainContainer ? (null) : this.mainDocView}
    } ; @@ -416,6 +420,7 @@ export class MainView extends React.Component { {CurrentUserUtils.GuestWorkspace ? "Exit" : "Log Out"}
    + {this.docButtons}
    ; } @@ -423,10 +428,9 @@ export class MainView extends React.Component { const sidebar = this.userDoc && this.userDoc.sidebarContainer; return !this.userDoc || !(sidebar instanceof Doc) ? (null) : (
    -
    -
    +
    +
    {this.flyout} @@ -471,7 +473,7 @@ export class MainView extends React.Component { @computed get docButtons() { if (CurrentUserUtils.UserDocument?.expandingButtons instanceof Doc) { return
    + style={{ height: !CurrentUserUtils.UserDocument.expandingButtons.isExpanded ? "42px" : undefined }} > - {this.docButtons} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 4374cde3c..ffcb3e9ce 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -32,6 +32,7 @@ import React = require("react"); import { ButtonSelector } from './ParentDocumentSelector'; import { DocumentType } from '../../documents/DocumentTypes'; import { ComputedField } from '../../../new_fields/ScriptField'; +import { TraceMobx } from '../../../new_fields/util'; library.add(faFile); const _global = (window /* browser */ || global /* node */) as any; @@ -662,6 +663,7 @@ export class DockedFrameRenderer extends React.Component { } @computed get docView() { + TraceMobx(); if (!this._document) return (null); const document = this._document; const resolvedDataDoc = document.layout instanceof Doc ? document : this._dataDoc; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 54f5a2c42..cd546696f 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -32,6 +32,7 @@ import { SelectionManager } from '../../util/SelectionManager'; import './CollectionView.scss'; import { FieldViewProps, FieldView } from '../nodes/FieldView'; import { Touchable } from '../Touchable'; +const path = require('path'); library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy); export enum CollectionViewType { @@ -234,10 +235,17 @@ export class CollectionView extends Touchable { } lightbox = (images: string[]) => { + if (!images.length) return (null); + const mainPath = path.extname(images[this._curLightboxImg]); + const nextPath = path.extname(images[(this._curLightboxImg + 1) % images.length]); + const prevPath = path.extname(images[(this._curLightboxImg + images.length - 1) % images.length]); + let main = images[this._curLightboxImg].replace(mainPath, "_o" + mainPath); + let next = images[(this._curLightboxImg + 1) % images.length].replace(nextPath, "_o" + nextPath); + let prev = images[(this._curLightboxImg + images.length - 1) % images.length].replace(prevPath, "_o" + prevPath); return !this._isLightboxOpen ? (null) : ( this._isLightboxOpen = false)} onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)} onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0d3748ded..81d8d467b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -711,7 +711,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { case "pivot": computedElementData = this.doPivotLayout(this._layoutPoolData); break; default: computedElementData = this.doFreeformLayout(this._layoutPoolData); break; } - this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).forEach(pair => + this.childLayoutPairs.filter((pair, i) => this.isCurrent(pair.layout)).forEach(pair => computedElementData.elements.push({ ele: { + public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive private get layoutDoc() { return this.props.Document && Doc.Layout(this.props.Document); } private get nativeWidth() { return NumCast(this.layoutDoc!.nativeWidth, this.props.PanelWidth()); } private get nativeHeight() { return NumCast(this.layoutDoc!.nativeHeight, this.props.PanelHeight()); } -- cgit v1.2.3-70-g09d2 From 16c84a8042fc3e9d7af3d74fce2758bf6862a5a2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 17 Dec 2019 20:55:38 -0500 Subject: warnings and fix to doc decorations close button to deselect first --- src/client/views/DocumentDecorations.tsx | 5 +++-- src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/MainView.scss | 4 ++-- src/client/views/collections/ParentDocumentSelector.tsx | 2 +- src/client/views/nodes/DocumentBox.tsx | 3 +++ 5 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index fdaca87a9..228cea00e 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -233,11 +233,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> e.stopPropagation(); if (e.button === 0) { const recent = Cast(CurrentUserUtils.UserDocument.recentlyClosed, Doc) as Doc; - SelectionManager.SelectedDocuments().map(dv => { + let selected = SelectionManager.SelectedDocuments().slice(); + SelectionManager.DeselectAll(); + selected.map(dv => { recent && Doc.AddDocToList(recent, "data", dv.props.Document, undefined, true, true); dv.props.removeDocument && dv.props.removeDocument(dv.props.Document); }); - SelectionManager.DeselectAll(); document.removeEventListener("pointermove", this.onCloseMove); document.removeEventListener("pointerup", this.onCloseUp); } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index c48e1df4f..979687ffb 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -127,7 +127,7 @@ export default class KeyManager { switch (keyname) { case "f": - let dv = SelectionManager.SelectedDocuments()?.[0]; + const dv = SelectionManager.SelectedDocuments()?.[0]; if (dv) { const ex = dv.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0]; const ey = dv.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1]; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index a8924c6b1..51a062118 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -70,7 +70,7 @@ position: absolute; display: flex; flex-direction: column; - z-index: 1; + z-index: 2; } .mainView-expandFlyoutButton { @@ -88,7 +88,7 @@ border: 1px solid black; border-radius: 5px; position: absolute; - z-index: 1; + z-index: 2; touch-action: none; cursor: ew-resize; } diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index ffaf41b91..422d01cee 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -118,7 +118,7 @@ export class ButtonSelector extends React.Component<{ Document: Doc, Stack: any render() { const view = DocumentManager.Instance.getDocumentView(this.props.Document); - let flyout = ( + const flyout = (
    diff --git a/src/client/views/nodes/DocumentBox.tsx b/src/client/views/nodes/DocumentBox.tsx index 6be0289d7..94755afec 100644 --- a/src/client/views/nodes/DocumentBox.tsx +++ b/src/client/views/nodes/DocumentBox.tsx @@ -33,6 +33,9 @@ export class DocumentBox extends DocComponent(DocB } }); } + componentWillUnmount() { + this._prevSelectionDisposer && this._prevSelectionDisposer(); + } specificContextMenu = (e: React.MouseEvent): void => { const funcs: ContextMenuProps[] = []; funcs.push({ description: (this.isSelectionLocked() ? "Show" : "Lock") + " Selection", event: () => this.toggleLockSelection, icon: "expand-arrows-alt" }); -- cgit v1.2.3-70-g09d2 From 8907ea70352d07c80233ff4c3e06c9c543c5be83 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 19 Dec 2019 13:00:18 -0500 Subject: fixing minor details with tree view opening workspace, moving documents in stacking views, avoiding search exceptions, --- src/client/util/SearchUtil.ts | 6 +++--- src/client/views/MainView.scss | 4 ++-- src/client/views/collections/CollectionDockingView.tsx | 3 +-- src/client/views/collections/CollectionTreeView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/util/SearchUtil.ts b/src/client/util/SearchUtil.ts index 1fda82880..4f6211d94 100644 --- a/src/client/util/SearchUtil.ts +++ b/src/client/util/SearchUtil.ts @@ -34,9 +34,9 @@ export namespace SearchUtil { export function Search(query: string, returnDocs: false, options?: SearchParams): Promise; export async function Search(query: string, returnDocs: boolean, options: SearchParams = {}) { query = query || "*"; //If we just have a filter query, search for * as the query - const result: IdSearchResult = JSON.parse(await rp.get(Utils.prepend("/search"), { - qs: { ...options, q: query }, - })); + let rpquery = Utils.prepend("/search"); + let gotten = await rp.get(rpquery, { qs: { ...options, q: query } }); + const result: IdSearchResult = gotten.startsWith("<") ? { ids: [], docs: [], numFound: 0, lines: [] } : JSON.parse(gotten); if (!returnDocs) { return result; } diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 51a062118..4c8c95529 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -59,8 +59,8 @@ .mainView-logout { position: absolute; - right: 0; - bottom: 0; + right: 5; + bottom: 5; font-size: 8px; } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 08b9fd216..232722f48 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -649,8 +649,7 @@ export class DockedFrameRenderer extends React.Component { addDocTab = (doc: Doc, dataDoc: Opt, location: string, libraryPath?: Doc[]) => { SelectionManager.DeselectAll(); if (doc.dockingConfig) { - MainView.Instance.openWorkspace(doc); - return true; + return MainView.Instance.openWorkspace(doc); } else if (location === "onRight") { return CollectionDockingView.AddRightSplit(doc, dataDoc, undefined, libraryPath); } else if (location === "close") { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 9f5db5faf..2b13d87ee 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -202,7 +202,6 @@ class TreeView extends React.Component { } ContextMenu.Instance.addItem({ description: "Delete Item", event: () => this.props.deleteDoc(this.props.document), icon: "trash-alt" }); } else { - ContextMenu.Instance.addItem({ description: "Open as Workspace", event: () => MainView.Instance.openWorkspace(this.dataDoc), icon: "caret-square-right" }); 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" }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0753ad632..6ea5b2d2a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -130,7 +130,7 @@ export class DocumentView extends DocComponent(Docu const [left, top] = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).inverse().transformPoint(0, 0); dragData.offset = this.props.ScreenToLocalTransform().scale(this.props.ContentScaling()).transformDirection(x - left, y - top); dragData.dropAction = dropAction; - dragData.moveDocument = this.Document.onDragStart ? undefined : this.props.moveDocument; + dragData.moveDocument = this.props.moveDocument;// this.Document.onDragStart ? undefined : this.props.moveDocument; dragData.applyAsTemplate = applyAsTemplate; dragData.dragDivName = this.props.dragDivName; DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.Document.onDragStart }); -- cgit v1.2.3-70-g09d2 From e410cde0e430553002d4e1a2f64364b57b65fdbc Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 16 Jan 2020 09:59:21 -0500 Subject: fixed context menu on mac. fixed exceptions with contentScaling and contentfittingviews. fixed sizing of mainview's contents --- src/client/views/MainView.scss | 8 ++++++++ src/client/views/nodes/ContentFittingDocumentView.tsx | 6 +++--- src/client/views/nodes/DocumentView.tsx | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 4c8c95529..c7fc6096a 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -5,6 +5,9 @@ .mainView-tabButtons { position: relative; width: 100%; + .documentView-node-topmost { + height: 200% !important; + } } .mainContent-div { @@ -13,6 +16,11 @@ height:100%; } +.mainView-contentArea { + .documentView-node-topmost { + height: 200% !important; + } +} // add nodes menu. Note that the + button is actually an input label, not an actual button. .mainView-docButtons { position: absolute; diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index bbec66233..e97445f27 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -48,11 +48,11 @@ export class ContentFittingDocumentView extends React.Component { - const wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth()); + const wscale = this.props.PanelWidth() / (this.nativeWidth || this.props.PanelWidth() || 1); if (wscale * this.nativeHeight > this.props.PanelHeight()) { - return this.props.PanelHeight() / (this.nativeHeight ? this.nativeHeight : this.props.PanelHeight()); + return (this.props.PanelHeight() / (this.nativeHeight || this.props.PanelHeight() || 1)) || 1; } - return wscale; + return wscale || 1; } @undoBatch diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b2c2ccff5..dabe5a7aa 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -583,7 +583,7 @@ export class DocumentView extends DocComponent(Docu @action onContextMenu = async (e: React.MouseEvent): Promise => { // the touch onContextMenu is button 0, the pointer onContextMenu is button 2 - if (e.button === 0) { + if (e.button === 0 && !e.ctrlKey) { e.preventDefault(); return; } -- cgit v1.2.3-70-g09d2 From fcd9313158f8c131cf5a124880c46ca7b20a1b25 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 16 Jan 2020 11:43:38 -0500 Subject: better fix for sidebar / stacking view sizing --- src/client/views/MainView.scss | 8 -------- src/client/views/collections/CollectionStackingView.tsx | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) (limited to 'src/client/views/MainView.scss') diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index c7fc6096a..4c8c95529 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -5,9 +5,6 @@ .mainView-tabButtons { position: relative; width: 100%; - .documentView-node-topmost { - height: 200% !important; - } } .mainContent-div { @@ -16,11 +13,6 @@ height:100%; } -.mainView-contentArea { - .documentView-node-topmost { - height: 200% !important; - } -} // add nodes menu. Note that the + button is actually an input label, not an actual button. .mainView-docButtons { position: absolute; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 886a9c870..ca792c134 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -396,7 +396,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { ref={this.createRef} style={{ transform: `scale(${Math.min(1, this.props.PanelHeight() / this.layoutDoc[HeightSym]())})`, - height: `${100 * 1 / Math.min(this.props.PanelWidth() / this.layoutDoc[WidthSym](), this.props.PanelHeight() / this.layoutDoc[HeightSym]())}%`, + height: `${Math.max(100, 100 * 1 / Math.min(this.props.PanelWidth() / this.layoutDoc[WidthSym](), this.props.PanelHeight() / this.layoutDoc[HeightSym]()))}%`, transformOrigin: "top" }} onScroll={action((e: React.UIEvent) => this._scroll = e.currentTarget.scrollTop)} -- cgit v1.2.3-70-g09d2