aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-09-26 23:13:42 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-09-26 23:13:42 -0400
commit2553bafde4a6d492512f0a5dfd25f5bd407af929 (patch)
treea8d144840c7de9255de1ec121ed66e350245ebf0 /src/client/views/nodes
parent2e61db73a1fcba3b87cc84a7d1af51617f6a02f6 (diff)
parent087f96367936f33743a304b3446b24074290c367 (diff)
merged with master again
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx24
-rw-r--r--src/client/views/nodes/DocumentView.tsx19
-rw-r--r--src/client/views/nodes/FieldView.tsx1
-rw-r--r--src/client/views/nodes/FormattedTextBox.scss20
-rw-r--r--src/client/views/nodes/FormattedTextBox.tsx83
-rw-r--r--src/client/views/nodes/PDFBox.scss130
-rw-r--r--src/client/views/nodes/PDFBox.tsx224
-rw-r--r--src/client/views/nodes/PresBox.tsx6
8 files changed, 308 insertions, 199 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 9685f9bca..49b6f22db 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -12,6 +12,7 @@ import { Doc, WidthSym, HeightSym } from "../../../new_fields/Doc";
import { random } from "animejs";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
+ dataProvider?: (doc: Doc, dataDoc?: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined;
x?: number;
y?: number;
width?: number;
@@ -32,14 +33,14 @@ export const PositionDocument = makeInterface(documentSchema, positionSchema);
@observer
export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, PositionDocument>(PositionDocument) {
_disposer: IReactionDisposer | undefined = undefined;
- @computed get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${random(-1, 1) * this.props.jitterRotation}deg)`; }
- @computed get X() { return this._animPos !== undefined ? this._animPos[0] : this.renderScriptDim ? this.renderScriptDim.x : this.props.x !== undefined ? this.props.x : this.Document.x || 0; }
- @computed get Y() { return this._animPos !== undefined ? this._animPos[1] : this.renderScriptDim ? this.renderScriptDim.y : this.props.y !== undefined ? this.props.y : this.Document.y || 0; }
- @computed get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.Document[WidthSym](); }
- @computed get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.Document[HeightSym](); }
+ 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; }
+ get width() { return this.renderScriptDim ? this.renderScriptDim.width : this.props.width !== undefined ? this.props.width : this.props.dataProvider && this.dataProvider ? this.dataProvider.width : this.props.Document[WidthSym](); }
+ get height() { return this.renderScriptDim ? this.renderScriptDim.height : this.props.height !== undefined ? this.props.height : this.props.dataProvider && this.dataProvider ? this.dataProvider.height : this.props.Document[HeightSym](); }
+ @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 nativeWidth() { return FieldValue(this.Document.nativeWidth, 0); }
@computed get nativeHeight() { return FieldValue(this.Document.nativeHeight, 0); }
- @computed get scaleToOverridingWidth() { return this.width / FieldValue(this.Document.width, this.width); }
@computed get renderScriptDim() {
if (this.Document.renderScript) {
@@ -72,7 +73,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
panelHeight = () => this.props.PanelHeight();
getTransform = (): Transform => this.props.ScreenToLocalTransform()
.translate(-this.X, -this.Y)
- .scale(1 / this.contentScaling()).scale(1 / this.scaleToOverridingWidth)
+ .scale(1 / this.contentScaling())
borderRounding = () => {
let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined;
@@ -98,6 +99,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@observable _animPos: number[] | undefined = undefined;
+ finalPanelWidh = () => this.dataProvider ? this.dataProvider.width : this.panelWidth();
+ finalPanelHeight = () => this.dataProvider ? this.dataProvider.height : this.panelHeight();
+
render() {
return (
<div className="collectionFreeFormDocumentView-container"
@@ -110,7 +114,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
StrCast(this.layoutDoc.boxShadow, ""),
borderRadius: this.borderRounding(),
transform: this.transform,
- transition: this.Document.isAnimating !== undefined ? ".5s ease-in" : this.props.transition ? this.props.transition : StrCast(this.layoutDoc.transition),
+ transition: this.Document.isAnimating !== undefined ? ".5s ease-in" : this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
width: this.width,
height: this.height,
zIndex: this.Document.zIndex || 0,
@@ -119,8 +123,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
ContentScaling={this.contentScaling}
ScreenToLocalTransform={this.getTransform}
backgroundColor={this.clusterColorFunc}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
+ PanelWidth={this.finalPanelWidh}
+ PanelHeight={this.finalPanelHeight}
/>
</div>
);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a6d350ca2..c96f954e9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -4,14 +4,11 @@ import { action, computed, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from "request-promise";
import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc";
-import { Copy, Id } from '../../../new_fields/FieldSymbols';
-import { List } from "../../../new_fields/List";
-import { ObjectField } from "../../../new_fields/ObjectField";
+import { Id } from '../../../new_fields/FieldSymbols';
import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema";
import { ScriptField } from '../../../new_fields/ScriptField';
-import { BoolCast, Cast, FieldValue, NumCast, PromiseValue, StrCast } from "../../../new_fields/Types";
+import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from "../../../new_fields/Types";
import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils";
-import { RouteStore } from '../../../server/RouteStore';
import { emptyFunction, returnTrue, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Docs, DocUtils } from "../../documents/Documents";
@@ -42,10 +39,8 @@ import React = require("react");
import { DocumentType } from '../../documents/DocumentTypes';
import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
import { ImageField } from '../../../new_fields/URLField';
-import { IDisposable } from '../../northstar/utils/IDisposable';
import SharingManager from '../../util/SharingManager';
import { Scripting } from '../../util/Scripting';
-const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
library.add(fa.faTrash);
library.add(fa.faShare);
@@ -262,7 +257,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._hitTemplateDrag = true;
}
}
- if (this.active) e.stopPropagation(); // events stop at the lowest document that is active.
+ if (this.active && e.button === 0) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag);
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
document.addEventListener("pointermove", this.onPointerMove);
@@ -270,11 +265,11 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
onPointerMove = (e: PointerEvent): void => {
if (e.cancelBubble && this.active) {
- document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView)
}
- else if (!e.cancelBubble && this.active) {
+ else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive())) {
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
- if (!e.altKey && !this.topMost && e.buttons === 1 && !BoolCast(this.Document.lockedPosition)) {
+ if (!e.altKey && !this.topMost && e.buttons === 1 && !this.Document.lockedPosition) {
document.removeEventListener("pointermove", this.onPointerMove);
document.removeEventListener("pointerup", this.onPointerUp);
this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag);
@@ -361,7 +356,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true);
// const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView);
de.data.linkSourceDocument !== this.props.Document &&
- (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc));
+ (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc, undefined, "in-text link being created")); // TODODO this is where in text links get passed
}
}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 49fc2263d..ec1b03a40 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -50,7 +50,6 @@ export interface FieldViewProps {
PanelWidth: () => number;
PanelHeight: () => number;
setVideoBox?: (player: VideoBox) => void;
- setPdfBox?: (player: PDFBox) => void;
ContentScaling: () => number;
ChromeHeight?: () => number;
}
diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss
index 0d7277cbe..45e516015 100644
--- a/src/client/views/nodes/FormattedTextBox.scss
+++ b/src/client/views/nodes/FormattedTextBox.scss
@@ -164,13 +164,13 @@ ol { counter-reset: deci1 0;}
.upper-alpha-ol {counter-reset: ualph; p { display: inline }; font-size: 18 }
.lower-roman-ol {counter-reset: lroman; p { display: inline }; font-size: 14; }
.lower-alpha-ol {counter-reset: lalpha; p { display: inline }; font-size: 10;}
-.decimal1:before { content: counter(deci1) ")"; counter-increment: deci1; display:inline-block; width: 30}
-.decimal2:before { content: counter(deci1) "." counter(deci2) ")"; counter-increment: deci2; display:inline-block; width: 35}
-.decimal3:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) ")"; counter-increment: deci3; display:inline-block; width: 35}
-.decimal4:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) ")"; counter-increment: deci4; display:inline-block; width: 40}
-.decimal5:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) ")"; counter-increment: deci5; display:inline-block; width: 40}
-.decimal6:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) "." counter(deci6) ")"; counter-increment: deci6; display:inline-block; width: 45}
-.decimal7:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) "." counter(deci6) "." counter(deci7) ")"; counter-increment: deci7; display:inline-block; width: 50}
-.upper-alpha:before { content: counter(deci1) "." counter(ualph, upper-alpha) ")"; counter-increment: ualph; display:inline-block; width: 35 }
-.lower-roman:before { content: counter(deci1) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) ")"; counter-increment: lroman;display:inline-block; width: 50 }
-.lower-alpha:before { content: counter(deci1) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) "." counter(lalpha, lower-alpha) ")"; counter-increment: lalpha; display:inline-block; width: 35}
+.decimal1:before { content: counter(deci1) ") "; counter-increment: deci1; display:inline-block; min-width: 30;}
+.decimal2:before { content: counter(deci1) "." counter(deci2) ") "; counter-increment: deci2; display:inline-block; min-width: 35}
+.decimal3:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) ") "; counter-increment: deci3; display:inline-block; min-width: 35}
+.decimal4:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) ") "; counter-increment: deci4; display:inline-block; min-width: 40}
+.decimal5:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) ") "; counter-increment: deci5; display:inline-block; min-width: 40}
+.decimal6:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) "." counter(deci6) ") "; counter-increment: deci6; display:inline-block; min-width: 45}
+.decimal7:before { content: counter(deci1) "." counter(deci2) "." counter(deci3) "." counter(deci4) "." counter(deci5) "." counter(deci6) "." counter(deci7) ") "; counter-increment: deci7; display:inline-block; min-width: 50}
+.upper-alpha:before { content: counter(deci1) "." counter(ualph, upper-alpha) ") "; counter-increment: ualph; display:inline-block; min-width: 35 }
+.lower-roman:before { content: counter(deci1) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) ") "; counter-increment: lroman;display:inline-block; min-width: 50 }
+.lower-alpha:before { content: counter(deci1) "." counter(ualph, upper-alpha) "." counter(lroman, lower-roman) "." counter(lalpha, lower-alpha) ") "; counter-increment: lalpha; display:inline-block; min-width: 35}
diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx
index cb9fecfc5..007093826 100644
--- a/src/client/views/nodes/FormattedTextBox.tsx
+++ b/src/client/views/nodes/FormattedTextBox.tsx
@@ -38,7 +38,7 @@ import { DictationManager } from '../../util/DictationManager';
import { ReplaceStep } from 'prosemirror-transform';
import { DocumentType } from '../../documents/DocumentTypes';
import { RichTextUtils } from '../../../new_fields/RichTextUtils';
-import * as _ from "lodash";
+import _ from "lodash";
import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment';
import { inputRules } from 'prosemirror-inputrules';
import { DocumentButtonBar } from '../DocumentButtonBar';
@@ -82,6 +82,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
private _nodeClicked: any;
private _undoTyping?: UndoManager.Batch;
private _searchReactionDisposer?: Lambda;
+ private _scroolToRegionReactionDisposer: Opt<IReactionDisposer>;
private _reactionDisposer: Opt<IReactionDisposer>;
private _textReactionDisposer: Opt<IReactionDisposer>;
private _heightReactionDisposer: Opt<IReactionDisposer>;
@@ -141,6 +142,52 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined);
}
FormattedTextBox.Instance = this;
+ this._scroolToRegionReactionDisposer = reaction(
+ () => StrCast(this.props.Document.scrollToLinkID),
+ async (scrollToLinkID) => {
+ let findLinkFrag = (frag: Fragment, editor: EditorView) => {
+ const nodes: Node[] = [];
+ frag.forEach((node, index) => {
+ let examinedNode = findLinkNode(node, editor);
+ if (examinedNode && examinedNode.textContent) {
+ nodes.push(examinedNode);
+ start += index;
+ }
+ });
+ return { frag: Fragment.fromArray(nodes), start: start };
+ };
+ let findLinkNode = (node: Node, editor: EditorView) => {
+ if (!node.isText) {
+ const content = findLinkFrag(node.content, editor);
+ return node.copy(content.frag);
+ }
+ const marks = [...node.marks];
+ const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.link);
+ return linkIndex !== -1 && scrollToLinkID === marks[linkIndex].attrs.href.replace(/.*\/doc\//, "") ? node : undefined;
+ };
+
+ let start = -1;
+
+ if (this._editorView && scrollToLinkID) {
+ let editor = this._editorView;
+ let ret = findLinkFrag(editor.state.doc.content, editor);
+
+ if (ret.frag.size > 2 && ((!this.props.isOverlay && !this.props.isSelected()) || (this.props.isSelected() && this.props.isOverlay))) {
+ let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start
+ if (ret.frag.firstChild) {
+ selection = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected
+ }
+ editor.dispatch(editor.state.tr.setSelection(new TextSelection(selection.$from, selection.$from)).scrollIntoView());
+ const mark = editor.state.schema.mark(this._editorView.state.schema.marks.search_highlight);
+ setTimeout(() => editor.dispatch(editor.state.tr.addMark(selection.from, selection.to, mark)), 0);
+ setTimeout(() => this.unhighlightSearchTerms(), 2000);
+
+ this.props.Document.scrollToLinkID = undefined;
+ }
+ }
+
+ }
+ );
}
public get CurrentDiv(): HTMLDivElement { return this._ref.current!; }
@@ -697,6 +744,15 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
this._editorView && this._editorView.destroy();
this._editorView = new EditorView(this._proseRef, {
state: field && field.Data ? EditorState.fromJSON(config, JSON.parse(field.Data)) : EditorState.create(config),
+ handleScrollToSelection: (editorView) => {
+ let ref = editorView.domAtPos(editorView.state.selection.from);
+ let refNode = ref.node as any;
+ while (refNode && !("getBoundingClientRect" in refNode)) refNode = refNode.parentElement;
+ let r1 = refNode && refNode.getBoundingClientRect();
+ let r3 = self._ref.current!.getBoundingClientRect();
+ r1 && (self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale);
+ return true;
+ },
dispatchTransaction: this.dispatchTransaction,
nodeViews: {
image(node, view, getPos) { return new ImageResizeView(node, view, getPos, self.props.addDocTab); },
@@ -737,6 +793,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
}
componentWillUnmount() {
+ this._scroolToRegionReactionDisposer && this._scroolToRegionReactionDisposer();
this._rulesReactionDisposer && this._rulesReactionDisposer();
this._reactionDisposer && this._reactionDisposer();
this._proxyReactionDisposer && this._proxyReactionDisposer();
@@ -784,11 +841,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let proto = Doc.GetProto(linkDoc);
let targetContext = await Cast(proto.targetContext, Doc);
let jumpToDoc = await Cast(linkDoc.anchor2, Doc);
+
if (jumpToDoc) {
- // if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
- DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, document => this.props.addDocTab(document, undefined, location ? location : "onRight"), NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page)));
- return;
- // }
+ if (DocumentManager.Instance.getDocumentView(jumpToDoc)) {
+ DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page)));
+ return;
+ }
}
if (targetContext) {
DocumentManager.Instance.jumpToDocument(targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"));
@@ -852,7 +910,18 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
let node = this._editorView!.state.doc.nodeAt(pos.pos);
let node2 = node && node.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined;
if (node === this._nodeClicked && node2 && (node2.type === schema.nodes.ordered_list || node2.type === schema.nodes.list_item)) {
- this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.pos - 1, node2.type, { ...node2.attrs, visibility: !node2.attrs.visibility }));
+ let hit = this._editorView!.domAtPos(pos.pos).node as any;
+ let beforeEle = document.querySelector("." + hit.className) as Element;
+ let before = beforeEle ? window.getComputedStyle(beforeEle, ':before') : undefined;
+ let beforeWidth = before ? Number(before.getPropertyValue('width').replace("px", "")) : undefined;
+ if (beforeWidth && e.nativeEvent.offsetX < beforeWidth) {
+ let ol = this._editorView!.state.doc.nodeAt(pos.pos - 2) ? this._editorView!.state.doc.nodeAt(pos.pos - 2) : undefined;
+ if (ol && ol.type === schema.nodes.ordered_list && !e.shiftKey) {
+ this._editorView!.dispatch(this._editorView!.state.tr.setSelection(new NodeSelection(this._editorView!.state.doc.resolve(pos.pos - 2))));
+ } else {
+ this._editorView!.dispatch(this._editorView!.state.tr.setNodeMarkup(pos.pos - 1, node2.type, { ...node2.attrs, visibility: !node2.attrs.visibility }));
+ }
+ }
}
}
}
@@ -903,7 +972,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe
if (e.key === "Tab" || e.key === "Enter") {
e.preventDefault();
}
- this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() }));
+ this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })));
if (!this._undoTyping) {
this._undoTyping = UndoManager.StartBatch("undoTyping");
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index c88a94c28..69c4397aa 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -3,75 +3,65 @@
display: flex;
flex-direction: row;
height: 100%;
- overflow-y: scroll;
- overflow-x: hidden;
- .pdfBox-scrollHack {
- pointer-events: none;
- }
+ width:100%;
+ overflow: hidden;
+ position:absolute;
+ z-index: -1;
}
.pdfBox-cont {
pointer-events: none;
- .pdfPage-textlayer {
- span {
- pointer-events: none !important;
- user-select: none;
+ .collectionFreeFormView-none {
+ pointer-events: none;
+ }
+ .pdfViewer-text {
+ .textLayer {
+ span {
+ user-select: none;
+ }
}
}
}
.pdfBox-cont-interactive {
pointer-events: all;
- .pdfPage-textlayer {
- span {
- pointer-events: all !important;
- user-select: text;
+ .pdfViewer-text {
+ .textLayer {
+ span {
+ user-select: text;
+ }
}
}
}
-.react-pdf__Page {
- transform-origin: left top;
- position: absolute;
- top: 0;
- left: 0;
-}
-
-.react-pdf__Page__textContent span {
- user-select: text;
-}
-
-.react-pdf__Document {
- position: absolute;
-}
-
.pdfBox-settingsCont {
position: absolute;
right: 0;
- top: 0;
+ top: 3;
+ pointer-events: all;
.pdfBox-settingsButton {
border-bottom-left-radius: 50%;
display: flex;
justify-content: space-evenly;
align-items: center;
- height: 70px;
+ height: 30px;
background: none;
padding: 0;
.pdfBox-settingsButton-arrow {
width: 0;
height: 0;
- border-top: 25px solid transparent;
- border-bottom: 25px solid transparent;
- border-right: 25px solid #121721;
+ border-top: 15px solid transparent;
+ border-bottom: 15px solid transparent;
+ border-right: 15px solid #121721;
transition: all 0.5s;
}
.pdfBox-settingsButton-iconCont {
background: #121721;
- height: 50px;
+ height: 30px;
width: 70px;
display: flex;
justify-content: center;
@@ -86,16 +76,15 @@
}
.pdfBox-settingsFlyout {
- width: 600px;
position: absolute;
background: #323232;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25);
- left: -400px;
+ right: 20px;
border-radius: 7px;
padding: 20px;
display: flex;
flex-direction: column;
- font-size: 30px;
+ font-size: 14px;
transition: all 0.5s;
.pdfBox-settingsFlyout-title {
@@ -108,4 +97,69 @@
grid-template-columns: 47.5% 5% 47.5%;
}
}
-} \ No newline at end of file
+}
+
+.pdfBox-overlayCont {
+ position: absolute;
+ width: 100%;
+ height: 40px;
+ background: #121721;
+ bottom: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 20px;
+ overflow: hidden;
+ transition: left .5s;
+ pointer-events: all;
+
+ .pdfBox-searchBar {
+ width: 70%;
+ font-size: 14px;
+ }
+}
+
+.pdfBox-overlayButton {
+ border-bottom-left-radius: 50%;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ height: 30px;
+ background: none;
+ padding: 0;
+ position: absolute;
+ pointer-events: all;
+
+ .pdfBox-overlayButton-arrow {
+ width: 0;
+ height: 0;
+ border-top: 15px solid transparent;
+ border-bottom: 15px solid transparent;
+ border-right: 15px solid #121721;
+ transition: all 0.5s;
+ }
+
+ .pdfBox-overlayButton-iconCont,
+ .pdfBox-nextIcon,
+ .pdfBox-prevIcon {
+ background: #121721;
+ height: 30px;
+ width: 70px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-left: -2px;
+ border-radius: 3px;
+ }
+}
+
+.pdfBox-overlayButton:hover {
+ background: none;
+}
+
+.pdfBox-nextIcon {
+ left: 20; top: 5; height: 30px; position: absolute;
+}
+.pdfBox-prevIcon {
+ left: 50; top: 5; height: 30px; position: absolute;
+}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 764051d62..eb4803cec 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,12 +1,12 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked, trace } from 'mobx';
import { observer } from "mobx-react";
import * as Pdfjs from "pdfjs-dist";
import "pdfjs-dist/web/pdf_viewer.css";
import 'react-image-lightbox/style.css';
import { Doc, Opt, WidthSym } from "../../../new_fields/Doc";
import { makeInterface } from "../../../new_fields/Schema";
-import { ComputedField, ScriptField } from '../../../new_fields/ScriptField';
+import { ScriptField } from '../../../new_fields/ScriptField';
import { Cast, NumCast } from "../../../new_fields/Types";
import { PdfField } from "../../../new_fields/URLField";
import { KeyCodes } from '../../northstar/utils/KeyCodes';
@@ -19,82 +19,56 @@ import { FieldView, FieldViewProps } from './FieldView';
import { pageSchema } from "./ImageBox";
import "./PDFBox.scss";
import React = require("react");
+import { undoBatch } from '../../util/UndoManager';
type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
@observer
export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocument) {
- public static LayoutString() { return FieldView.LayoutString(PDFBox); }
- private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
- private _reactionDisposer?: IReactionDisposer;
+ public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(PDFBox, "data", fieldExt); }
private _keyValue: string = "";
private _valueValue: string = "";
private _scriptValue: string = "";
+ private _searchString: string = "";
+ private _isChildActive = false;
+ private _pdfViewer: PDFViewer | undefined;
private _keyRef: React.RefObject<HTMLInputElement> = React.createRef();
private _valueRef: React.RefObject<HTMLInputElement> = React.createRef();
private _scriptRef: React.RefObject<HTMLInputElement> = React.createRef();
+ @observable private _searching: boolean = false;
@observable private _flyout: boolean = false;
- @observable private _alt = false;
@observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
+ @observable private _pageControls = false;
@computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); }
-
@computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplate ? this.props.DataDoc : Doc.GetProto(this.props.Document); }
componentDidMount() {
- this.props.setPdfBox && this.props.setPdfBox(this);
-
- this.props.Document.curPage = ComputedField.MakeFunction("Math.floor(Number(this.panY) / Number(this.nativeHeight) + 1)");
-
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
if (pdfUrl instanceof PdfField) {
Pdfjs.getDocument(pdfUrl.url.pathname).promise.then(pdf => runInAction(() => this._pdf = pdf));
}
- this._reactionDisposer = reaction(
- () => this.Document.panY,
- () => this._mainCont.current && this._mainCont.current.scrollTo({ top: this.Document.panY || 0, behavior: "auto" })
- );
- }
-
- componentWillUnmount() {
- this._reactionDisposer && this._reactionDisposer();
- }
-
- public GetPage() {
- return Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
- }
-
- @action
- public BackPage() {
- let cp = Math.ceil((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1;
- cp = cp - 1;
- if (cp > 0) {
- this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
- }
}
-
- @action
- public GotoPage = (p: number) => {
- if (p > 0 && p <= NumCast(this.dataDoc.numPages)) {
- this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0);
+ loaded = (nw: number, nh: number, np: number) => {
+ this.dataDoc.numPages = np;
+ if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) {
+ let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1);
+ this.Document.nativeWidth = nw * 96 / 72;
+ this.Document.nativeHeight = this.Document.nativeHeight ? nw * 96 / 72 * oldaspect : nh * 96 / 72;
}
+ this.Document.height = this.Document[WidthSym]() * (nh / nw);
}
- @action
- public ForwardPage() {
- let cp = this.GetPage() + 1;
- if (cp <= NumCast(this.dataDoc.numPages)) {
- this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0);
- }
- }
-
- @action
- setPanY = (y: number) => {
- this.Document.panY = y;
- }
+ public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); }
+ public prevAnnotation() { this._pdfViewer && this._pdfViewer.prevAnnotation(); }
+ public nextAnnotation() { this._pdfViewer && this._pdfViewer.nextAnnotation(); }
+ public backPage() { this._pdfViewer!.gotoPage(NumCast(this.props.Document.curPage) - 1); }
+ public gotoPage = (p: number) => { this._pdfViewer!.gotoPage(p); };
+ public forwardPage() { this._pdfViewer!.gotoPage(NumCast(this.props.Document.curPage) + 1); }
+ @undoBatch
@action
private applyFilter = () => {
let scriptText = this._scriptValue ? this._scriptValue :
@@ -102,10 +76,6 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
this.props.Document.filterScript = ScriptField.MakeFunction(scriptText);
}
- scrollTo = (y: number) => {
- this._mainCont.current && this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current.offsetHeight / 2), 0), behavior: "auto" });
- }
-
private resetFilters = () => {
this._keyValue = this._valueValue = this._scriptValue = "";
this._keyRef.current && (this._keyRef.current.value = "");
@@ -117,83 +87,103 @@ export class PDFBox extends DocComponent<FieldViewProps, PdfDocument>(PdfDocumen
private newValueChange = (e: React.ChangeEvent<HTMLInputElement>) => this._valueValue = e.currentTarget.value;
private newScriptChange = (e: React.ChangeEvent<HTMLInputElement>) => this._scriptValue = e.currentTarget.value;
+ whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive);
+ active = () => this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0;
+ setPdfViewer = (pdfViewer: PDFViewer) => { this._pdfViewer = pdfViewer; };
+ searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
+
settingsPanel() {
+ let pageBtns = <>
+ <button className="pdfBox-overlayButton-iconCont" key="back" title="Page Back"
+ onPointerDown={(e) => e.stopPropagation()}
+ onClick={() => this.backPage()}
+ style={{ left: 50, top: 5, height: "30px", position: "absolute", pointerEvents: "all" }}>
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" />
+ </button>
+ <button className="pdfBox-overlayButton-iconCont" key="fwd" title="Page Forward"
+ onPointerDown={(e) => e.stopPropagation()}
+ onClick={() => this.forwardPage()}
+ style={{ left: 80, top: 5, height: "30px", position: "absolute", pointerEvents: "all" }}>
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-right"} size="sm" />
+ </button>
+ </>;
return !this.props.active() ? (null) :
- (<div className="pdfBox-settingsCont" onPointerDown={(e) => e.stopPropagation()}>
- <button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings"
- style={{ marginTop: `${this.Document.panY || 0}px` }}>
- <div className="pdfBox-settingsButton-arrow"
- style={{
- borderTop: `25px solid ${this._flyout ? "#121721" : "transparent"}`,
- borderBottom: `25px solid ${this._flyout ? "#121721" : "transparent"}`,
- borderRight: `25px solid ${this._flyout ? "transparent" : "#121721"}`,
- transform: `scaleX(${this._flyout ? -1 : 1})`
- }} />
- <div className="pdfBox-settingsButton-iconCont">
- <FontAwesomeIcon style={{ color: "white" }} icon="cog" size="3x" />
- </div>
+ (<div className="pdfBox-ui" onKeyDown={e => e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true}
+ onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none", position: "absolute", width: "100%", height: "100%", zIndex: 1, pointerEvents: "none" }}>
+ <div className="pdfBox-overlayCont" key="cont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
+ <button className="pdfBox-overlayButton" title="Open Search Bar" />
+ <input className="pdfBox-searchBar" placeholder="Search" onChange={this.searchStringChanged} onKeyDown={e => e.keyCode === KeyCodes.ENTER && this.search(this._searchString, !e.shiftKey)} />
+ <button title="Search" onClick={e => this.search(this._searchString, !e.shiftKey)}>
+ <FontAwesomeIcon icon="search" size="sm" color="white" /></button>
+ <button className="pdfBox-prevIcon " title="Previous Annotation" onClick={e => this.prevAnnotation()} >
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-up"} size="sm" />
+ </button>
+ <button className="pdfBox-nextIcon" title="Next Annotation" onClick={e => this.nextAnnotation()} >
+ <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-down"} size="sm" />
+ </button>
+ </div>
+ <button className="pdfBox-overlayButton" key="search" onClick={action(() => this._searching = !this._searching)} title="Open Search Bar" style={{ bottom: 8, right: 0 }}>
+ <div className="pdfBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()}></div>
+ <div className="pdfBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
+ <FontAwesomeIcon style={{ color: "white", padding: 5 }} icon={this._searching ? "times" : "search"} size="3x" /></div>
</button>
- <div className="pdfBox-settingsFlyout" style={{ left: `${this._flyout ? -600 : 100}px` }} >
- <div className="pdfBox-settingsFlyout-title">
- Annotation View Settings
- </div>
- <div className="pdfBox-settingsFlyout-kvpInput">
- <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange}
- style={{ gridColumn: 1 }} ref={this._keyRef} />
- <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange}
- style={{ gridColumn: 3 }} ref={this._valueRef} />
- </div>
- <div className="pdfBox-settingsFlyout-kvpInput">
- <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} />
- </div>
- <div className="pdfBox-settingsFlyout-kvpInput">
- <button style={{ gridColumn: 1 }} onClick={this.resetFilters}>
- <FontAwesomeIcon style={{ color: "white" }} icon="trash" size="lg" />
- &nbsp; Reset Filters
- </button>
- <button style={{ gridColumn: 3 }} onClick={this.applyFilter}>
- <FontAwesomeIcon style={{ color: "white" }} icon="check" size="lg" />
- &nbsp; Apply
- </button>
+ <input value={`${NumCast(this.props.Document.curPage)}`}
+ onChange={e => this.gotoPage(Number(e.currentTarget.value))}
+ style={{ left: 20, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }}
+ onClick={action(() => this._pageControls = !this._pageControls)} />
+ {this._pageControls ? pageBtns : (null)}
+ <div className="pdfBox-settingsCont" key="settings" onPointerDown={(e) => e.stopPropagation()}>
+ <button className="pdfBox-settingsButton" onClick={action(() => this._flyout = !this._flyout)} title="Open Annotation Settings" >
+ <div className="pdfBox-settingsButton-arrow" style={{ transform: `scaleX(${this._flyout ? -1 : 1})` }} />
+ <div className="pdfBox-settingsButton-iconCont">
+ <FontAwesomeIcon style={{ color: "white", padding: 5 }} icon="cog" size="3x" />
+ </div>
+ </button>
+ <div className="pdfBox-settingsFlyout" style={{ right: `${this._flyout ? 20 : -600}px` }} >
+ <div className="pdfBox-settingsFlyout-title">
+ Annotation View Settings
+ </div>
+ <div className="pdfBox-settingsFlyout-kvpInput">
+ <input placeholder="Key" className="pdfBox-settingsFlyout-input" onChange={this.newKeyChange} style={{ gridColumn: 1 }} ref={this._keyRef} />
+ <input placeholder="Value" className="pdfBox-settingsFlyout-input" onChange={this.newValueChange} style={{ gridColumn: 3 }} ref={this._valueRef} />
+ </div>
+ <div className="pdfBox-settingsFlyout-kvpInput">
+ <input placeholder="Custom Script" onChange={this.newScriptChange} style={{ gridColumn: "1 / 4" }} ref={this._scriptRef} />
+ </div>
+ <div className="pdfBox-settingsFlyout-kvpInput">
+ <button style={{ gridColumn: 1 }} onClick={this.resetFilters}>
+ <FontAwesomeIcon style={{ color: "white" }} icon="trash" size="lg" />
+ &nbsp; Reset Filters
+ </button>
+ <button style={{ gridColumn: 3 }} onClick={this.applyFilter}>
+ <FontAwesomeIcon style={{ color: "white" }} icon="check" size="lg" />
+ &nbsp; Apply
+ </button>
+ </div>
</div>
</div>
</div>);
}
- loaded = (nw: number, nh: number, np: number) => {
- this.dataDoc.numPages = np;
- if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) {
- let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1);
- this.Document.nativeWidth = nw;
- this.Document.nativeHeight = this.Document.nativeHeight ? nw * oldaspect : nh;
- this.Document.height = this.Document[WidthSym]() * (nh / nw);
- this.Document.scrollHeight = np * this.Document.nativeHeight;
- }
- }
-
- @action
- onScroll = (e: React.UIEvent<HTMLDivElement>) => {
- if (e.currentTarget && this.props.ContainingCollectionDoc) {
- this.props.Document.panTransformType = "None";
- this.Document.panY = e.currentTarget.scrollTop;
- }
- }
-
-
render() {
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
- let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : "");
+ let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive");
return (!(pdfUrl instanceof PdfField) || !this._pdf ?
<div>{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}</div> :
- <div className={classname}
- onScroll={this.onScroll}
- style={{ marginTop: `${(this.Document.panY || 0)}px` }}
- ref={this._mainCont}>
- <div className="pdfBox-scrollHack" style={{ height: NumCast(this.props.Document.scrollHeight) + ((this.Document.nativeHeight || 0) - (this.Document.nativeHeight || 0) / (this.Document.scale || 1)) }} />
- <PDFViewer pdf={this._pdf} url={pdfUrl.url.pathname} active={this.props.active} scrollTo={this.scrollTo} loaded={this.loaded} panY={this.Document.panY || 0}
- Document={this.props.Document} DataDoc={this.dataDoc}
- addDocTab={this.props.addDocTab} setPanY={this.setPanY} GoToPage={this.GotoPage}
+ <div className={classname} onWheel={(e: React.WheelEvent) => e.stopPropagation()} onPointerDown={(e: React.PointerEvent) => {
+ let hit = document.elementFromPoint(e.clientX, e.clientY);
+ if (hit && hit.localName === "span" && this.props.isSelected()) {
+ e.button === 0 && e.stopPropagation();
+ }
+ }}>
+ <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} GoToPage={this.gotoPage}
pinToPres={this.props.pinToPres} addDocument={this.props.addDocument}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged}
fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} />
{this.settingsPanel()}
</div>);
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index e376fbddb..5afd85430 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -161,10 +161,8 @@ export class PresBox extends React.Component<FieldViewProps> { //FieldViewProps?
if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.showButton) {
let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null);
let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]);
- if (prevScale !== undefined) {
- if (prevScale !== curScale) {
- DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale);
- }
+ if (prevScale !== undefined && prevScale !== curScale) {
+ DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale);
}
}
}