aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/util/SelectionManager.ts41
-rw-r--r--src/client/views/DocComponent.tsx14
-rw-r--r--src/client/views/DocumentButtonBar.scss106
-rw-r--r--src/client/views/DocumentButtonBar.tsx264
-rw-r--r--src/client/views/Main.scss6
-rw-r--r--src/client/views/MainView.scss1
-rw-r--r--src/client/views/TemplateMenu.scss50
-rw-r--r--src/client/views/TemplateMenu.tsx4
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx2
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx14
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx6
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx87
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx5
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx2
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx27
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx10
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx7
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/pdf/PDFViewer.tsx14
-rw-r--r--src/new_fields/Doc.ts16
25 files changed, 326 insertions, 364 deletions
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<DocumentView> = [];
+ SelectedDocuments: ObservableMap<DocumentView, boolean> = 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<DocumentView> {
- 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<P extends DocExtendableProps, T>(schemaCtor: (doc: Doc) => T) {
@@ -37,7 +37,7 @@ export function DocExtendableComponent<P extends DocExtendableProps, T>(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<P extends DocAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
@@ -82,10 +82,10 @@ export function DocAnnotatableComponent<P extends DocAnnotatableProps, T>(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<HTMLDivElement>();
- private _aliasButton = React.createRef<HTMLDivElement>();
- private _tooltipoff = React.createRef<HTMLDivElement>();
- 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 (
- <div className={"linkButtonWrapper"}>
- <div title={`${published ? "Push" : "Publish"} to Google Docs`} className="linkButton-linker" onClick={() => {
- DocumentButtonBar.hasPushedHack = false;
- this.targetDoc[Pushes] = NumCast(this.targetDoc[Pushes]) + 1;
- }}>
- <FontAwesomeIcon className="documentdecorations-icon" icon={icon} size={published ? "sm" : "xs"} />
- </div>
- </div>
- );
+ @computed
+ get considerGoogleDocsPush() {
+ let targetDoc = this.props.views[0].props.Document;
+ let published = Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
+ return <div title={`${published ? "Push" : "Publish"} to Google Docs`} className="documentButtonBar-linker" onClick={() => {
+ DocumentButtonBar.hasPushedHack = false;
+ targetDoc[Pushes] = NumCast(targetDoc[Pushes]) + 1;
+ }}>
+ <FontAwesomeIcon className="documentdecorations-icon" icon={published ? (this.pushIcon as any) : cloud} size={published ? "sm" : "xs"} />
+ </div>;
}
- 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 (
- <div className={"linkButtonWrapper"}>
- <div
- title={title}
- className="linkButton-linker"
- style={{
- backgroundColor: this.pullColor,
- transition: "0.2s ease all"
- }}
- onPointerEnter={e => 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);
- }
- }}>
- <FontAwesomeIcon
- style={{
- WebkitAnimation: animation,
- MozAnimation: animation
- }}
- className="documentdecorations-icon"
- icon={icon}
- size="sm"
- />
- </div>
- </div>
- );
+ return !dataDoc[GoogleRef] ? (null) : <div className="documentButtonBar-linker"
+ title={`${!dataDoc.unchanged ? "Pull from" : "Fetch"} Google Docs`}
+ style={{ backgroundColor: this.pullColor }}
+ onPointerEnter={e => 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);
+ }
+ }}>
+ <FontAwesomeIcon className="documentdecorations-icon" size="sm"
+ style={{ WebkitAnimation: animation, MozAnimation: animation }}
+ icon={this.openHover ? "share" : dataDoc.unchanged === false ? (this.pullIcon as any) : fetch}
+ />
+ </div>;
}
- 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 (
- <div className="tooltipwrapper">
- <div title="Hide Tooltip" className="linkButton-linker" ref={this._tooltipoff} onPointerDown={this.onTooltipOff}>
- {/* <FontAwesomeIcon className="fa-image" icon="image" size="sm" /> */}
+ @computed
+ get linkButton() {
+ let linkCount = LinkManager.Instance.getAllRelatedLinks(this.props.views[0].props.Document).length;
+ return <div title="Drag(create link) Tap(view links)" className="documentButtonBar-linkFlyout" ref={this._linkButton}>
+ <Flyout anchorPoint={anchorPoints.RIGHT_TOP}
+ content={<LinkMenu docView={this.props.views[0]} addDocTab={this.props.views[0].props.addDocTab} changeFlyout={emptyFunction} />}>
+ <div className={"documentButtonBar-linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
+ {linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
</div>
- </div>
-
- );
+ </Flyout>
+ </div>;
}
- 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 <ParentDocSelector Views={this.props.views} Document={this.props.views[0].props.Document} addDocTab={(doc, data, where) => {
+ 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 = <Flyout anchorPoint={anchorPoints.RIGHT_TOP}
- content={<LinkMenu docView={selFirst} addDocTab={selFirst.props.addDocTab} changeFlyout={emptyFunction} />}>
- <div className={"linkButton-" + (linkCount ? "nonempty" : "empty")} onPointerDown={this.onLinkButtonDown} >
- {linkCount ? linkCount : <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />}
- </div>
- </Flyout >;
- }
-
let templates: Map<Template, boolean> = 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 (<div className="documentButtonBar">
- <div className="linkButtonWrapper">
- <div title="Drag(create link) Tap(view links)" className="linkFlyout" ref={this._linkButton}> {linkButton} </div>
+ 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 <div className="documentButtonBar">
+ <div className="documentButtonBar-button">
+ {this.linkButton}
</div>
- <div className="linkButtonWrapper">
+ <div className="documentButtonBar-button">
<TemplateMenu docs={this.props.views} templates={templates} />
</div>
- {this.considerGoogleDocsPush()}
- {this.considerGoogleDocsPull()}
- <ParentDocSelector Views={this.props.views} Document={this.props.views[0].props.Document} addDocTab={(doc, data, where) => {
- 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()} */}
- </div>
- );
+ <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
+ {this.considerGoogleDocsPush}
+ </div>
+ <div className="documentButtonBar-button" style={{ display: !considerPull ? "none" : "" }}>
+ {this.considerGoogleDocsPull}
+ </div>
+ {this.contextButton}
+ </div>;
}
} \ 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<TemplateMenuProps> {
return (
<div className="templating-menu" onPointerDown={this.onAliasButtonDown}>
<div title="Drag:(create alias). Tap:(modify layout)." className="templating-button" onClick={() => this.toggleTemplateActivity()}>+</div>
- <ul id="template-list" ref={this._dragRef} style={{ display: this._hidden ? "none" : "block" }}>
+ <ul className="template-list" ref={this._dragRef} style={{ display: this._hidden ? "none" : "block" }}>
{templateMenu}
{<button onClick={this.clearTemplates}>Restore Defaults</button>}
</ul>
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<SubCollectionViewProp
tab.element.append(upDiv);
tab.reactionDisposer = reaction(() => [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 <div className="collectionSchemaView-container">
- <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active() && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}>
+ <div className="collectionSchemaView-tableContainer" onPointerDown={this.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.onDrop(e, {})} ref={this.createTarget}>
{this.schemaTable}
</div>
{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<Element>, 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<SchemaTableProps> {
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<SchemaTableProps> {
}
render() {
- return <div className="collectionSchemaView-table" onPointerDown={this.onPointerDown} onWheel={e => this.props.active() && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
+ return <div className="collectionSchemaView-table" onPointerDown={this.onPointerDown} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
{this.reactTable}
<div className="collectionSchemaView-addRow" onClick={() => this.createRow()}>+ new</div>
</div>;
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<TreeViewProps> {
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<TreeViewProps> {
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<FieldViewProps> {
// 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 210a5132a..8791a256c 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<string, any>();
+
+ 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: <CollectionFreeFormDocumentView key={pair.layout[Id]} dataProvider={this.lookupLayout}
+ ele: <CollectionFreeFormDocumentView key={pair.layout[Id]} dataProvider={this.childDataProvider}
ruleProvider={this.Document.isRuleProvider ? this.props.Document : this.props.ruleProvider}
jitterRotation={NumCast(this.props.Document.jitterRotation)} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />,
- 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) :
- <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}
- style={{ pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined, height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
+ <div className={"collectionfreeformview-container"} ref={this.createDropTarget} onWheel={this.onPointerWheel}//pointerEvents: SelectionManager.GetIsDragging() ? "all" : undefined,
+ style={{ height: this.isAnnotationOverlay ? (this.props.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight() }}
onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onTouchStart={this.onTouchStart}>
<MarqueeView {...this.props} extensionDoc={this.extensionDoc} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument}
addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform} isAnnotationOverlay={this.isAnnotationOverlay}>
@@ -863,11 +862,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
{this.children}
</CollectionFreeFormViewPannableContents>
</MarqueeView>
- {this.overlayViews}
+ <CollectionFreeFormOverlayView elements={this.elementFunc} />
</div>;
}
}
+interface CollectionFreeFormOverlayViewProps {
+ elements: () => ViewDefResult[];
+}
+
+@observer
+class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps>{
+ @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<SubCollectionViewProps & Marque
@action
onPointerUp = (e: PointerEvent): void => {
- 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 bbd9859a8..bcff91f4b 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<CollectionFreeFormDocumentViewProps, PositionDocument>(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<CollectionFreeF
let hgt = this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.layoutDoc[HeightSym]();
return (hgt === undefined && this.nativeWidth && this.nativeHeight) ? this.width * this.nativeHeight / this.nativeWidth : hgt;
}
- @computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document, this.props.DataDoc) ? this.props.dataProvider(this.props.Document, this.props.DataDoc) : undefined; }
+ @computed get dataProvider() { return this.props.dataProvider && this.props.dataProvider(this.props.Document) ? this.props.dataProvider(this.props.Document) : undefined; }
@computed get nativeWidth() { return NumCast(this.layoutDoc.nativeWidth); }
@computed get nativeHeight() { return NumCast(this.layoutDoc.nativeHeight); }
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index c8255b6fe..573a55710 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -32,7 +32,7 @@ interface ContentFittingDocumentViewProps {
addDocument: (document: Doc) => 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<DocumentViewProps & {
- isSelected: () => 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 338cf6202..becb7bdfe 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<DocumentViewProps, Document>(Docu
private _mainCont = React.createRef<HTMLDivElement>();
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<DocumentViewProps, Document>(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<DocumentViewProps, Document>(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<DocumentViewProps, Document>(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<DocumentViewProps, Document>(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) =>
<div className="documentView-docuLinkWrapper" key={`${d[Id]}`} style={{ transform: `scale(${this.layoutDoc.fitWidth ? 1 : 1 / 1})` }}>
- <DocumentView {...this.props} Document={d} layoutKey={this.linkEndpoint(d)} backgroundColor={returnTransparent} removeDocument={undoBatch(doc => Doc.AddDocToList(this.layoutDoc, "hiddenLinks", doc))} />
+ <DocumentView {...this.props} ContentScaling={returnOne} Document={d} layoutKey={this.linkEndpoint(d)} backgroundColor={returnTransparent} removeDocument={undoBatch(doc => Doc.AddDocToList(this.layoutDoc, "hiddenLinks", doc))} />
</div>)}
{!showTitle && !showCaption ?
this.Document.searchFields ?
@@ -638,9 +638,12 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(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<DocumentViewProps, Document>(Docu
let highlighting = fullDegree && this.layoutDoc.type !== DocumentType.FONTICON && this.layoutDoc.viewType !== CollectionViewType.Linear;
return <div className={`documentView-node${this.topMost ? "-topmost" : ""}`} ref={this._mainCont}
onDrop={this.onDrop} onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick}
- onPointerEnter={() => 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 0e5fae4af..2f9e8d5f4 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<FieldViewProps> {
}
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<FieldViewProps, PdfDocument>
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<FieldViewProps, PdfDocument>
</div>;
}
- isChildActive = () => this._isChildActive;
+ isChildActive = (outsideReaction?: boolean) => this._isChildActive;
@computed get renderPdfView() {
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
return <div className={"pdfBox-cont"} onContextMenu={this.specificContextMenu}>
<PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
- Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
addDocTab={this.props.addDocTab} focus={this.props.focus}
pinToPres={this.props.pinToPres} addDocument={this.addDocument}
+ Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling}
ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select}
isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
isChildActive={this.isChildActive}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 4c826f835..1d1972841 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -248,7 +248,7 @@ export class VideoBox extends DocAnnotatableComponent<FieldViewProps, VideoDocum
this._youtubeReactionDisposer && this._youtubeReactionDisposer();
this._reactionDisposer = reaction(() => 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 c56422076..de764346f 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<IViewerProps, PdfDocument
}
copy = (e: ClipboardEvent) => {
- 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<IViewerProps, PdfDocument
@action
onPointerDown = (e: React.PointerEvent): void => {
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<IViewerProps, PdfDocument
this._downY = e.clientY;
addStyleSheetRule(PDFViewer._annotationStyle, "pdfAnnotation", { "pointer-events": "none" });
if ((this.Document.scale || 1) !== 1) return;
- if ((e.button !== 0 || e.altKey) && this.active()) {
+ if ((e.button !== 0 || e.altKey) && this.active(true)) {
this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true);
}
this._marqueeing = false;
- if (!e.altKey && e.button === 0 && this.active()) {
+ if (!e.altKey && e.button === 0 && this.active(true)) {
// clear out old marquees and initialize menu for new selection
PDFMenu.Instance.StartDrag = this.startDrag;
PDFMenu.Instance.Highlight = this.highlight;
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index bae7f6a91..271b7cfd3 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -15,6 +15,7 @@ import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast, ToConstruct
import { deleteProperty, getField, getter, makeEditable, makeReadOnly, setter, updateFunction } from "./util";
import { intersectRect } from "../Utils";
import { UndoManager } from "../client/util/UndoManager";
+import { computedFn } from "mobx-utils";
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -637,13 +638,12 @@ export namespace Doc {
}
export class DocBrush {
- @observable BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+ BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
}
const brushManager = new DocBrush();
export class DocData {
@observable _user_doc: Doc = undefined!;
- @observable BrushedDoc: ObservableMap<Doc, boolean> = 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);