aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Utils.ts2
-rw-r--r--src/client/views/AntimodeMenu.scss29
-rw-r--r--src/client/views/AntimodeMenu.tsx114
-rw-r--r--src/client/views/MainView.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx52
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx206
-rw-r--r--src/client/views/nodes/DocumentView.tsx8
-rw-r--r--src/client/views/pdf/PDFMenu.scss40
-rw-r--r--src/client/views/pdf/PDFMenu.tsx128
10 files changed, 358 insertions, 231 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 4b892aa70..ca134e165 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -278,6 +278,8 @@ export function returnEmptyString() { return ""; }
export function emptyFunction() { }
+export function unimplementedFunction() { throw new Error("This function is not implemented, but should be."); }
+
export type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
export type Predicate<K, V> = (entry: [K, V]) => boolean;
diff --git a/src/client/views/AntimodeMenu.scss b/src/client/views/AntimodeMenu.scss
new file mode 100644
index 000000000..f3da5f284
--- /dev/null
+++ b/src/client/views/AntimodeMenu.scss
@@ -0,0 +1,29 @@
+.antimodeMenu-cont {
+ position: absolute;
+ z-index: 10000;
+ height: 35px;
+ background: #323232;
+ box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
+ border-radius: 0px 6px 6px 6px;
+ overflow: hidden;
+ display: flex;
+
+ .antimodeMenu-button {
+ background-color: transparent;
+ width: 35px;
+ height: 35px;
+ }
+
+ .antimodeMenu-button:hover {
+ background-color: #121212;
+ }
+
+ .antimodeMenu-dragger {
+ height: 100%;
+ transition: width .2s;
+ background-image: url("https://logodix.com/logo/1020374.png");
+ background-size: 90% 100%;
+ background-repeat: no-repeat;
+ background-position: left center;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
new file mode 100644
index 000000000..9b0ba6312
--- /dev/null
+++ b/src/client/views/AntimodeMenu.tsx
@@ -0,0 +1,114 @@
+import React = require("react");
+import { observer } from "mobx-react";
+import { observable, action } from "mobx";
+import "./AntimodeMenu.scss";
+
+export default abstract class AntimodeMenu extends React.Component {
+ protected _offsetY: number = 0;
+ protected _offsetX: number = 0;
+ protected _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
+ protected _dragging: boolean = false;
+
+ @observable protected _top: number = -300;
+ @observable protected _left: number = -300;
+ @observable protected _opacity: number = 1;
+ @observable protected _transition: string = "opacity 0.5s";
+ @observable protected _transitionDelay: string = "";
+
+ @observable public Pinned: boolean = false;
+
+ @action
+ public jumpTo = (x: number, y: number, forceJump: boolean = false) => {
+ if (!this.Pinned || forceJump) {
+ this._transition = this._transitionDelay = "";
+ this._opacity = 1;
+ this._left = x;
+ this._top = y;
+ }
+ }
+
+ @action
+ public fadeOut = (forceOut: boolean) => {
+ if (!this.Pinned) {
+ if (this._opacity === 0.2) {
+ this._transition = "opacity 0.1s";
+ this._transitionDelay = "";
+ this._opacity = 0;
+ this._left = this._top = -300;
+ }
+
+ if (forceOut) {
+ this._transition = "";
+ this._transitionDelay = "";
+ this._opacity = 0;
+ this._left = this._top = -300;
+ }
+ }
+ }
+
+ @action
+ protected pointerLeave = (e: React.PointerEvent) => {
+ if (!this.Pinned) {
+ this._transition = "opacity 0.5s";
+ this._transitionDelay = "1s";
+ this._opacity = 0.2;
+ setTimeout(() => this.fadeOut(false), 3000);
+ }
+ }
+
+ @action
+ protected pointerEntered = (e: React.PointerEvent) => {
+ this._transition = "opacity 0.1s";
+ this._transitionDelay = "";
+ this._opacity = 1;
+ }
+
+ @action
+ protected togglePin = (e: React.MouseEvent) => {
+ this.Pinned = !this.Pinned;
+ }
+
+ protected dragStart = (e: React.PointerEvent) => {
+ document.removeEventListener("pointermove", this.dragging);
+ document.addEventListener("pointermove", this.dragging);
+ document.removeEventListener("pointerup", this.dragEnd);
+ document.addEventListener("pointerup", this.dragEnd);
+
+ this._offsetX = this._mainCont.current!.getBoundingClientRect().width - e.nativeEvent.offsetX;
+ this._offsetY = e.nativeEvent.offsetY;
+
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ @action
+ protected dragging = (e: PointerEvent) => {
+ this._left = e.pageX - this._offsetX;
+ this._top = e.pageY - this._offsetY;
+
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ protected dragEnd = (e: PointerEvent) => {
+ document.removeEventListener("pointermove", this.dragging);
+ document.removeEventListener("pointerup", this.dragEnd);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ protected handleContextMenu = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ protected getElement(buttons: JSX.Element[]) {
+ return (
+ <div className="antimodeMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
+ style={{ left: this._left, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}>
+ {buttons}
+ <div className="antimodeMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} />
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 660b42290..53951224c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,6 +1,5 @@
import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV } from '@fortawesome/free-solid-svg-icons';
-import { faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faTv, faUndoAlt } from '@fortawesome/free-solid-svg-icons';
+import { faLink, faArrowDown, faArrowUp, faBolt, faCaretUp, faCat, faCheck, faClone, faCloudUploadAlt, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faLongArrowAltRight, faMusic, faObjectGroup, faPause, faPenNib, faPlay, faPortrait, faRedoAlt, faThumbtack, faTree, faUndoAlt, faTv, faChevronRight, faEllipsisV, faCompressArrowsAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -42,6 +41,7 @@ import { OverlayView } from './OverlayView';
import PDFMenu from './pdf/PDFMenu';
import { PreviewCursor } from './PreviewCursor';
import { FilterBox } from './search/FilterBox';
+import MarqueeOptionsMenu from './collections/collectionFreeForm/MarqueeOptionsMenu';
@observer
export class MainView extends React.Component {
@@ -202,6 +202,7 @@ export class MainView extends React.Component {
library.add(faMusic);
library.add(faTree);
library.add(faPlay);
+ library.add(faCompressArrowsAlt);
library.add(faPause);
library.add(faClone);
library.add(faCut);
@@ -687,6 +688,7 @@ export class MainView extends React.Component {
{this.nodesMenu()}
{this.miscButtons}
<PDFMenu />
+ <MarqueeOptionsMenu />
<MainOverlayTextBox firstinstance={true} />
<OverlayView />
</div >
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2f9d2606f..54d5b95b6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -39,6 +39,7 @@ import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
import { InteractionUtils } from "../../../util/InteractionUtils";
+import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload);
@@ -289,6 +290,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) {
@action
pan = (e: PointerEvent | React.Touch): void => {
+ // I think it makes sense for the marquee menu to go away when panned. -syip2
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+
let x = this.Document.panX || 0;
let y = this.Document.panY || 0;
let docs = this.childLayoutPairs.map(pair => pair.layout);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
new file mode 100644
index 000000000..7c5b87283
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -0,0 +1,52 @@
+import React = require("react")
+import AntimodeMenu from "../../AntimodeMenu";
+import { observer } from "mobx-react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { unimplementedFunction } from "../../../../Utils";
+
+@observer
+export default class MarqueeOptionsMenu extends AntimodeMenu {
+ static Instance: MarqueeOptionsMenu;
+
+ public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public showMarquee: () => void = unimplementedFunction;
+ public hideMarquee: () => void = unimplementedFunction;
+
+ constructor(props: Readonly<{}>) {
+ super(props);
+
+ MarqueeOptionsMenu.Instance = this;
+ }
+
+ render() {
+ let buttons = [
+ <button
+ className="antimodeMenu-button"
+ title="Create a Collection"
+ onPointerDown={this.createCollection}
+ onPointerLeave={this.hideMarquee}
+ onPointerEnter={this.showMarquee}>
+ <FontAwesomeIcon icon="object-group" size="lg" />
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Summarize Documents"
+ onPointerDown={this.summarize}
+ onPointerLeave={this.hideMarquee}
+ onPointerEnter={this.showMarquee}>
+ <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
+ </button>,
+ <button
+ className="antimodeMenu-button"
+ title="Delete Documents"
+ onPointerDown={this.delete}
+ onPointerLeave={this.hideMarquee}
+ onPointerEnter={this.showMarquee}>
+ <FontAwesomeIcon icon="trash-alt" size="lg" />
+ </button>,
+ ]
+ return this.getElement(buttons);
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 82193aefa..ad4da7733 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -19,6 +19,7 @@ import { CollectionViewType } from "../CollectionBaseView";
import { CollectionFreeFormView } from "./CollectionFreeFormView";
import "./MarqueeView.scss";
import React = require("react");
+import MarqueeOptionsMenu from "./MarqueeOptionsMenu";
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -195,6 +196,14 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
}
this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]);
}
+ if (!this._commandExecuted) {
+ MarqueeOptionsMenu.Instance.createCollection = this.collection;
+ MarqueeOptionsMenu.Instance.delete = this.delete;
+ MarqueeOptionsMenu.Instance.summarize = this.summary;
+ MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
+ MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
+ MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
+ }
this.cleanupInteractions(true);
if (e.altKey) {
@@ -255,6 +264,117 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
Doc.fieldExtensionDoc(cprops.Document, cprops.fieldKey).ink = value;
}
+ @action
+ showMarquee = () => {
+ this._visible = true;
+ }
+
+ @action
+ hideMarquee = () => {
+ this._visible = false;
+ }
+
+ @action
+ delete = () => {
+ this.marqueeSelect(false).map(d => this.props.removeDocument(d));
+ if (this.ink) {
+ this.marqueeInkDelete(this.ink.inkData);
+ }
+ SelectionManager.DeselectAll();
+ this.cleanupInteractions(false);
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ }
+
+ getCollection = (selected: Doc[]) => {
+ let bounds = this.Bounds;
+ let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
+ "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
+ let colorPalette = Cast(this.props.container.props.Document.colorPalette, listSpec("string"));
+ if (!colorPalette) this.props.container.props.Document.colorPalette = new List<string>(defaultPalette);
+ let palette = Array.from(Cast(this.props.container.props.Document.colorPalette, listSpec("string")) as string[]);
+ let usedPaletted = new Map<string, number>();
+ [...this.props.activeDocuments(), this.props.container.props.Document].map(child => {
+ let bg = StrCast(child.layout instanceof Doc ? child.layout.backgroundColor : child.backgroundColor);
+ if (palette.indexOf(bg) !== -1) {
+ palette.splice(palette.indexOf(bg), 1);
+ if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
+ else usedPaletted.set(bg, 1);
+ }
+ });
+ usedPaletted.delete("#f1efeb");
+ usedPaletted.delete("white");
+ usedPaletted.delete("rgba(255,255,255,1)");
+ let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
+ let chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
+ let inkData = this.ink ? this.ink.inkData : undefined;
+ let newCollection = Docs.Create.FreeformDocument(selected, {
+ x: bounds.left,
+ y: bounds.top,
+ panX: 0,
+ panY: 0,
+ backgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
+ defaultBackgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
+ width: bounds.width,
+ height: bounds.height,
+ title: "a nested collection",
+ });
+ let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
+ dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
+ this.marqueeInkDelete(inkData);
+ return newCollection;
+ }
+
+ @action
+ collection = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ let bounds = this.Bounds;
+ let selected = this.marqueeSelect(false);
+ if (e instanceof KeyboardEvent ? e.key === "c" : true) {
+ selected.map(d => {
+ this.props.removeDocument(d);
+ d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
+ d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
+ d.page = -1;
+ return d;
+ });
+ }
+ let newCollection = this.getCollection(selected);
+ this.props.addDocument(newCollection, false);
+ this.props.selectDocuments([newCollection]);
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ }
+
+ @action
+ summary = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ let bounds = this.Bounds;
+ let selected = this.marqueeSelect(false);
+ let newCollection = this.getCollection(selected);
+
+ selected.map(d => {
+ this.props.removeDocument(d);
+ d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
+ d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
+ d.page = -1;
+ return d;
+ });
+ newCollection.chromeStatus = "disabled";
+ let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
+ Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
+ newCollection.x = bounds.left + bounds.width;
+ Doc.GetProto(newCollection).summaryDoc = summary;
+ Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`);
+ if (e instanceof KeyboardEvent ? e.key === "s" : true) { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
+ let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
+ container.viewType = CollectionViewType.Stacking;
+ container.autoHeight = true;
+ Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "onRight"
+ this.props.addLiveTextDocument(container);
+ } else if (e instanceof KeyboardEvent ? e.key === "S" : false) { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them
+ Doc.GetProto(summary).maximizeLocation = "inTab"; // or "inPlace", or "onRight"
+ this.props.addLiveTextDocument(summary);
+ }
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ }
+
@undoBatch
@action
marqueeCommand = async (e: KeyboardEvent) => {
@@ -265,12 +385,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
this._commandExecuted = true;
e.stopPropagation();
(e as any).propagationIsStopped = true;
- this.marqueeSelect(false).map(d => this.props.removeDocument(d));
- if (this.ink) {
- this.marqueeInkDelete(this.ink.inkData);
- }
- SelectionManager.DeselectAll();
- this.cleanupInteractions(false);
+ this.delete();
e.stopPropagation();
}
if (e.key === "c" || e.key === "s" || e.key === "S") {
@@ -278,80 +393,12 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
e.stopPropagation();
e.preventDefault();
(e as any).propagationIsStopped = true;
- let bounds = this.Bounds;
- let selected = this.marqueeSelect(false);
if (e.key === "c") {
- selected.map(d => {
- this.props.removeDocument(d);
- d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.page = -1;
- return d;
- });
+ this.collection(e);
}
- let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)",
- "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",];
- let colorPalette = Cast(this.props.container.props.Document.colorPalette, listSpec("string"));
- if (!colorPalette) this.props.container.props.Document.colorPalette = new List<string>(defaultPalette);
- let palette = Array.from(Cast(this.props.container.props.Document.colorPalette, listSpec("string")) as string[]);
- let usedPaletted = new Map<string, number>();
- [...this.props.activeDocuments(), this.props.container.props.Document].map(child => {
- let bg = StrCast(child.layout instanceof Doc ? child.layout.backgroundColor : child.backgroundColor);
- if (palette.indexOf(bg) !== -1) {
- palette.splice(palette.indexOf(bg), 1);
- if (usedPaletted.get(bg)) usedPaletted.set(bg, usedPaletted.get(bg)! + 1);
- else usedPaletted.set(bg, 1);
- }
- });
- usedPaletted.delete("#f1efeb");
- usedPaletted.delete("white");
- usedPaletted.delete("rgba(255,255,255,1)");
- let usedSequnce = Array.from(usedPaletted.keys()).sort((a, b) => usedPaletted.get(a)! < usedPaletted.get(b)! ? -1 : usedPaletted.get(a)! > usedPaletted.get(b)! ? 1 : 0);
- let chosenColor = (usedPaletted.size === 0) ? "white" : palette.length ? palette[0] : usedSequnce[0];
- let inkData = this.ink ? this.ink.inkData : undefined;
- let newCollection = Docs.Create.FreeformDocument(selected, {
- x: bounds.left,
- y: bounds.top,
- panX: 0,
- panY: 0,
- backgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
- defaultBackgroundColor: this.props.isAnnotationOverlay ? undefined : chosenColor,
- width: bounds.width,
- height: bounds.height,
- title: "a nested collection",
- });
- let dataExtensionField = Doc.CreateDocumentExtensionForField(newCollection, "data");
- dataExtensionField.ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
- this.marqueeInkDelete(inkData);
if (e.key === "s" || e.key === "S") {
- selected.map(d => {
- this.props.removeDocument(d);
- d.x = NumCast(d.x) - bounds.left - bounds.width / 2;
- d.y = NumCast(d.y) - bounds.top - bounds.height / 2;
- d.page = -1;
- return d;
- });
- newCollection.chromeStatus = "disabled";
- let summary = Docs.Create.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, autoHeight: true, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" });
- Doc.GetProto(summary).summarizedDocs = new List<Doc>([newCollection]);
- newCollection.x = bounds.left + bounds.width;
- Doc.GetProto(newCollection).summaryDoc = summary;
- Doc.GetProto(newCollection).title = ComputedField.MakeFunction(`summaryTitle(this);`);
- if (e.key === "s") { // summary is wrapped in an expand/collapse container that also contains the summarized documents in a free form view.
- let container = Docs.Create.FreeformDocument([summary, newCollection], { x: bounds.left, y: bounds.top, width: 300, height: 200, chromeStatus: "disabled", title: "-summary-" });
- container.viewType = CollectionViewType.Stacking;
- container.autoHeight = true;
- Doc.GetProto(summary).maximizeLocation = "inPlace"; // or "onRight"
- this.props.addLiveTextDocument(container);
- } else if (e.key === "S") { // the summary stands alone, but is linked to a collection of the summarized documents - set the OnCLick behavior to link follow to access them
- Doc.GetProto(summary).maximizeLocation = "inTab"; // or "inPlace", or "onRight"
- this.props.addLiveTextDocument(summary);
- }
- }
- else {
- this.props.addDocument(newCollection, false);
- this.props.selectDocuments([newCollection]);
+ this.summary(e);
}
this.cleanupInteractions(false);
}
@@ -435,8 +482,13 @@ export class MarqueeView extends React.Component<MarqueeViewProps>
@computed
get marqueeDiv() {
let v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ /**
+ * @RE - The commented out span below
+ * This contains the "C for collection, ..." text on marquees.
+ * Commented out by syip2 when the marquee menu was added.
+ */
return <div className="marquee" style={{ width: `${Math.abs(v[0])}`, height: `${Math.abs(v[1])}`, zIndex: 2000 }} >
- <span className="marquee-legend" />
+ {/* <span className="marquee-legend" /> */}
</div>;
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index bf90e2d08..f74023f42 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -41,6 +41,7 @@ import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { ImageField } from '../../../new_fields/URLField';
import SharingManager from '../../util/SharingManager';
import { Scripting } from '../../util/Scripting';
+import { InteractionUtils } from '../../util/InteractionUtils';
library.add(fa.faTrash);
library.add(fa.faShare);
@@ -248,6 +249,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
document.addEventListener("pointermove", this.onPointerMove);
document.addEventListener("pointerup", this.onPointerUp);
}
+
onPointerMove = (e: PointerEvent): void => {
if (e.cancelBubble && this.active) {
document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView)
@@ -264,6 +266,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e.preventDefault();
}
}
+
onPointerUp = (e: PointerEvent): void => {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
@@ -611,11 +614,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
DataDoc={this.props.DataDoc} />);
}
- handle1Pointer = (e: TouchEvent) => {
- e.stopPropagation();
- e.preventDefault();
- }
-
render() {
let animDims = this.props.Document.animateToDimensions ? Array.from(Cast(this.props.Document.animateToDimensions, listSpec("number"))!) : undefined;
const ruleColor = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleColor_" + this.Document.heading]) : undefined;
diff --git a/src/client/views/pdf/PDFMenu.scss b/src/client/views/pdf/PDFMenu.scss
index b06d19c53..3c08ba80d 100644
--- a/src/client/views/pdf/PDFMenu.scss
+++ b/src/client/views/pdf/PDFMenu.scss
@@ -1,36 +1,6 @@
-.pdfMenu-cont {
- position: absolute;
- z-index: 10000;
- height: 35px;
- background: #323232;
- box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
- border-radius: 0px 6px 6px 6px;
- overflow: hidden;
- display: flex;
-
- .pdfMenu-button {
- background-color: transparent;
- width: 35px;
- height: 35px;
- }
-
- .pdfMenu-button:hover {
- background-color: #121212;
- }
-
- .pdfMenu-dragger {
- height: 100%;
- transition: width .2s;
- background-image: url("https://logodix.com/logo/1020374.png");
- background-size: 90% 100%;
- background-repeat: no-repeat;
- background-position: left center;
- }
-
- .pdfMenu-addTag {
- display: grid;
- width: 200px;
- padding: 5px;
- grid-template-columns: 90px 20px 90px;
- }
+.pdfMenu-addTag {
+ display: grid;
+ width: 200px;
+ padding: 5px;
+ grid-template-columns: 90px 20px 90px;
} \ No newline at end of file
diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx
index e62542014..1997ee0f5 100644
--- a/src/client/views/pdf/PDFMenu.tsx
+++ b/src/client/views/pdf/PDFMenu.tsx
@@ -3,39 +3,29 @@ import "./PDFMenu.scss";
import { observable, action, } from "mobx";
import { observer } from "mobx-react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { emptyFunction, returnFalse } from "../../../Utils";
-import { Doc } from "../../../new_fields/Doc";
+import { unimplementedFunction, returnFalse } from "../../../Utils";
+import AntimodeMenu from "../AntimodeMenu";
@observer
-export default class PDFMenu extends React.Component {
+export default class PDFMenu extends AntimodeMenu {
static Instance: PDFMenu;
- private _offsetY: number = 0;
- private _offsetX: number = 0;
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _commentCont = React.createRef<HTMLButtonElement>();
private _snippetButton: React.RefObject<HTMLButtonElement> = React.createRef();
- private _dragging: boolean = false;
- @observable private _top: number = -300;
- @observable private _left: number = -300;
- @observable private _opacity: number = 1;
- @observable private _transition: string = "opacity 0.5s";
- @observable private _transitionDelay: string = "";
@observable private _keyValue: string = "";
@observable private _valueValue: string = "";
@observable private _added: boolean = false;
@observable public Highlighting: boolean = false;
@observable public Status: "pdf" | "annotation" | "snippet" | "" = "";
- @observable public Pinned: boolean = false;
- public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = emptyFunction;
- public Highlight: (color: string) => void = emptyFunction;
- public Delete: () => void = emptyFunction;
- public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction;
+ public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
+ public Highlight: (color: string) => void = unimplementedFunction;
+ public Delete: () => void = unimplementedFunction;
+ public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = unimplementedFunction;
public AddTag: (key: string, value: string) => boolean = returnFalse;
- public PinToPres: () => void = emptyFunction;
+ public PinToPres: () => void = unimplementedFunction;
public Marquee: { left: number; top: number; width: number; height: number; } | undefined;
constructor(props: Readonly<{}>) {
@@ -73,86 +63,11 @@ export default class PDFMenu extends React.Component {
}
@action
- jumpTo = (x: number, y: number, forceJump: boolean = false) => {
- if (!this.Pinned || forceJump) {
- this._transition = this._transitionDelay = "";
- this._opacity = 1;
- this._left = x;
- this._top = y;
- }
- }
-
- @action
- fadeOut = (forceOut: boolean) => {
- if (!this.Pinned) {
- if (this._opacity === 0.2) {
- this._transition = "opacity 0.1s";
- this._transitionDelay = "";
- this._opacity = 0;
- this._left = this._top = -300;
- }
-
- if (forceOut) {
- this._transition = "";
- this._transitionDelay = "";
- this._opacity = 0;
- this._left = this._top = -300;
- }
- }
- }
-
- @action
- pointerLeave = (e: React.PointerEvent) => {
- if (!this.Pinned) {
- this._transition = "opacity 0.5s";
- this._transitionDelay = "1s";
- this._opacity = 0.2;
- setTimeout(() => this.fadeOut(false), 3000);
- }
- }
-
- @action
- pointerEntered = (e: React.PointerEvent) => {
- this._transition = "opacity 0.1s";
- this._transitionDelay = "";
- this._opacity = 1;
- }
-
- @action
togglePin = (e: React.MouseEvent) => {
this.Pinned = !this.Pinned;
!this.Pinned && (this.Highlighting = false);
}
- dragStart = (e: React.PointerEvent) => {
- document.removeEventListener("pointermove", this.dragging);
- document.addEventListener("pointermove", this.dragging);
- document.removeEventListener("pointerup", this.dragEnd);
- document.addEventListener("pointerup", this.dragEnd);
-
- this._offsetX = this._mainCont.current!.getBoundingClientRect().width - e.nativeEvent.offsetX;
- this._offsetY = e.nativeEvent.offsetY;
-
- e.stopPropagation();
- e.preventDefault();
- }
-
- @action
- dragging = (e: PointerEvent) => {
- this._left = e.pageX - this._offsetX;
- this._top = e.pageY - this._offsetY;
-
- e.stopPropagation();
- e.preventDefault();
- }
-
- dragEnd = (e: PointerEvent) => {
- document.removeEventListener("pointermove", this.dragging);
- document.removeEventListener("pointerup", this.dragEnd);
- e.stopPropagation();
- e.preventDefault();
- }
-
@action
highlightClicked = (e: React.MouseEvent) => {
if (!this.Pinned) {
@@ -168,11 +83,6 @@ export default class PDFMenu extends React.Component {
this.Delete();
}
- handleContextMenu = (e: React.MouseEvent) => {
- e.stopPropagation();
- e.preventDefault();
- }
-
snippetStart = (e: React.PointerEvent) => {
document.removeEventListener("pointermove", this.snippetDrag);
document.addEventListener("pointermove", this.snippetDrag);
@@ -223,33 +133,27 @@ export default class PDFMenu extends React.Component {
render() {
let buttons = this.Status === "pdf" || this.Status === "snippet" ?
[
- <button key="1" className="pdfMenu-button" title="Click to Highlight" onClick={this.highlightClicked} style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
+ <button key="1" className="antimodeMenu-button" title="Click to Highlight" onClick={this.highlightClicked} style={this.Highlighting ? { backgroundColor: "#121212" } : {}}>
<FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} /></button>,
- <button key="2" className="pdfMenu-button" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown}>
+ <button key="2" className="antimodeMenu-button" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown}>
<FontAwesomeIcon icon="comment-alt" size="lg" /></button>,
- <button key="3" className="pdfMenu-button" title="Drag to Snippetize Selection" style={{ display: this.Status === "snippet" ? "" : "none" }} onPointerDown={this.snippetStart} ref={this._snippetButton}>
+ <button key="3" className="antimodeMenu-button" title="Drag to Snippetize Selection" style={{ display: this.Status === "snippet" ? "" : "none" }} onPointerDown={this.snippetStart} ref={this._snippetButton}>
<FontAwesomeIcon icon="cut" size="lg" /></button>,
- <button key="4" className="pdfMenu-button" title="Pin Menu" onClick={this.togglePin} style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
+ <button key="4" className="antimodeMenu-button" title="Pin Menu" onClick={this.togglePin} style={this.Pinned ? { backgroundColor: "#121212" } : {}}>
<FontAwesomeIcon icon="thumbtack" size="lg" style={{ transition: "transform 0.1s", transform: this.Pinned ? "rotate(45deg)" : "" }} /> </button>
] : [
- <button key="5" className="pdfMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}>
+ <button key="5" className="antimodeMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}>
<FontAwesomeIcon icon="trash-alt" size="lg" /></button>,
- <button key="6" className="pdfMenu-button" title="Pin to Presentation" onPointerDown={this.PinToPres}>
+ <button key="6" className="antimodeMenu-button" title="Pin to Presentation" onPointerDown={this.PinToPres}>
<FontAwesomeIcon icon="map-pin" size="lg" /></button>,
<div key="7" className="pdfMenu-addTag" >
<input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} />
<input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} />
</div>,
- <button key="8" className="pdfMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}>
+ <button key="8" className="antimodeMenu-button" title={`Add tag: ${this._keyValue} with value: ${this._valueValue}`} onPointerDown={this.addTag}>
<FontAwesomeIcon style={{ transition: "all .2s" }} color={this._added ? "#42f560" : "white"} icon="check" size="lg" /></button>,
];
- return (
- <div className="pdfMenu-cont" onPointerLeave={this.pointerLeave} onPointerEnter={this.pointerEntered} ref={this._mainCont} onContextMenu={this.handleContextMenu}
- style={{ left: this._left, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}>
- {buttons}
- <div className="pdfMenu-dragger" onPointerDown={this.dragStart} style={{ width: this.Pinned ? "20px" : "0px" }} />
- </div >
- );
+ return this.getElement(buttons);
}
} \ No newline at end of file