From cc6040e717632ae9725c134cd8722813cb1733c2 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 23 Aug 2019 13:49:28 -0400 Subject: progress on guid method --- src/client/documents/Documents.ts | 1 - src/client/goldenLayout.js | 2 +- src/client/util/RichTextSchema.tsx | 3 +- src/client/util/TooltipTextMenu.tsx | 14 +++-- src/client/views/nodes/FormattedTextBox.tsx | 91 +++++++++++++++++++++++++++-- src/client/views/nodes/LinkMenuItem.tsx | 3 +- src/client/views/pdf/Page.tsx | 2 +- 7 files changed, 101 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9c8b6c129..1777174ef 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -621,7 +621,6 @@ export namespace DocUtils { linkDocProto.sourceContext = sourceContext; linkDocProto.title = title === "" ? source.title + " to " + target.title : title; linkDocProto.linkDescription = description; - linkDocProto.type = DocumentType.LINK; linkDocProto.anchor1 = source; linkDocProto.anchor1Page = source.curPage; diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index ad78139c1..29b750720 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -377,7 +377,7 @@ this._nOriginalY = coordinates.y; this._oDocument.on('mousemove touchmove', this._fMove); - this._oDocument.one('mouseup touchend', this._fUp); + this._oDocument.on('mouseup touchend', this._fUp); this._timeout = setTimeout(lm.utils.fnBind(this._startDrag, this), this._nDelay); } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 6d2abfaa2..44ebc05d4 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -214,7 +214,8 @@ export const marks: { [index: string]: MarkSpec } = { attrs: { href: {}, location: { default: null }, - title: { default: null } + title: { default: null }, + guid: { default: null } }, inclusive: false, parseDOM: [{ diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 4672dd246..5304f4cc6 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -302,11 +302,13 @@ export class TooltipTextMenu { dragComplete: action(() => { // let m = dragData.droppedDocuments; let linkDoc = dragData.linkDocument; + let guid = Utils.GenerateGuid(); let proto = Doc.GetProto(linkDoc); if (docView && docView.props.ContainingCollectionView) { proto.sourceContext = docView.props.ContainingCollectionView.props.Document; } - linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab"); + linkDoc.guid = guid; + linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab", guid); }), }, hideSource: false @@ -390,13 +392,13 @@ export class TooltipTextMenu { } } - makeLinkWithState = (state: EditorState, target: string, location: string) => { - let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); - } + // makeLinkWithState = (state: EditorState, target: string, location: string) => { + // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); + // } - makeLink = (target: string, location: string) => { + makeLink = (target: string, location: string, guid?: string) => { let node = this.view.state.selection.$from.nodeAfter; - let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location }); + let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: guid }); this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); node = this.view.state.selection.$from.nodeAfter; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index f7890e5a6..0266364e9 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -6,7 +6,7 @@ import { baseKeymap } from "prosemirror-commands"; import { history } from "prosemirror-history"; import { keymap } from "prosemirror-keymap"; import { Fragment, Node, Node as ProsNode, NodeType, Slice } from "prosemirror-model"; -import { EditorState, Plugin, Transaction } from "prosemirror-state"; +import { EditorState, Plugin, Transaction, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc"; @@ -126,6 +126,84 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } document.addEventListener("paste", this.paste); + reaction( + () => StrCast(this.props.Document.guid), // this refers to source guid + (guid) => { + let linkNode; + let start = -1; + + // go thru marks and look for guid + if (this._editorView && guid) { + let editor = this._editorView; + + console.log(guid); + + // find index of where mark === 'link' AND guid of text is matching that of the source of the followed link + // linkNode = editor.state.doc.content.firstChild; + // editor.state.tr.selection.content. + + let ret = findLinkFrag(editor.state.doc.content, editor); + + if (ret.frag.size > 2) { // fragment is not empty + console.log('here is frag', ret.frag); + console.log('here is node', linkNode); + console.log(editor.state.tr.selection); + // 1. get pos of start of frag in doc + // get from frag to slice to selection + let slice = new Slice(ret.frag, 1, 1); // significance of open depth??? + // let tr = editor.state.tr.setSelection(TextSelection.create(slice, )); + // editor.dispatch(tr.scrollIntoView()); + } + + // this._editorView.state.tr.setSelection(editor.state.doc, start of node, end of node); + // slice = new Slice(frag, slice.openStart, slice.openEnd); + // var tr = view.state.tr.replaceSelection(slice); + // view.dispatch(tr.scrollIntoView() + + // this._editorView.dispatch( /** pass in transaction */); + } + /** + * todo + * 1. recurse through fragment/node content until we find text nodes + * 2. once we find text nodes, find the specific one that matches guid (which we can do?????) + * 3. transport that back into main + * PROBLEM: this reaction is called 4 times for some reason + */ + + function findLinkFrag(frag: Fragment, editor: EditorView) { + const nodes: Node[] = []; + frag.forEach((node, index) => { + let examinedNode = findLinkNode(node, editor); + if (examinedNode) { + // here -- add information about 'for each' index? + nodes.push(examinedNode); + if (index > start) { + start = index; + } + } + }); + return { frag: Fragment.fromArray(nodes), start: start }; + } + function 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.name === "link"); + if (linkIndex !== -1) { + if (guid === marks[linkIndex].attrs.guid) { + console.log('linkindex is not -1,', linkIndex); + console.log('found match,', node); + linkNode = node; + return node; + } + } else { + return undefined; + } + } + } + ); } @computed get extensionDoc() { return Doc.resolvedFieldDataDoc(this.dataDoc, this.props.fieldKey, "dummy"); } @@ -167,7 +245,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false); if (state.selection.empty && FormattedTextBox._toolTipTextMenu) { const marks = tx.storedMarks; - console.log(marks); if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); } } this._applyingChange = true; @@ -292,8 +369,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined; return field ? field.Data : `{"doc":{"type":"doc","content":[]},"selection":{"type":"text","anchor":0,"head":0}}`; }, - field2 => this._editorView && !this._applyingChange && - this._editorView.updateState(EditorState.fromJSON(config, JSON.parse(field2))) + field2 => { + let ff2 = JSON.parse(field2); + this._editorView && !this._applyingChange && + this._editorView.updateState(EditorState.fromJSON(config, ff2)); + } ); this.props.isOverlay && (this._heightReactionDisposer = reaction( @@ -384,9 +464,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (link) { cbe.clipboardData!.setData("dash/linkDoc", link[Id]); linkId = link[Id]; + let guid = StrCast(link.guid); + link.guid = guid; let frag = addMarkToFrag(slice.content); slice = new Slice(frag, slice.openStart, slice.openEnd); var tr = view.state.tr.replaceSelection(slice); + view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); } }); diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index a119eb39b..1ce60ac88 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -43,9 +43,9 @@ export class LinkMenuItem extends React.Component { let proto = Doc.GetProto(this.props.linkDoc); let targetContext = await Cast(proto.targetContext, Doc); let sourceContext = await Cast(proto.sourceContext, Doc); + let guid = StrCast(this.props.linkDoc.guid); let self = this; - let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; if (e.ctrlKey) { dockingFunc = (document: Doc) => CollectionDockingView.Instance.AddRightSplit(document, undefined); @@ -55,6 +55,7 @@ export class LinkMenuItem extends React.Component { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext!); } else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { + jumpToDoc.guid = guid; DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 7ca9d2d7d..09bdd8043 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -112,7 +112,7 @@ export default class Page extends React.Component { if (!BoolCast(annotationDoc.linkedToDoc)) { let annotations = await DocListCastAsync(annotationDoc.annotations); annotations && annotations.forEach(anno => anno.target = targetDoc); - DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`) + DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`); } } }, -- cgit v1.2.3-70-g09d2 From d722bf96c11ecff06904029d2e3f49544f6f03f9 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 23 Aug 2019 14:41:05 -0400 Subject: on our way --- src/client/views/nodes/FormattedTextBox.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 82df43d16..2160b338d 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -156,11 +156,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe console.log('here is frag', ret.frag); console.log('here is node', linkNode); console.log(editor.state.tr.selection); + let anchor = editor.state.doc.resolve(ret.start); + let head = editor.state.doc.resolve(ret.start + ret.frag.size); + // 1. get pos of start of frag in doc // get from frag to slice to selection - let slice = new Slice(ret.frag, 1, 1); // significance of open depth??? - // let tr = editor.state.tr.setSelection(TextSelection.create(slice, )); - // editor.dispatch(tr.scrollIntoView()); + let tr = editor.state.tr.setSelection(TextSelection.between(anchor, head)); + editor.dispatch(tr.scrollIntoView()); } // this._editorView.state.tr.setSelection(editor.state.doc, start of node, end of node); -- cgit v1.2.3-70-g09d2 From c9d96b08bc32d040e9d0353edc84e775a5ac03b7 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 23 Aug 2019 16:50:06 -0400 Subject: pushing current code --- src/client/views/nodes/FormattedTextBox.tsx | 43 +++++------------------------ 1 file changed, 7 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 2160b338d..27520c81d 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -134,58 +134,31 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } document.addEventListener("paste", this.paste); + reaction( - () => StrCast(this.props.Document.guid), // this refers to source guid + () => StrCast(this.props.Document.guid), (guid) => { - let linkNode; let start = -1; - // go thru marks and look for guid if (this._editorView && guid) { let editor = this._editorView; - console.log(guid); - - // find index of where mark === 'link' AND guid of text is matching that of the source of the followed link - // linkNode = editor.state.doc.content.firstChild; - // editor.state.tr.selection.content. - let ret = findLinkFrag(editor.state.doc.content, editor); if (ret.frag.size > 2) { // fragment is not empty console.log('here is frag', ret.frag); - console.log('here is node', linkNode); - console.log(editor.state.tr.selection); - let anchor = editor.state.doc.resolve(ret.start); - let head = editor.state.doc.resolve(ret.start + ret.frag.size); - - // 1. get pos of start of frag in doc - // get from frag to slice to selection - let tr = editor.state.tr.setSelection(TextSelection.between(anchor, head)); - editor.dispatch(tr.scrollIntoView()); + let tr = editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(ret.start))); + editor.dispatch(tr.scrollIntoView()); // not sure whether scrollintoview or focus will work, waiting on fix for resizing text boxes + editor.focus(); } - - // this._editorView.state.tr.setSelection(editor.state.doc, start of node, end of node); - // slice = new Slice(frag, slice.openStart, slice.openEnd); - // var tr = view.state.tr.replaceSelection(slice); - // view.dispatch(tr.scrollIntoView() - - // this._editorView.dispatch( /** pass in transaction */); + this.props.Document.guid = ""; } - /** - * todo - * 1. recurse through fragment/node content until we find text nodes - * 2. once we find text nodes, find the specific one that matches guid (which we can do?????) - * 3. transport that back into main - * PROBLEM: this reaction is called 4 times for some reason - */ function findLinkFrag(frag: Fragment, editor: EditorView) { const nodes: Node[] = []; frag.forEach((node, index) => { let examinedNode = findLinkNode(node, editor); if (examinedNode) { - // here -- add information about 'for each' index? nodes.push(examinedNode); if (index > start) { start = index; @@ -203,11 +176,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const linkIndex = marks.findIndex(mark => mark.type.name === "link"); if (linkIndex !== -1) { if (guid === marks[linkIndex].attrs.guid) { - console.log('linkindex is not -1,', linkIndex); - console.log('found match,', node); - linkNode = node; return node; } + return undefined; } else { return undefined; } -- cgit v1.2.3-70-g09d2 From ad5def13620e361990423253e113d2c3104f5668 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Fri, 23 Aug 2019 17:56:25 -0400 Subject: pushing progress --- src/client/views/nodes/FormattedTextBox.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 7efeabdf6..6a922d3d6 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -147,11 +147,22 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (ret.frag.size > 2) { // fragment is not empty console.log('here is frag', ret.frag); - let tr = editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(ret.start))); - editor.dispatch(tr.scrollIntoView()); // not sure whether scrollintoview or focus will work, waiting on fix for resizing text boxes + let tr; + let index = 0; + while (ret.frag.child(index).nodeSize === 2) { + index++; + } + + // TODO find how to select correct child............................. + if (ret.frag.child(index)) { + tr = editor.state.tr.setSelection(TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.child(index).nodeSize))); + } else { // fallback + tr = editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(ret.start))); + } editor.focus(); + editor.dispatch(tr.scrollIntoView()); + this.props.Document.guid = ""; } - this.props.Document.guid = ""; } function findLinkFrag(frag: Fragment, editor: EditorView) { -- cgit v1.2.3-70-g09d2 From d3728e4898d11cfc4202474a43112cd67eaaa1ed Mon Sep 17 00:00:00 2001 From: kimdahey Date: Mon, 26 Aug 2019 13:37:32 -0400 Subject: scroll to link working --- src/client/views/nodes/FormattedTextBox.tsx | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 41344cf50..12dac412b 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -143,25 +143,19 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (this._editorView && guid) { let editor = this._editorView; - console.log(guid); let ret = findLinkFrag(editor.state.doc.content, editor); if (ret.frag.size > 2) { // fragment is not empty - console.log('here is frag', ret.frag); let tr; - let index = 0; - while (ret.frag.child(index).nodeSize === 2) { - index++; - } + if (ret.frag.firstChild) { - // TODO find how to select correct child............................. - if (ret.frag.child(index)) { - tr = editor.state.tr.setSelection(TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.child(index).nodeSize))); - } else { // fallback + tr = editor.state.tr.setSelection(TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize))); + } else { tr = editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(ret.start))); } editor.focus(); editor.dispatch(tr.scrollIntoView()); + editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? this.props.Document.guid = ""; } } @@ -170,11 +164,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe const nodes: Node[] = []; frag.forEach((node, index) => { let examinedNode = findLinkNode(node, editor); - if (examinedNode) { + if (examinedNode && examinedNode.textContent !== "") { nodes.push(examinedNode); - if (index > start) { - start = index; - } + start += index; } }); return { frag: Fragment.fromArray(nodes), start: start }; -- cgit v1.2.3-70-g09d2 From 33d47c3aa10af81101d7529244948ed74a69b975 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Mon, 26 Aug 2019 14:39:58 -0400 Subject: fixed couple of bugs --- src/client/views/nodes/FormattedTextBox.tsx | 4 ++-- src/client/views/nodes/LinkMenuItem.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 12dac412b..1dbbf29cb 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -138,7 +138,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe reaction( () => StrCast(this.props.Document.guid), - (guid) => { + async (guid) => { let start = -1; if (this._editorView && guid) { @@ -155,7 +155,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } editor.focus(); editor.dispatch(tr.scrollIntoView()); - editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? + // editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? this.props.Document.guid = ""; } } diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 20a8e20d1..1856e8a85 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -55,8 +55,8 @@ export class LinkMenuItem extends React.Component { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext); } else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { - jumpToDoc.guid = guid; DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); + jumpToDoc.guid = guid; } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page))); -- cgit v1.2.3-70-g09d2 From 54538028e5e32523ededaa86f90e78066ded2538 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Mon, 26 Aug 2019 15:04:38 -0400 Subject: bugfixing --- src/client/views/nodes/FormattedTextBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 1dbbf29cb..5d232ab84 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -155,7 +155,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } editor.focus(); editor.dispatch(tr.scrollIntoView()); - // editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? + editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? this.props.Document.guid = ""; } } -- cgit v1.2.3-70-g09d2 From 609c6708e361ad715e26c63de1cac646aec65873 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Mon, 26 Aug 2019 15:23:06 -0400 Subject: cleaned up code --- src/client/views/nodes/FormattedTextBox.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 5d232ab84..bccac3bda 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -182,10 +182,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (guid === marks[linkIndex].attrs.guid) { return node; } - return undefined; - } else { - return undefined; } + return undefined; } } ); -- cgit v1.2.3-70-g09d2 From 795f306f445bc2bf3afefee35d9eb8493e16b003 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Tue, 27 Aug 2019 11:21:42 -0400 Subject: fixes --- src/client/views/nodes/FormattedTextBox.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index bccac3bda..a98ae76ec 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -145,10 +145,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let editor = this._editorView; let ret = findLinkFrag(editor.state.doc.content, editor); - if (ret.frag.size > 2) { // fragment is not empty + if (ret.frag.size > 2) { let tr; if (ret.frag.firstChild) { - tr = editor.state.tr.setSelection(TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize))); } else { tr = editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(ret.start))); -- cgit v1.2.3-70-g09d2 From 81c01ab4fc75f1a6212c8227a52bcf53b0d081e6 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Tue, 27 Aug 2019 16:51:52 -0400 Subject: working on self-healing links --- src/client/views/nodes/FormattedTextBox.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index a98ae76ec..2f4c888f9 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -37,6 +37,7 @@ import { DocumentDecorations } from '../DocumentDecorations'; import { DictationManager } from '../../util/DictationManager'; import { ReplaceStep } from 'prosemirror-transform'; import { DocumentType } from '../../documents/DocumentTypes'; +import { link } from 'fs'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -726,9 +727,22 @@ 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); + + let guid: string; + if ((e.target as any).attributes.guid) { + guid = (e.target as any).attributes.guid; + } else if (linkDoc.guid) { + guid = StrCast(linkDoc.guid); + (e.target as any).attributes.guid = linkDoc.guid; + } else { + guid = Utils.GenerateGuid(); + (e.target as any).attributes.guid = guid; + linkDoc.guid = guid; + } + if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - + // if !guid, then generate guid and apply to link.... DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); return; } -- cgit v1.2.3-70-g09d2 From f62fe3aacba8201193b022c4e903cc140cc889f0 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Tue, 27 Aug 2019 17:27:22 -0400 Subject: still working on self healing links --- src/client/views/nodes/FormattedTextBox.tsx | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 2f4c888f9..0780b067d 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -141,6 +141,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe () => StrCast(this.props.Document.guid), async (guid) => { let start = -1; + let href = this.props.Document.href; if (this._editorView && guid) { let editor = this._editorView; @@ -157,6 +158,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe editor.dispatch(tr.scrollIntoView()); editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? this.props.Document.guid = ""; + this.props.Document.href = ""; } } @@ -181,6 +183,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (linkIndex !== -1) { if (guid === marks[linkIndex].attrs.guid) { return node; + } else if (href === marks[linkIndex].attrs.href) { + marks[linkIndex].attrs.guid = guid; + return node; } } return undefined; @@ -728,18 +733,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let targetContext = await Cast(proto.targetContext, Doc); let jumpToDoc = await Cast(linkDoc.anchor2, Doc); - let guid: string; - if ((e.target as any).attributes.guid) { - guid = (e.target as any).attributes.guid; - } else if (linkDoc.guid) { - guid = StrCast(linkDoc.guid); - (e.target as any).attributes.guid = linkDoc.guid; - } else { - guid = Utils.GenerateGuid(); - (e.target as any).attributes.guid = guid; - linkDoc.guid = guid; - } - if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { // if !guid, then generate guid and apply to link.... -- cgit v1.2.3-70-g09d2 From 9e3569038b0f51443f0cf1b86dab74ce97065fca Mon Sep 17 00:00:00 2001 From: kimdahey Date: Wed, 28 Aug 2019 12:28:10 -0400 Subject: working on self-healing --- src/client/views/nodes/LinkMenuItem.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 1856e8a85..fa2d178b9 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -44,6 +44,7 @@ export class LinkMenuItem extends React.Component { let targetContext = await Cast(proto.targetContext, Doc); let sourceContext = await Cast(proto.sourceContext, Doc); let guid = StrCast(this.props.linkDoc.guid); + let href = StrCast(this.props.linkDoc.href); let self = this; let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; @@ -56,6 +57,7 @@ export class LinkMenuItem extends React.Component { } else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); + jumpToDoc.href = href; jumpToDoc.guid = guid; } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { -- cgit v1.2.3-70-g09d2 From 65eecfc4295512f4f4fc6441f888c50f42adaced Mon Sep 17 00:00:00 2001 From: kimdahey Date: Wed, 28 Aug 2019 15:04:18 -0400 Subject: still...working.............. --- src/client/views/nodes/FormattedTextBox.tsx | 20 ++++++++++++++------ src/client/views/nodes/LinkMenuItem.tsx | 5 +++-- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0780b067d..33f585279 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -141,7 +141,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe () => StrCast(this.props.Document.guid), async (guid) => { let start = -1; - let href = this.props.Document.href; + let href = this.props.Document.linkHref; if (this._editorView && guid) { let editor = this._editorView; @@ -158,7 +158,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe editor.dispatch(tr.scrollIntoView()); editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? this.props.Document.guid = ""; - this.props.Document.href = ""; + this.props.Document.linkHref = ""; } } @@ -183,10 +183,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (linkIndex !== -1) { if (guid === marks[linkIndex].attrs.guid) { return node; - } else if (href === marks[linkIndex].attrs.href) { - marks[linkIndex].attrs.guid = guid; - return node; + } else { + console.log(marks[linkIndex].attrs.href); } + // else if (href === marks[linkIndex].attrs.href) { + // marks[linkIndex].attrs.guid = guid; + // return node; + // } } return undefined; } @@ -716,6 +719,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let ctrlKey = e.ctrlKey; if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) { let href = (e.target as any).href; + let guid = (e.target as any).guid; let location: string; if ((e.target as any).attributes.location) { location = (e.target as any).attributes.location.value; @@ -735,7 +739,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - // if !guid, then generate guid and apply to link.... + // if !guid, then generate guid and apply to full doc + if (!guid) { + console.log('making new guid!'); + linkDoc.guid = Utils.GenerateGuid(); + } DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); return; } diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index fa2d178b9..e2ea43a3c 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -13,6 +13,7 @@ import { LinkManager } from '../../util/LinkManager'; import { DragLinkAsDocument } from '../../util/DragManager'; import { CollectionDockingView } from '../collections/CollectionDockingView'; import { SelectionManager } from '../../util/SelectionManager'; +import { Utils } from '../../../Utils'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); @@ -44,7 +45,7 @@ export class LinkMenuItem extends React.Component { let targetContext = await Cast(proto.targetContext, Doc); let sourceContext = await Cast(proto.sourceContext, Doc); let guid = StrCast(this.props.linkDoc.guid); - let href = StrCast(this.props.linkDoc.href); + // let href = Utils.prepend("/doc/" + sourceContext[Id]); // trying to get id (?) so that we can search for this in the link marks let self = this; let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; @@ -57,7 +58,7 @@ export class LinkMenuItem extends React.Component { } else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); - jumpToDoc.href = href; + // jumpToDoc.linkHref = href; jumpToDoc.guid = guid; } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { -- cgit v1.2.3-70-g09d2 From 2f81ba96ac9c2aecbb9d610ecdc7d35335931e38 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Wed, 28 Aug 2019 15:15:49 -0400 Subject: stil.lllll working --- src/client/views/nodes/FormattedTextBox.tsx | 9 +++------ src/client/views/nodes/LinkMenuItem.tsx | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 33f585279..33edcc7f6 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -183,13 +183,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (linkIndex !== -1) { if (guid === marks[linkIndex].attrs.guid) { return node; - } else { - console.log(marks[linkIndex].attrs.href); + } else if (href && href === marks[linkIndex].attrs.href) { // retroactively fixing old in-text links by adding guid + marks[linkIndex].attrs.guid = guid; + return node; } - // else if (href === marks[linkIndex].attrs.href) { - // marks[linkIndex].attrs.guid = guid; - // return node; - // } } return undefined; } diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index e2ea43a3c..a012c9db2 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -14,6 +14,7 @@ import { DragLinkAsDocument } from '../../util/DragManager'; import { CollectionDockingView } from '../collections/CollectionDockingView'; import { SelectionManager } from '../../util/SelectionManager'; import { Utils } from '../../../Utils'; +import { Id } from '../../../new_fields/FieldSymbols'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); @@ -45,7 +46,10 @@ export class LinkMenuItem extends React.Component { let targetContext = await Cast(proto.targetContext, Doc); let sourceContext = await Cast(proto.sourceContext, Doc); let guid = StrCast(this.props.linkDoc.guid); - // let href = Utils.prepend("/doc/" + sourceContext[Id]); // trying to get id (?) so that we can search for this in the link marks + let href; + if (sourceContext) { + href = Utils.prepend("/doc/" + sourceContext[Id]); // trying to get id (?) so that we can search for this in the link marks + } let self = this; let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; @@ -58,8 +62,14 @@ export class LinkMenuItem extends React.Component { } else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); - // jumpToDoc.linkHref = href; - jumpToDoc.guid = guid; + if (guid) { + jumpToDoc.guid = guid; + } else if (href) { // retroactively fixing old in-text links by adding guid + console.log('wegotthis', href, guid); + jumpToDoc.linkHref = href; + let newguid = Utils.GenerateGuid(); + jumpToDoc.guid = newguid; + } } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page))); -- cgit v1.2.3-70-g09d2 From e0b8d5f213d7f547b939877c71f07804ac8c24cf Mon Sep 17 00:00:00 2001 From: kimdahey Date: Wed, 28 Aug 2019 16:19:54 -0400 Subject: working --- src/client/views/nodes/FormattedTextBox.tsx | 1 + src/client/views/nodes/LinkMenuItem.tsx | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 33edcc7f6..8d9944d59 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -187,6 +187,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe marks[linkIndex].attrs.guid = guid; return node; } + console.log('href was and is ', href, marks[linkIndex].attrs.href); } return undefined; } diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index a012c9db2..ffc191235 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -47,9 +47,13 @@ export class LinkMenuItem extends React.Component { let sourceContext = await Cast(proto.sourceContext, Doc); let guid = StrCast(this.props.linkDoc.guid); let href; + let href2; if (sourceContext) { href = Utils.prepend("/doc/" + sourceContext[Id]); // trying to get id (?) so that we can search for this in the link marks } + if (targetContext) { + href2 = Utils.prepend("/doc/" + targetContext[Id]); // trying to get id (?) so that we can search for this in the link marks + } let self = this; let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; @@ -65,9 +69,10 @@ export class LinkMenuItem extends React.Component { if (guid) { jumpToDoc.guid = guid; } else if (href) { // retroactively fixing old in-text links by adding guid - console.log('wegotthis', href, guid); + console.log('wegotthis', href, href2, proto.href, guid); jumpToDoc.linkHref = href; let newguid = Utils.GenerateGuid(); + this.props.linkDoc.guid = newguid; jumpToDoc.guid = newguid; } } -- cgit v1.2.3-70-g09d2 From 116c17c1e4ccf189d770d170d07a6bfbf95ca4e1 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 29 Aug 2019 12:47:39 -0400 Subject: testing id fix --- src/client/views/nodes/FormattedTextBox.tsx | 2 +- src/client/views/nodes/LinkMenuItem.tsx | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 8d9944d59..f2c27d303 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -739,7 +739,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { // if !guid, then generate guid and apply to full doc if (!guid) { - console.log('making new guid!'); + console.log('making new guid!'); // hehheehhehehe linkDoc.guid = Utils.GenerateGuid(); } DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index ffc191235..259dbc04e 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -46,14 +46,6 @@ export class LinkMenuItem extends React.Component { let targetContext = await Cast(proto.targetContext, Doc); let sourceContext = await Cast(proto.sourceContext, Doc); let guid = StrCast(this.props.linkDoc.guid); - let href; - let href2; - if (sourceContext) { - href = Utils.prepend("/doc/" + sourceContext[Id]); // trying to get id (?) so that we can search for this in the link marks - } - if (targetContext) { - href2 = Utils.prepend("/doc/" + targetContext[Id]); // trying to get id (?) so that we can search for this in the link marks - } let self = this; let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; @@ -67,10 +59,12 @@ export class LinkMenuItem extends React.Component { else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); if (guid) { + console.log('wegotthis', self.props.linkDoc.anchor2, jumpToDoc[Id]); + jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(this.props.linkDoc.anchor2)); jumpToDoc.guid = guid; - } else if (href) { // retroactively fixing old in-text links by adding guid - console.log('wegotthis', href, href2, proto.href, guid); - jumpToDoc.linkHref = href; + } else { // retroactively fixing old in-text links by adding guid + console.log('wegotthis', self.props.linkDoc.anchor2, jumpToDoc[Id]); + jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(this.props.linkDoc.anchor2)); let newguid = Utils.GenerateGuid(); this.props.linkDoc.guid = newguid; jumpToDoc.guid = newguid; -- cgit v1.2.3-70-g09d2 From 9b2562906bf3b87f18854dcaab9ad12b19dc3fce Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 29 Aug 2019 15:43:15 -0400 Subject: merge conflicts --- src/client/views/nodes/FormattedTextBox.tsx | 3 --- src/client/views/nodes/LinkMenuItem.tsx | 2 +- src/client/views/pdf/Page.tsx | 2 +- src/server/index.ts | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e08c4c988..ec1ddbdb7 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -37,12 +37,9 @@ import { DocumentDecorations } from '../DocumentDecorations'; import { DictationManager } from '../../util/DictationManager'; import { ReplaceStep } from 'prosemirror-transform'; import { DocumentType } from '../../documents/DocumentTypes'; -<<<<<<< HEAD import { link } from 'fs'; -======= import { selectionSizePlugin, findStartOfMark, findUserMark, findEndOfMark, findOtherUserMark, SelectionSizeTooltip } from './FormattedTextBoxComment'; import { date } from 'serializr'; ->>>>>>> 22a5999626b11cf75cafbcd421601e668438f6ad library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); diff --git a/src/client/views/nodes/LinkMenuItem.tsx b/src/client/views/nodes/LinkMenuItem.tsx index 259dbc04e..15f8c0786 100644 --- a/src/client/views/nodes/LinkMenuItem.tsx +++ b/src/client/views/nodes/LinkMenuItem.tsx @@ -59,7 +59,7 @@ export class LinkMenuItem extends React.Component { else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); if (guid) { - console.log('wegotthis', self.props.linkDoc.anchor2, jumpToDoc[Id]); + console.log('wegotthis', StrCast(self.props.linkDoc.anchor2), jumpToDoc[Id]); jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(this.props.linkDoc.anchor2)); jumpToDoc.guid = guid; } else { // retroactively fixing old in-text links by adding guid diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 8df2dce29..429642e9f 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -64,7 +64,7 @@ export default class Page extends React.Component { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes if (this._state !== "rendering" && !this._page && this._canvas.current && this._textLayer.current) { this._state = "rendering"; - let viewport = page.getViewport({ scale: scale }); + let viewport = page.getViewport(1 as any); this._canvas.current.width = this._width = viewport.width; this._canvas.current.height = this._height = viewport.height; this.props.pageLoaded(viewport); diff --git a/src/server/index.ts b/src/server/index.ts index fca90a585..17cd59ec7 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -447,7 +447,7 @@ function LoadPage(file: string, pageNumber: number, res: Response) { console.log(pageNumber); pdf.getPage(pageNumber).then((page: Pdfjs.PDFPageProxy) => { console.log("reading " + page); - let viewport = page.getViewport({ scale: 1 }); + let viewport = page.getViewport(1); let canvasAndContext = factory.create(viewport.width, viewport.height); let renderContext = { canvasContext: canvasAndContext.context, -- cgit v1.2.3-70-g09d2 From bbdd26d89a231922cebd1560761ffffba97b9a40 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 12 Sep 2019 15:53:27 -0400 Subject: updated to master, will check self healing links now --- src/client/views/linking/LinkFollowBox.tsx | 20 ++++++++- src/client/views/linking/LinkMenuItem.tsx | 64 ++--------------------------- src/client/views/nodes/FormattedTextBox.tsx | 8 +++- 3 files changed, 29 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index d5ed01f53..603515d2a 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -18,6 +18,7 @@ import { DocServer } from "../../DocServer"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { docs_v1 } from "googleapis"; +import { Utils } from "../../../Utils"; enum FollowModes { OPENTAB = "Open in Tab", @@ -245,15 +246,32 @@ export class LinkFollowBox extends React.Component { let proto = Doc.GetProto(LinkFollowBox.linkDoc); let targetContext = await Cast(proto.targetContext, Doc); let sourceContext = await Cast(proto.sourceContext, Doc); + let guid = StrCast(LinkFollowBox.linkDoc.guid); const shouldZoom = options ? options.shouldZoom : false; let dockingFunc = (document: Doc) => { this._addDocTab && this._addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; - if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, async document => dockingFunc(document), undefined, targetContext); } else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); + if (LinkFollowBox.sourceDoc) { + if (guid) { + console.log("guid"); + console.log('wegotthis', StrCast(LinkFollowBox.sourceDoc[Id])); // need to find if jumptodoc is the doc to follow, take id + jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); + LinkFollowBox.destinationDoc.guid = guid; + // process to follow: if guid, then we want to find the linkhref and use that to figure out whether we can find the links that correspond to the guid. + } else { + console.log("no guid"); // retroactively fixing old in-text links by adding guid + jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); + let newguid = Utils.GenerateGuid(); + LinkFollowBox.linkDoc.guid = newguid; + jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); + LinkFollowBox.destinationDoc.guid = newguid; + // if we find a link that doesnt match a guid but matches the OG link ref that correspond to the original anchor, then we move forward + } + } } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, undefined, undefined, diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 0e951fc38..5631727ca 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -3,8 +3,8 @@ import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc } from '../../../new_fields/Doc'; -import { Cast, StrCast } from '../../../new_fields/Types'; +import { Doc, DocListCastAsync } from '../../../new_fields/Doc'; +import { StrCast, Cast, FieldValue, NumCast } from '../../../new_fields/Types'; import { DragLinkAsDocument } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { ContextMenu } from '../ContextMenu'; @@ -12,18 +12,12 @@ import { MainView } from '../MainView'; import { LinkFollowBox } from './LinkFollowBox'; import './LinkMenu.scss'; import React = require("react"); -<<<<<<< HEAD:src/client/views/nodes/LinkMenuItem.tsx -import { Doc, DocListCastAsync } from '../../../new_fields/Doc'; -import { StrCast, Cast, FieldValue, NumCast } from '../../../new_fields/Types'; -import { observable, action } from 'mobx'; -import { LinkManager } from '../../util/LinkManager'; -import { DragLinkAsDocument } from '../../util/DragManager'; import { CollectionDockingView } from '../collections/CollectionDockingView'; import { SelectionManager } from '../../util/SelectionManager'; import { Utils } from '../../../Utils'; import { Id } from '../../../new_fields/FieldSymbols'; -======= ->>>>>>> ec62b213439ab49134fa2dbbdf38a6d1ef5737cd:src/client/views/linking/LinkMenuItem.tsx +import { DocumentManager } from '../../util/DocumentManager'; +import { undoBatch } from '../../util/UndoManager'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); @@ -40,58 +34,8 @@ interface LinkMenuItemProps { export class LinkMenuItem extends React.Component { private _drag = React.createRef(); @observable private _showMore: boolean = false; -<<<<<<< HEAD:src/client/views/nodes/LinkMenuItem.tsx @action toggleShowMore() { this._showMore = !this._showMore; } - @undoBatch - onFollowLink = async (e: React.PointerEvent): Promise => { - e.stopPropagation(); - e.persist(); - let jumpToDoc = this.props.destinationDoc; - let pdfDoc = FieldValue(Cast(this.props.destinationDoc, Doc)); - if (pdfDoc) { - jumpToDoc = pdfDoc; - } - let proto = Doc.GetProto(this.props.linkDoc); - let targetContext = await Cast(proto.targetContext, Doc); - let sourceContext = await Cast(proto.sourceContext, Doc); - let guid = StrCast(this.props.linkDoc.guid); - let self = this; - - let dockingFunc = (document: Doc) => { this.props.addDocTab(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; - if (e.ctrlKey) { - dockingFunc = (document: Doc) => CollectionDockingView.Instance.AddRightSplit(document, undefined); - } - - if (this.props.destinationDoc === self.props.linkDoc.anchor2 && targetContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, async document => dockingFunc(document), undefined, targetContext); - } - else if (this.props.destinationDoc === self.props.linkDoc.anchor1 && sourceContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, document => dockingFunc(sourceContext!)); - if (guid) { - console.log('wegotthis', StrCast(self.props.linkDoc.anchor2), jumpToDoc[Id]); - jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(this.props.linkDoc.anchor2)); - jumpToDoc.guid = guid; - } else { // retroactively fixing old in-text links by adding guid - console.log('wegotthis', self.props.linkDoc.anchor2, jumpToDoc[Id]); - jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(this.props.linkDoc.anchor2)); - let newguid = Utils.GenerateGuid(); - this.props.linkDoc.guid = newguid; - jumpToDoc.guid = newguid; - } - } - else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((this.props.destinationDoc === self.props.linkDoc.anchor2 ? self.props.linkDoc.anchor2Page : self.props.linkDoc.anchor1Page))); - } - else { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, false, dockingFunc); - } -======= - @action toggleShowMore() { - this._showMore = !this._showMore; ->>>>>>> ec62b213439ab49134fa2dbbdf38a6d1ef5737cd:src/client/views/linking/LinkMenuItem.tsx - } - onEdit = (e: React.PointerEvent): void => { e.stopPropagation(); this.props.showEditor(this.props.linkDoc); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 985075a52..ede0facd8 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -138,6 +138,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe document.addEventListener("paste", this.paste); + this.props.Document.guid = undefined; + this.props.Document.linkHref = undefined; + + console.log('formattextbox', this.props.Document[Id]); reaction( () => StrCast(this.props.Document.guid), async (guid) => { @@ -158,8 +162,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe editor.focus(); editor.dispatch(tr.scrollIntoView()); editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? - this.props.Document.guid = ""; - this.props.Document.linkHref = ""; + this.props.Document.guid = undefined; + this.props.Document.linkHref = undefined; } } -- cgit v1.2.3-70-g09d2 From d6a31b7c55f91336b94dce59096da1bb2dc01d88 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 12 Sep 2019 16:03:18 -0400 Subject: will check guid statuses --- src/client/views/linking/LinkFollowBox.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 603515d2a..2ae5fcda0 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -255,15 +255,16 @@ export class LinkFollowBox extends React.Component { } else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) { DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); - if (LinkFollowBox.sourceDoc) { + if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) { if (guid) { console.log("guid"); - console.log('wegotthis', StrCast(LinkFollowBox.sourceDoc[Id])); // need to find if jumptodoc is the doc to follow, take id + console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id])); // need to find if jumptodoc is the doc to follow, take id jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); LinkFollowBox.destinationDoc.guid = guid; // process to follow: if guid, then we want to find the linkhref and use that to figure out whether we can find the links that correspond to the guid. } else { console.log("no guid"); // retroactively fixing old in-text links by adding guid + console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id])); jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); let newguid = Utils.GenerateGuid(); LinkFollowBox.linkDoc.guid = newguid; -- cgit v1.2.3-70-g09d2 From 5f9aad860613575510400e07889f5dc3dda95de5 Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 12 Sep 2019 16:04:26 -0400 Subject: will check guid statuses --- src/client/views/linking/LinkFollowBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 2ae5fcda0..009c3ac55 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -264,7 +264,7 @@ export class LinkFollowBox extends React.Component { // process to follow: if guid, then we want to find the linkhref and use that to figure out whether we can find the links that correspond to the guid. } else { console.log("no guid"); // retroactively fixing old in-text links by adding guid - console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id])); + console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id]), 'as well as the linkdoc id', LinkFollowBox.linkDoc[Id]); jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); let newguid = Utils.GenerateGuid(); LinkFollowBox.linkDoc.guid = newguid; -- cgit v1.2.3-70-g09d2 From 8c0643bab365b58af25101345b209bacc49a955b Mon Sep 17 00:00:00 2001 From: kimdahey Date: Thu, 12 Sep 2019 16:19:09 -0400 Subject: self healing links established (?!) --- src/client/views/linking/LinkFollowBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 009c3ac55..0a3c8a320 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -268,7 +268,7 @@ export class LinkFollowBox extends React.Component { jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); let newguid = Utils.GenerateGuid(); LinkFollowBox.linkDoc.guid = newguid; - jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); + jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id])); LinkFollowBox.destinationDoc.guid = newguid; // if we find a link that doesnt match a guid but matches the OG link ref that correspond to the original anchor, then we move forward } -- cgit v1.2.3-70-g09d2 From c453ef0f405125aa0660fb469e2488a75d8dfe54 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Sun, 15 Sep 2019 16:08:21 -0400 Subject: self heal --- src/client/views/linking/LinkFollowBox.tsx | 8 ++++---- src/client/views/nodes/FormattedTextBox.tsx | 11 +++-------- 2 files changed, 7 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 0a3c8a320..3b36bf854 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -257,14 +257,14 @@ export class LinkFollowBox extends React.Component { DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) { if (guid) { - console.log("guid"); - console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id])); // need to find if jumptodoc is the doc to follow, take id + // console.log("guid"); + // console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id])); // need to find if jumptodoc is the doc to follow, take id jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); LinkFollowBox.destinationDoc.guid = guid; // process to follow: if guid, then we want to find the linkhref and use that to figure out whether we can find the links that correspond to the guid. } else { - console.log("no guid"); // retroactively fixing old in-text links by adding guid - console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id]), 'as well as the linkdoc id', LinkFollowBox.linkDoc[Id]); + // console.log("no guid"); // retroactively fixing old in-text links by adding guid + // console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id]), 'as well as the linkdoc id', LinkFollowBox.linkDoc[Id]); jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); let newguid = Utils.GenerateGuid(); LinkFollowBox.linkDoc.guid = newguid; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ede0facd8..d9a781c9c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -141,7 +141,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.props.Document.guid = undefined; this.props.Document.linkHref = undefined; - console.log('formattextbox', this.props.Document[Id]); reaction( () => StrCast(this.props.Document.guid), async (guid) => { @@ -159,9 +158,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } else { tr = editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(ret.start))); } + editor.focus(); editor.dispatch(tr.scrollIntoView()); - editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? + // editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? + this.props.Document.guid = undefined; this.props.Document.linkHref = undefined; } @@ -192,7 +193,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe marks[linkIndex].attrs.guid = guid; return node; } - console.log('href was and is ', href, marks[linkIndex].attrs.href); } return undefined; } @@ -815,11 +815,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - // if !guid, then generate guid and apply to full doc - if (!guid) { - console.log('making new guid!'); // hehheehhehehe - linkDoc.guid = Utils.GenerateGuid(); - } DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); return; } -- cgit v1.2.3-70-g09d2 From b4520766b3032ba8b3886197fa46de0cdb1cea50 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Sun, 15 Sep 2019 16:28:29 -0400 Subject: scroll into view --- src/client/views/linking/LinkFollowBox.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 3b36bf854..6e20cf8c1 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -263,7 +263,7 @@ export class LinkFollowBox extends React.Component { LinkFollowBox.destinationDoc.guid = guid; // process to follow: if guid, then we want to find the linkhref and use that to figure out whether we can find the links that correspond to the guid. } else { - // console.log("no guid"); // retroactively fixing old in-text links by adding guid + console.log("no guid"); // retroactively fixing old in-text links by adding guid // console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id]), 'as well as the linkdoc id', LinkFollowBox.linkDoc[Id]); jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); let newguid = Utils.GenerateGuid(); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d9a781c9c..e28d42be1 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -154,14 +154,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (ret.frag.size > 2) { let tr; if (ret.frag.firstChild) { - tr = editor.state.tr.setSelection(TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize))); + let between = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); + tr = editor.state.tr.setSelection(between); } else { - tr = editor.state.tr.setSelection(TextSelection.near(editor.state.doc.resolve(ret.start))); + let near = TextSelection.near(editor.state.doc.resolve(ret.start)); + tr = editor.state.tr.setSelection(near); } editor.focus(); editor.dispatch(tr.scrollIntoView()); - // editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? + editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? this.props.Document.guid = undefined; this.props.Document.linkHref = undefined; -- cgit v1.2.3-70-g09d2 From 3f38401ee477bd37ad95e5170783ca406437e195 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Sun, 15 Sep 2019 16:41:54 -0400 Subject: cleaning code --- src/client/views/linking/LinkFollowBox.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 6e20cf8c1..66661d178 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -257,20 +257,13 @@ export class LinkFollowBox extends React.Component { DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) { if (guid) { - // console.log("guid"); - // console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id])); // need to find if jumptodoc is the doc to follow, take id - jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); LinkFollowBox.destinationDoc.guid = guid; - // process to follow: if guid, then we want to find the linkhref and use that to figure out whether we can find the links that correspond to the guid. } else { - console.log("no guid"); // retroactively fixing old in-text links by adding guid - // console.log('source and dest ids respectively are', StrCast(LinkFollowBox.sourceDoc[Id]), StrCast(LinkFollowBox.destinationDoc[Id]), 'as well as the linkdoc id', LinkFollowBox.linkDoc[Id]); jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); + // jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id])); let newguid = Utils.GenerateGuid(); LinkFollowBox.linkDoc.guid = newguid; - jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id])); LinkFollowBox.destinationDoc.guid = newguid; - // if we find a link that doesnt match a guid but matches the OG link ref that correspond to the original anchor, then we move forward } } } -- cgit v1.2.3-70-g09d2 From 9e2343a94a70559f50694da3687f5c297dd4eca0 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Sun, 15 Sep 2019 16:45:47 -0400 Subject: cleaning code --- src/client/views/linking/LinkFollowBox.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 66661d178..9c65bbcf9 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -259,8 +259,7 @@ export class LinkFollowBox extends React.Component { if (guid) { LinkFollowBox.destinationDoc.guid = guid; } else { - jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.sourceDoc[Id])); - // jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id])); + jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id])); let newguid = Utils.GenerateGuid(); LinkFollowBox.linkDoc.guid = newguid; LinkFollowBox.destinationDoc.guid = newguid; -- cgit v1.2.3-70-g09d2 From 4c63c4cc4503faa511ef141984274e19c00c41ba Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Sun, 15 Sep 2019 16:54:14 -0400 Subject: working follow link / self heal --- src/client/views/linking/LinkMenuItem.tsx | 10 ++-------- src/client/views/nodes/FormattedTextBox.tsx | 4 +--- 2 files changed, 3 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 5631727ca..0cdc3e42f 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -3,8 +3,8 @@ import { faArrowRight, faChevronDown, faChevronUp, faEdit, faEye, faTimes } from import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; -import { Doc, DocListCastAsync } from '../../../new_fields/Doc'; -import { StrCast, Cast, FieldValue, NumCast } from '../../../new_fields/Types'; +import { Doc } from '../../../new_fields/Doc'; +import { StrCast, Cast } from '../../../new_fields/Types'; import { DragLinkAsDocument } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { ContextMenu } from '../ContextMenu'; @@ -12,12 +12,6 @@ import { MainView } from '../MainView'; import { LinkFollowBox } from './LinkFollowBox'; import './LinkMenu.scss'; import React = require("react"); -import { CollectionDockingView } from '../collections/CollectionDockingView'; -import { SelectionManager } from '../../util/SelectionManager'; -import { Utils } from '../../../Utils'; -import { Id } from '../../../new_fields/FieldSymbols'; -import { DocumentManager } from '../../util/DocumentManager'; -import { undoBatch } from '../../util/UndoManager'; library.add(faEye, faEdit, faTimes, faArrowRight, faChevronDown, faChevronUp); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index e28d42be1..c292f12ad 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -119,7 +119,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @undoBatch public setFontColor(color: string) { - this._editorView!.state.storedMarks + this._editorView!.state.storedMarks; if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false; if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) { this.props.Document.color = color; @@ -136,8 +136,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); } - document.addEventListener("paste", this.paste); - this.props.Document.guid = undefined; this.props.Document.linkHref = undefined; -- cgit v1.2.3-70-g09d2 From 147c208a9a1196d2ded540e28dc3d7f26d5ba0a9 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Sun, 15 Sep 2019 16:56:54 -0400 Subject: finished --- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 0cdc3e42f..d0c0d184d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, observable } from 'mobx'; import { observer } from "mobx-react"; import { Doc } from '../../../new_fields/Doc'; -import { StrCast, Cast } from '../../../new_fields/Types'; +import { Cast, StrCast } from '../../../new_fields/Types'; import { DragLinkAsDocument } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { ContextMenu } from '../ContextMenu'; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index c292f12ad..9abc7b329 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -661,7 +661,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(doc.title))); slice = new Slice(frag, slice.openStart, slice.openEnd); var tr = view.state.tr.replaceSelection(slice); - view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); } }); -- cgit v1.2.3-70-g09d2 From 315e292084134dc1f66187bfc2e31aa471709fa6 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Sat, 21 Sep 2019 15:55:44 -0400 Subject: Presentation Mode Dragging Fixed --- src/client/views/presentationview/PresentationModeMenu.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/views/presentationview/PresentationModeMenu.tsx b/src/client/views/presentationview/PresentationModeMenu.tsx index 4de8da587..0dd2b7731 100644 --- a/src/client/views/presentationview/PresentationModeMenu.tsx +++ b/src/client/views/presentationview/PresentationModeMenu.tsx @@ -21,10 +21,12 @@ export interface PresModeMenuProps { export default class PresModeMenu extends React.Component { @observable private _top: number = 20; - @observable private _right: number = 0; + @observable private _left: number = window.innerWidth - 160; @observable private _opacity: number = 1; @observable private _transition: string = "opacity 0.5s"; @observable private _transitionDelay: string = ""; + private _offsetY: number = 0; + private _offsetX: number = 0; private _mainCont: React.RefObject = React.createRef(); @@ -35,8 +37,8 @@ export default class PresModeMenu extends React.Component { */ @action dragging = (e: PointerEvent) => { - this._right -= e.movementX; - this._top += e.movementY; + this._left = e.pageX - this._offsetX; + this._top = e.pageY - this._offsetY; e.stopPropagation(); e.preventDefault(); @@ -63,6 +65,9 @@ export default class PresModeMenu extends React.Component { 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(); } @@ -82,7 +87,7 @@ export default class PresModeMenu extends React.Component { render() { return (
+ style={{ left: this._left, top: this._top, opacity: this._opacity, transition: this._transition, transitionDelay: this._transitionDelay }}> {this.renderPlayPauseButton()} -- cgit v1.2.3-70-g09d2 From c3e5b50ed59f474cddace89ad4ca25f2ef0c2f74 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Sat, 21 Sep 2019 17:01:07 -0400 Subject: mostly refactored --- src/client/views/pdf/PDFViewer.scss | 14 +- src/client/views/pdf/PDFViewer.tsx | 287 ++++++++++++++++++++++++++++++++---- src/debug/Test.tsx | 104 +++++++++++-- 3 files changed, 357 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a2f3911c5..8290a0ee3 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -1,13 +1,14 @@ - .pdfViewer-viewer { - pointer-events:inherit; + pointer-events: inherit; width: 100%; + .pdfViewer-visibleElements { .pdfPage-cont { .pdfPage-textLayer { div { user-select: text; } + span { color: transparent; position: absolute; @@ -19,9 +20,14 @@ } } } + .pdfViewer-text { transform: scale(1.5); transform-origin: top left; + + .page .textLayer { + user-select: text; + } } .pdfViewer-annotationLayer { @@ -29,6 +35,7 @@ top: 0; width: 100%; pointer-events: none; + .pdfPage-annotationBox { position: absolute; background-color: red; @@ -48,6 +55,7 @@ padding: 20px; overflow: hidden; transition: left .5s; + .pdfViewer-overlaySearchBar { width: 20%; height: 100%; @@ -90,4 +98,4 @@ .pdfViewer-overlayButton:hover { background: none; } -} +} \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index c94b4e3a4..108d649a1 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -5,11 +5,11 @@ import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import * as rp from "request-promise"; import { Dictionary } from "typescript-collections"; -import { Doc, DocListCast, FieldResult } from "../../../new_fields/Doc"; +import { Doc, DocListCast, FieldResult, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { ScriptField } from "../../../new_fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { Cast, NumCast, StrCast, BoolCast } from "../../../new_fields/Types"; import { Utils, numberRange } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; @@ -20,6 +20,8 @@ import Page from "./Page"; import "./PDFViewer.scss"; import React = require("react"); import requestPromise = require("request-promise"); +import PDFMenu from "./PDFMenu"; +import { DragManager } from "../../util/DragManager"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); export const scale = 2; @@ -55,6 +57,10 @@ export class PDFViewer extends React.Component { @observable private _script: CompiledScript = CompileScript("return true") as CompiledScript; @observable private _searching: boolean = false; @observable private Index: number = -1; + @observable private _marqueeX: number = 0; + @observable private _marqueeY: number = 0; + @observable private _marqueeWidth: number = 0; + @observable private _marqueeHeight: number = 0; private _pageBuffer: number = 1; private _annotationLayer: React.RefObject = React.createRef(); @@ -64,9 +70,14 @@ export class PDFViewer extends React.Component { private _viewer: React.RefObject = React.createRef(); private _mainCont: React.RefObject = React.createRef(); public _pdfViewer: any; + private _simpleLinkService: SimpleLinkService | undefined; private _pdfFindController: any; private _searchString: string = ""; private _selectionText: string = ""; + private _marquee: React.RefObject = React.createRef(); + private _marqueeing: boolean = false; + private _startY: number = 0; + private _startX: number = 0; @computed get panY(): number { return this.props.panY; } @@ -87,15 +98,24 @@ export class PDFViewer extends React.Component { return this._annotations.filter(anno => this._script.run({ this: anno }, console.log, true).result); } - componentDidUpdate = (prevProps: IViewerProps) => this.panY !== prevProps.panY && this.renderPages(); + componentDidUpdate = (prevProps: IViewerProps) => { + if (this.panY !== prevProps.panY && this._simpleLinkService) { + let p = this.getPageFromScroll(this.panY); + for (let i = Math.max(0, p - 1); i <= Math.min(this.props.pdf.numPages - 1, p + 1); i++) { + this._pdfViewer._ensurePdfPageLoaded(this._pdfViewer._pages[i]).then(() => { + this._pdfViewer.renderingQueue.renderView(this._pdfViewer._pages[i]); + }); + } + } + } componentDidMount = async () => { await this.initialLoad(); - this._reactionDisposer = reaction( - () => [this.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0], - () => this.renderPages(), - { fireImmediately: true }); + // this._reactionDisposer = reaction( + // () => [this.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0], + // () => this.renderPages(), + // { fireImmediately: true }); this._annotationReactionDisposer = reaction( () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations), @@ -153,29 +173,55 @@ export class PDFViewer extends React.Component { @action initialLoad = async () => { - if (this._pageSizes.length === 0) { - this._isPage = Array(this.props.pdf.numPages); - this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); - this._visibleElements = Array(this.props.pdf.numPages); + this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); + if (this._mainCont.current) { + this._simpleLinkService = new SimpleLinkService(this); + this._pdfViewer = new PDFJSViewer.PDFViewer({ + container: this._mainCont.current, + viewer: this._viewer.current, + linkService: this._simpleLinkService + }); + this._simpleLinkService.setPDFJSview(this._pdfViewer); + this._mainCont.current.addEventListener("pagesinit", () => { + this._pdfViewer.currentScaleValue = 1; + }); + this._mainCont.current.addEventListener("pagerendered", () => console.log("rendered")); + this._pdfViewer.setDocument(this.props.pdf); + this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer); + this._pdfViewer.findController = this._pdfFindController; await Promise.all(this._pageSizes.map>((val, i) => - this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => { + this.props.pdf.getPage(i + 1).then((page: Pdfjs.PDFPageProxy) => { this._pageSizes.splice(i, 1, { width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale, height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale }); - this._visibleElements.splice(i, 1, -
- "PAGE IS LOADING... " -
); - this.getPlaceholderPage(i); - })))); + } + ))); this.props.loaded(Math.max(...this._pageSizes.map(i => i.width)), this._pageSizes[0].height, this.props.pdf.numPages); - - let startY = NumCast(this.props.Document.startY, NumCast(this.props.Document.panY)); - this.props.setPanY && this.props.setPanY(startY); - this.props.scrollTo(startY); } + // if (this._pageSizes.length === 0) { + // this._isPage = Array(this.props.pdf.numPages); + // this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); + // this._visibleElements = Array(this.props.pdf.numPages); + // await Promise.all(this._pageSizes.map>((val, i) => + // this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => { + // this._pageSizes.splice(i, 1, { + // width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale, + // height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale + // }); + // this._visibleElements.splice(i, 1, + //
+ // "PAGE IS LOADING... " + //
); + // this.getPlaceholderPage(i); + // })))); + // this.props.loaded(Math.max(...this._pageSizes.map(i => i.width)), this._pageSizes[0].height, this.props.pdf.numPages); + + // let startY = NumCast(this.props.Document.startY, NumCast(this.props.Document.panY)); + // this.props.setPanY && this.props.setPanY(startY); + // this.props.scrollTo(startY); + // } } @action @@ -416,12 +462,194 @@ export class PDFViewer extends React.Component { return this._visibleElements; } + @action + onPointerDown = (e: React.PointerEvent): void => { + // if alt+left click, drag and annotate + if (NumCast(this.props.Document.scale, 1) !== 1) return; + if (!e.altKey && e.button === 0) { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + PDFMenu.Instance.Snippet = this.createSnippet; + PDFMenu.Instance.Status = "pdf"; + PDFMenu.Instance.fadeOut(true); + if (e.target && (e.target as any).parentElement.className === "textLayer") { + e.stopPropagation(); + if (!e.ctrlKey) { + this.receiveAnnotations([], -1); + } + } + else { + // set marquee x and y positions to the spatially transformed position + if (this._mainCont.current) { + let boundingRect = this._mainCont.current.getBoundingClientRect(); + this._startX = this._marqueeX = (e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width); + this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height); + } + this._marqueeing = true; + this._marquee.current && (this._marquee.current.style.opacity = "0.2"); + this.receiveAnnotations([], -1); + } + document.removeEventListener("pointermove", this.onSelectStart); + document.addEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); + document.addEventListener("pointerup", this.onSelectEnd); + } + } + + @action + onSelectStart = (e: PointerEvent): void => { + if (this._marqueeing && this._mainCont.current) { + // transform positions and find the width and height to set the marquee to + let boundingRect = this._mainCont.current.getBoundingClientRect(); + this._marqueeWidth = ((e.clientX - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width)) - this._startX; + this._marqueeHeight = ((e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height)) - this._startY; + this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth); + this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight); + this._marqueeWidth = Math.abs(this._marqueeWidth); + e.stopPropagation(); + e.preventDefault(); + } + else if (e.target && (e.target as any).parentElement === this._mainCont.current) { + e.stopPropagation(); + } + } + + @action + createTextAnnotation = (sel: Selection, selRange: Range) => { + if (this._mainCont.current) { + let boundingRect = this._mainCont.current.getBoundingClientRect(); + let clientRects = selRange.getClientRects(); + for (let i = 0; i < clientRects.length; i++) { + let rect = clientRects.item(i); + if (rect && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height) { + let annoBox = document.createElement("div"); + annoBox.className = "pdfPage-annotationBox"; + // transforms the positions from screen onto the pdf div + annoBox.style.top = ((rect.top - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height)).toString(); + annoBox.style.left = ((rect.left - boundingRect.left) * (this._mainCont.current.offsetWidth / boundingRect.width)).toString(); + annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width).toString(); + annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height).toString(); + this.createAnnotation(annoBox, this.getPageFromScroll(rect.height)); + } + } + } + let text = selRange.cloneContents().textContent; + text && this.setSelectionText(text); + + // clear selection + if (sel.empty) { // Chrome + sel.empty(); + } else if (sel.removeAllRanges) { // Firefox + sel.removeAllRanges(); + } + } + + @action + onSelectEnd = (e: PointerEvent): void => { + if (this._marqueeing) { + this._marqueeing = false; + if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { + if (this._marquee.current) { // make a copy of the marquee + let copy = document.createElement("div"); + let style = this._marquee.current.style; + copy.style.left = style.left; + copy.style.top = style.top; + copy.style.width = style.width; + copy.style.height = style.height; + copy.style.border = style.border; + copy.style.opacity = style.opacity; + copy.className = "pdfPage-annotationBox"; + this.createAnnotation(copy, this.getPageFromScroll(this._marqueeY)); + this._marquee.current.style.opacity = "0"; + } + + if (!e.ctrlKey) { + PDFMenu.Instance.Status = "snippet"; + PDFMenu.Instance.Marquee = { left: this._marqueeX, top: this._marqueeY, width: this._marqueeWidth, height: this._marqueeHeight }; + } + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + } + + this._marqueeHeight = this._marqueeWidth = 0; + } + else { + let sel = window.getSelection(); + if (sel && sel.type === "Range") { + let selRange = sel.getRangeAt(0); + this.createTextAnnotation(sel, selRange); + PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + } + } + + if (PDFMenu.Instance.Highlighting) { + this.highlight(undefined, "goldenrod"); + } + else { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; + } + document.removeEventListener("pointermove", this.onSelectStart); + document.removeEventListener("pointerup", this.onSelectEnd); + } + + @action + highlight = (targetDoc: Doc | undefined, color: string) => { + // creates annotation documents for current highlights + let annotationDoc = this.makeAnnotationDocument(targetDoc, color, false); + Doc.AddDocToList(this.props.fieldExtensionDoc, "annotations", annotationDoc); + return annotationDoc; + } + + /** + * This is temporary for creating annotations from highlights. It will + * start a drag event and create or put the necessary info into the drag event. + */ + @action + startDrag = (e: PointerEvent, ele: HTMLElement): void => { + e.preventDefault(); + e.stopPropagation(); + let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" }); + targetDoc.targetPage = this.getPageFromScroll(this._marqueeY); + let annotationDoc = this.highlight(undefined, "red"); + annotationDoc.linkedToDoc = false; + let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc); + DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, { + handlers: { + dragComplete: async () => { + if (!BoolCast(annotationDoc.linkedToDoc)) { + let annotations = await DocListCastAsync(annotationDoc.annotations); + annotations && annotations.forEach(anno => anno.target = targetDoc); + DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`); + } + } + }, + hideSource: false + }); + } + + createSnippet = (marquee: { left: number, top: number, width: number, height: number }): void => { + let view = Doc.MakeAlias(this.props.Document); + let data = Doc.MakeDelegate(Doc.GetProto(this.props.Document)); + data.title = StrCast(data.title) + "_snippet"; + view.proto = data; + view.nativeHeight = marquee.height; + view.height = (this.props.Document[WidthSym]() / NumCast(this.props.Document.nativeWidth)) * marquee.height; + view.nativeWidth = this.props.Document.nativeWidth; + view.startY = marquee.top; + view.width = this.props.Document[WidthSym](); + DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0); + } + render() { - return (
-
- {this.visibleElementWrapper} -
+ return (
+
+
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )} @@ -481,12 +709,13 @@ class SimpleLinkService { get pagesCount() { return this._viewer._pdfViewer.pagesCount; } - get page() { return NumCast(this._viewer.props.Document.curPage); } + get page() { return this._viewer.getPageFromScroll(NumCast(this._viewer.props.panY)) + 1; } set page(p: number) { this._pdfjsView._ensurePdfPageLoaded(this._pdfjsView._pages[p - 1]).then(() => { this._pdfjsView.renderingQueue.renderView(this._pdfjsView._pages[p - 1]); - if (this._viewer.props.GoToPage) + if (this._viewer.props.GoToPage) { this._viewer.props.GoToPage(p); + } }); } diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx index 79f87f4ac..20d02488e 100644 --- a/src/debug/Test.tsx +++ b/src/debug/Test.tsx @@ -2,35 +2,107 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { DocServer } from '../client/DocServer'; import { Doc } from '../new_fields/Doc'; +import * as Pdfjs from "pdfjs-dist"; +import "pdfjs-dist/web/pdf_viewer.css"; +import { Utils } from '../Utils'; +import { scale } from '../client/views/pdf/PDFViewer'; +const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const protoId = "protoDoc"; const delegateId = "delegateDoc"; class Test extends React.Component { - onCreateClick = () => { - const proto = new Doc(protoId, true); - const delegate = Doc.MakeDelegate(proto, delegateId); - } + private _viewer: React.RefObject = React.createRef(); + private _mainCont: React.RefObject = React.createRef(); + private _pageSizes: Array<{ width: number, height: number }> = []; + _pdfViewer: PDFJSViewer.PDFViewer; + _pdfFindController: PDFJSViewer.PDFFindController; + _page: number = 0; - onReadClick = async () => { - console.log("reading"); - const docs = await DocServer.GetRefFields([delegateId, protoId]); - console.log("done"); - console.log(docs); + componentDidMount = () => { + let pdfUrl = Utils.CorsProxy("https://www.hq.nasa.gov/alsj/a17/A17_FlightPlan.pdf"); + Pdfjs.getDocument(pdfUrl).promise.then(async pdf => { + if (this._mainCont.current) { + let simpleLinkService = new SimpleLinkService(this); + this._pdfViewer = new PDFJSViewer.PDFViewer({ + container: this._mainCont.current, + viewer: this._viewer.current, + linkService: simpleLinkService + }); + simpleLinkService.setPDFJSview(this._pdfViewer); + this._mainCont.current.addEventListener("pagesinit", () => { + this._pdfViewer.currentScaleValue = 1; + console.log(this._pdfViewer); + }); + this._mainCont.current.addEventListener("pagerendered", () => console.log("rendered")); + this._pdfViewer.setDocument(pdf); + this._pageSizes = Array<{ width: number, height: number }>(pdf.numPages); + this._pdfFindController = new PDFJSViewer.PDFFindController(this._pdfViewer); + this._pdfViewer.findController = this._pdfFindController; + await Promise.all(this._pageSizes.map>((val, i) => + pdf.getPage(i + 1).then((page: Pdfjs.PDFPageProxy) => { + this._pageSizes.splice(i, 1, { + width: (page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]) * scale, + height: (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]) * scale + }); + } + ))); + } + }); } - onDeleteClick = () => { - DocServer.DeleteDocuments([protoId, delegateId]); + goToPage = (page: number) => { + if (this._mainCont.current) { + // this._mainCont.current.scrollTo() + } } render() { return ( -
- - - +
+
- ); + ) + } +} + +class SimpleLinkService { + _viewer: Test; + _pdfjsView: any; + + constructor(viewer: Test) { + this._viewer = viewer; } + setPDFJSview(v: any) { this._pdfjsView = v; } + + navigateTo() { } + + getDestinationHash() { return "#"; } + + getAnchorUrl() { return "#"; } + + setHash() { } + + isPageVisible(page: number) { return true; } + + executeNamedAction() { } + + cachePageRef() { } + + get pagesCount() { return this._viewer._pdfViewer.pagesCount; } + + get page() { return this._viewer._page; } + set page(p: number) { + this._pdfjsView._ensurePdfPageLoaded(this._pdfjsView._pages[p - 1]).then(() => { + this._pdfjsView.renderingQueue.renderView(this._pdfjsView._pages[p - 1]); + if (this._viewer.goToPage) { + this._viewer.goToPage(p); + } + }); + } + + + get rotation() { return 0; } + set rotation(value: any) { } } DocServer.init(window.location.protocol, window.location.hostname, 4321, "test"); -- cgit v1.2.3-70-g09d2 From 81551fad8582129bc05581cdd132cada5e9f23db Mon Sep 17 00:00:00 2001 From: kimdahey Date: Sun, 22 Sep 2019 15:29:02 -0400 Subject: error with key duplicates, but 2+ links to same doc works now --- src/client/documents/Documents.ts | 3 +++ src/client/util/LinkManager.ts | 15 +++++++++++++++ src/client/util/TooltipTextMenu.tsx | 13 +++++++++++-- src/client/views/nodes/DocumentView.tsx | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b2a320517..079ff00db 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -639,6 +639,8 @@ export namespace DocUtils { } export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) { // if (LinkManager.Instance.doesLinkExist(source, target)) return undefined; + if (LinkManager.Instance.doesNormalLinkExist(source, target) && description !== "in-text link being created") return undefined; // normal describes the type of link attempting to be created + // if normal link already exists and !normal (in text link is not being created) then return let sv = DocumentManager.Instance.getDocumentView(source); if (sv && sv.props.ContainingCollectionDoc === target) return; if (target === CurrentUserUtils.UserDocument) return undefined; @@ -664,6 +666,7 @@ export namespace DocUtils { Doc.GetProto(source).links = ComputedField.MakeFunction("links(this)"); Doc.GetProto(target).links = ComputedField.MakeFunction("links(this)"); + }, "make link"); return linkDocProto; } diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 8a668e8d8..b285b967b 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -238,6 +238,21 @@ export class LinkManager { return index !== -1; } + // checks if a normal link (i.e. no in-text link) exists with given anchors + public doesNormalLinkExist(anchor1: Doc, anchor2: Doc): boolean { + let allLinks = LinkManager.Instance.getAllLinks(); + let index = allLinks.findIndex(linkDoc => { + if ((Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) || + (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1))) { + console.log("guid: " + linkDoc.guid); + } + return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2) && linkDoc.guid === undefined) || + (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1) && linkDoc.guid === undefined); + }); + return index !== -1; + } + + // finds the opposite anchor of a given anchor in a link //TODO This should probably return undefined if there isn't an opposite anchor //TODO This should also await the return value of the anchor so we don't filter out promises diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index a2653855c..c84d98df9 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -309,7 +309,10 @@ export class TooltipTextMenu { proto.sourceContext = docView.props.ContainingCollectionDoc; } linkDoc.guid = guid; - linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab", guid); + let text = this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab", guid); + if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) { + proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link + } }), }, hideSource: false @@ -395,13 +398,19 @@ export class TooltipTextMenu { // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); // } - makeLink = (target: string, location: string, guid?: string) => { + makeLink = (target: string, location: string, guid?: string): string => { let node = this.view.state.selection.$from.nodeAfter; let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: guid }); this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); node = this.view.state.selection.$from.nodeAfter; link = node && node.marks.find(m => m.type.name === "link"); + if (node) { + if (node.text) { + return node.text; + } + } + return ""; } deleteLink = () => { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d8cfff973..e89fddd25 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -350,7 +350,7 @@ export class DocumentView extends DocComponent(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 } } -- cgit v1.2.3-70-g09d2 From 9fbdb0088bb42235bb530602c5275e015f2609bd Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 23 Sep 2019 13:27:38 -0400 Subject: restructured link following to text regions --- src/client/documents/Documents.ts | 3 -- src/client/util/LinkManager.ts | 15 ------ src/client/util/RichTextSchema.tsx | 3 +- src/client/util/TooltipTextMenu.tsx | 25 ++++----- src/client/views/linking/LinkFollowBox.tsx | 8 ++- src/client/views/nodes/FormattedTextBox.tsx | 80 +++++++++++++---------------- 6 files changed, 53 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 079ff00db..4ae770e25 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -638,9 +638,6 @@ export namespace DocUtils { }); } export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) { - // if (LinkManager.Instance.doesLinkExist(source, target)) return undefined; - if (LinkManager.Instance.doesNormalLinkExist(source, target) && description !== "in-text link being created") return undefined; // normal describes the type of link attempting to be created - // if normal link already exists and !normal (in text link is not being created) then return let sv = DocumentManager.Instance.getDocumentView(source); if (sv && sv.props.ContainingCollectionDoc === target) return; if (target === CurrentUserUtils.UserDocument) return undefined; diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index b285b967b..8a668e8d8 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -238,21 +238,6 @@ export class LinkManager { return index !== -1; } - // checks if a normal link (i.e. no in-text link) exists with given anchors - public doesNormalLinkExist(anchor1: Doc, anchor2: Doc): boolean { - let allLinks = LinkManager.Instance.getAllLinks(); - let index = allLinks.findIndex(linkDoc => { - if ((Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2)) || - (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1))) { - console.log("guid: " + linkDoc.guid); - } - return (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor1) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor2) && linkDoc.guid === undefined) || - (Doc.AreProtosEqual(Cast(linkDoc.anchor1, Doc, null), anchor2) && Doc.AreProtosEqual(Cast(linkDoc.anchor2, Doc, null), anchor1) && linkDoc.guid === undefined); - }); - return index !== -1; - } - - // finds the opposite anchor of a given anchor in a link //TODO This should probably return undefined if there isn't an opposite anchor //TODO This should also await the return value of the anchor so we don't filter out promises diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index ea31671ac..9d5ccffe9 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -253,8 +253,7 @@ export const marks: { [index: string]: MarkSpec } = { href: {}, location: { default: null }, title: { default: null }, - guid: { default: null }, - docref: { default: false } + docref: { default: false } // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text }, inclusive: false, parseDOM: [{ diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index c84d98df9..987bc4f58 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -302,16 +302,16 @@ export class TooltipTextMenu { { handlers: { dragComplete: action(() => { - let linkDoc = dragData.linkDocument; - let guid = Utils.GenerateGuid(); - let proto = Doc.GetProto(linkDoc); - if (proto && docView) { - proto.sourceContext = docView.props.ContainingCollectionDoc; - } - linkDoc.guid = guid; - let text = this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab", guid); - if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) { - proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link + if (dragData.linkDocument) { + let linkDoc = dragData.linkDocument; + let proto = Doc.GetProto(linkDoc); + if (proto && docView) { + proto.sourceContext = docView.props.ContainingCollectionDoc; + } + let text = this.makeLink(linkDoc, ctrlKey ? "onRight" : "inTab"); + if (linkDoc instanceof Doc && linkDoc.anchor2 instanceof Doc) { + proto.title = text === "" ? proto.title : text + " to " + linkDoc.anchor2.title; // TODODO open to more descriptive descriptions of following in text link + } } }), }, @@ -398,9 +398,10 @@ export class TooltipTextMenu { // let link = state.schema.mark(state.schema.marks.link, { href: target, location: location }); // } - makeLink = (target: string, location: string, guid?: string): string => { + makeLink = (targetDoc: Doc, location: string): string => { + let target = Utils.prepend("/doc/" + targetDoc[Id]); let node = this.view.state.selection.$from.nodeAfter; - let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: guid }); + let link = this.view.state.schema.mark(this.view.state.schema.marks.link, { href: target, location: location, guid: targetDoc[Id] }); this.view.dispatch(this.view.state.tr.removeMark(this.view.state.selection.from, this.view.state.selection.to, this.view.state.schema.marks.link)); this.view.dispatch(this.view.state.tr.addMark(this.view.state.selection.from, this.view.state.selection.to, link)); node = this.view.state.selection.$from.nodeAfter; diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 1280ae28b..cad404d1f 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -243,7 +243,7 @@ export class LinkFollowBox extends React.Component { let proto = Doc.GetProto(LinkFollowBox.linkDoc); let targetContext = await Cast(proto.targetContext, Doc); let sourceContext = await Cast(proto.sourceContext, Doc); - let guid = StrCast(LinkFollowBox.linkDoc.guid); + let guid = StrCast(LinkFollowBox.linkDoc[Id]); const shouldZoom = options ? options.shouldZoom : false; let dockingFunc = (document: Doc) => { (this._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; @@ -255,12 +255,10 @@ export class LinkFollowBox extends React.Component { DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) { if (guid) { - LinkFollowBox.destinationDoc.guid = guid; + let views = DocumentManager.Instance.getDocumentViews(jumpToDoc); + views.length && (views[0].props.Document.scrollToLinkID = guid); } else { jumpToDoc.linkHref = Utils.prepend("/doc/" + StrCast(LinkFollowBox.linkDoc[Id])); - let newguid = Utils.GenerateGuid(); - LinkFollowBox.linkDoc.guid = newguid; - LinkFollowBox.destinationDoc.guid = newguid; } } } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 53f28ac00..0a8b841a9 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -80,6 +80,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private _nodeClicked: any; private _undoTyping?: UndoManager.Batch; private _searchReactionDisposer?: Lambda; + private _guidReactionDisposer: Opt; private _reactionDisposer: Opt; private _textReactionDisposer: Opt; private _heightReactionDisposer: Opt; @@ -139,66 +140,50 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); } - this.props.Document.guid = undefined; - this.props.Document.linkHref = undefined; - - reaction( - () => StrCast(this.props.Document.guid), - async (guid) => { - let start = -1; - let href = this.props.Document.linkHref; - - if (this._editorView && guid) { - let editor = this._editorView; - let ret = findLinkFrag(editor.state.doc.content, editor); - - if (ret.frag.size > 2) { - let tr; - if (ret.frag.firstChild) { - let between = TextSelection.between(editor.state.doc.resolve(ret.start + 2), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); - tr = editor.state.tr.setSelection(between); - } else { - let near = TextSelection.near(editor.state.doc.resolve(ret.start)); - tr = editor.state.tr.setSelection(near); - } - - editor.focus(); - editor.dispatch(tr.scrollIntoView()); - editor.dispatch(tr.scrollIntoView()); // bcz: sometimes selection doesn't fully scroll into view on smaller text boxes <5 lines visibility -- hopefully avoidable by ppl just not using small boxes...? - - this.props.Document.guid = undefined; - this.props.Document.linkHref = undefined; - } - } - - function findLinkFrag(frag: Fragment, editor: EditorView) { + this._guidReactionDisposer = 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 !== "") { + if (examinedNode && examinedNode.textContent) { nodes.push(examinedNode); start += index; } }); return { frag: Fragment.fromArray(nodes), start: start }; } - function findLinkNode(node: Node, editor: EditorView) { + 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.name === "link"); - if (linkIndex !== -1) { - if (guid === marks[linkIndex].attrs.guid) { - return node; - } else if (href && href === marks[linkIndex].attrs.href) { // retroactively fixing old in-text links by adding guid - marks[linkIndex].attrs.guid = guid; - return node; + 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; } - return undefined; } + } ); } @@ -759,6 +744,13 @@ 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 r1 = (ref.node as any).getBoundingClientRect(); + let r3 = self._ref.current!.getBoundingClientRect(); + 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); }, @@ -799,6 +791,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } componentWillUnmount() { + this._guidReactionDisposer && this._guidReactionDisposer(); this._rulesReactionDisposer && this._rulesReactionDisposer(); this._reactionDisposer && this._reactionDisposer(); this._proxyReactionDisposer && this._proxyReactionDisposer(); @@ -823,7 +816,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let ctrlKey = e.ctrlKey; if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) { let href = (e.target as any).href; - let guid = (e.target as any).guid; let location: string; if ((e.target as any).attributes.location) { location = (e.target as any).attributes.location.value; -- cgit v1.2.3-70-g09d2 From 910a3de2c294b1f5e074eef3cbfe043462ef22dd Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 23 Sep 2019 13:29:48 -0400 Subject: from last --- src/client/views/nodes/FormattedTextBox.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 0a8b841a9..cc73c32a1 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -80,7 +80,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private _nodeClicked: any; private _undoTyping?: UndoManager.Batch; private _searchReactionDisposer?: Lambda; - private _guidReactionDisposer: Opt; + private _scroolToRegionReactionDisposer: Opt; private _reactionDisposer: Opt; private _textReactionDisposer: Opt; private _heightReactionDisposer: Opt; @@ -140,7 +140,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); } - this._guidReactionDisposer = reaction( + this._scroolToRegionReactionDisposer = reaction( () => StrCast(this.props.Document.scrollToLinkID), async (scrollToLinkID) => { let findLinkFrag = (frag: Fragment, editor: EditorView) => { @@ -791,7 +791,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } componentWillUnmount() { - this._guidReactionDisposer && this._guidReactionDisposer(); + this._scroolToRegionReactionDisposer && this._scroolToRegionReactionDisposer(); this._rulesReactionDisposer && this._rulesReactionDisposer(); this._reactionDisposer && this._reactionDisposer(); this._proxyReactionDisposer && this._proxyReactionDisposer(); -- cgit v1.2.3-70-g09d2 From 0d281d479cd023fbc8bbd8206a66f0283eae11af Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 23 Sep 2019 19:11:14 -0400 Subject: fixed exception in scrolltoposition for text. --- src/client/views/nodes/FormattedTextBox.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index cc73c32a1..ddabe7e10 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -746,9 +746,11 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe 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 r1 = (ref.node as any).getBoundingClientRect(); + let refNode = ref.node as any; + while (refNode && !("getBoundingClientRect" in refNode)) refNode = refNode.parentElement; + let r1 = refNode && (refNode as any).getBoundingClientRect(); let r3 = self._ref.current!.getBoundingClientRect(); - self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale; + r1 && (self._ref.current!.scrollTop += (r1.top - r3.top) * self.props.ScreenToLocalTransform().Scale); return true; }, dispatchTransaction: this.dispatchTransaction, -- cgit v1.2.3-70-g09d2 From 0e08e20021a4fdb3235cb13b2a288ad8a3705529 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 23 Sep 2019 21:51:02 -0400 Subject: allowed resizing of bullets in a more reasonable way. restore use_marks. --- src/client/util/ProsemirrorExampleTransfer.ts | 48 +++++++++++++++------------ src/client/util/RichTextSchema.tsx | 7 ++-- src/client/util/TooltipTextMenu.tsx | 10 +++--- src/client/util/prosemirrorPatches.js | 2 +- src/client/views/nodes/FormattedTextBox.scss | 20 +++++------ src/client/views/nodes/FormattedTextBox.tsx | 15 +++++++-- 6 files changed, 61 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 3e3d3155c..aab437176 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -11,6 +11,20 @@ const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : export type KeyMap = { [key: string]: any }; +export let updateBullets = (tx2: Transaction, schema: Schema) => { + let fontSize: number | undefined = undefined; + tx2.doc.descendants((node: any, offset: any, index: any) => { + if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) { + let path = (tx2.doc.resolve(offset) as any).path; + let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0); + if (node.type === schema.nodes.ordered_list) depth++; + fontSize = depth === 1 && node.attrs.setFontSize ? Number(node.attrs.setFontSize) : fontSize; + let fsize = fontSize && node.type === schema.nodes.ordered_list ? Math.max(6, fontSize - (depth - 1) * 4) : undefined; + tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle: node.attrs.mapStyle, bulletStyle: depth, inheritedFontSize: fsize }, node.marks); + } + }); + return tx2; +}; export default function buildKeymap>(schema: S, mapKeys?: KeyMap): KeyMap { let keys: { [key: string]: any } = {}, type; @@ -93,16 +107,6 @@ export default function buildKeymap>(schema: S, mapKeys?: bind("Mod-s", TooltipTextMenu.insertStar); - let updateBullets = (tx2: Transaction) => { - tx2.doc.descendants((node: any, offset: any, index: any) => { - if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) { - let path = (tx2.doc.resolve(offset) as any).path; - let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && c.type === schema.nodes.ordered_list ? 1 : 0), 0); - if (node.type === schema.nodes.ordered_list) depth++; - tx2.setNodeMarkup(offset, node.type, { ...node.attrs, mapStyle: node.attrs.mapStyle, bulletStyle: depth }, node.marks); - } - }); - }; bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -110,18 +114,18 @@ export default function buildKeymap>(schema: S, mapKeys?: var range = ref.$from.blockRange(ref.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { - updateBullets(tx2); - marks && tx2.ensureMarks([...marks]); - marks && tx2.setStoredMarks([...marks]); - dispatch(tx2); + let tx3 = updateBullets(tx2, schema); + marks && tx3.ensureMarks([...marks]); + marks && tx3.setStoredMarks([...marks]); + dispatch(tx3); })) { // couldn't sink into an existing list, so wrap in a new one let newstate = state.applyTransaction(state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end))); if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => { - updateBullets(tx2); + let tx3 = updateBullets(tx2, schema); // when promoting to a list, assume list will format things so don't copy the stored marks. - marks && tx2.ensureMarks([...marks]); - marks && tx2.setStoredMarks([...marks]); - dispatch(tx2); + marks && tx3.ensureMarks([...marks]); + marks && tx3.setStoredMarks([...marks]); + dispatch(tx3); })) { console.log("bullet promote fail"); } @@ -132,10 +136,10 @@ export default function buildKeymap>(schema: S, mapKeys?: var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => { - updateBullets(tx2); - marks && tx2.ensureMarks([...marks]); - marks && tx2.setStoredMarks([...marks]); - dispatch(tx2); + let tx3 = updateBullets(tx2, schema); + marks && tx3.ensureMarks([...marks]); + marks && tx3.setStoredMarks([...marks]); + dispatch(tx3); })) { console.log("bullet demote fail"); } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 9d5ccffe9..710d55605 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -198,6 +198,8 @@ export const nodes: { [index: string]: NodeSpec } = { attrs: { bulletStyle: { default: 0 }, mapStyle: { default: "decimal" }, + setFontSize: { default: undefined }, + inheritedFontSize: { default: undefined }, visibility: { default: true } }, toDOM(node: Node) { @@ -205,8 +207,9 @@ export const nodes: { [index: string]: NodeSpec } = { const decMap = bs ? "decimal" + bs : ""; const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : ""; let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap; - return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0] : - ['ol', { class: `${map}-ol`, style: `list-style: none;` }]; + let fsize = node.attrs.setFontSize ? node.attrs.setFontSize : node.attrs.inheritedFontSize; + return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none;font-size: ${fsize}` }, 0] : + ['ol', { class: `${map}-ol`, style: `list-style: none; font-size: ${fsize}` }]; } }, diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 987bc4f58..a83a3949d 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -19,6 +19,7 @@ import { LinkManager } from "./LinkManager"; import { schema } from "./RichTextSchema"; import "./TooltipTextMenu.scss"; import { Cast, NumCast } from '../../new_fields/Types'; +import { updateBullets } from './ProsemirrorExampleTransfer'; const { toggleMark, setBlockType } = require("prosemirror-commands"); const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js"); @@ -518,10 +519,11 @@ export class TooltipTextMenu { } } //actually apply font - return toggleMark(markType)(view.state, view.dispatch, view); - } - else { - return; + if ((view.state.selection as any).node && (view.state.selection as any).node.type === view.state.schema.nodes.ordered_list) { + view.dispatch(updateBullets(view.state.tr.setNodeMarkup(view.state.selection.from, (view.state.selection as any).node.type, + { ...(view.state.selection as NodeSelection).node.attrs, setFontSize: Number(markType.name.replace(/p/, "")) }), view.state.schema)); + } + else toggleMark(markType)(view.state, view.dispatch, view); } } diff --git a/src/client/util/prosemirrorPatches.js b/src/client/util/prosemirrorPatches.js index 188e3e1c5..269423482 100644 --- a/src/client/util/prosemirrorPatches.js +++ b/src/client/util/prosemirrorPatches.js @@ -82,7 +82,7 @@ function sinkListItem(itemType) { if (dispatch) { var nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type; var inner = prosemirrorModel.Fragment.from(nestedBefore ? itemType.create() : null); - let slice = new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(itemType.create(null, prosemirrorModel.Fragment.from(parent.type.create(parent.attrs, inner)))), + let slice = new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(itemType.create(null, prosemirrorModel.Fragment.from(parent.type.create({ ...parent.attrs, fontSize: parent.attrs.fontSize ? parent.attrs.fontSize - 4 : undefined }, inner)))), nestedBefore ? 3 : 1, 0); var before = range.start, after = range.end; dispatch(state.tr.step(new prosemirrorTransform.ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after, diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 0d7277cbe..435f5c055 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 ddabe7e10..47b64e260 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -910,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 })); + } + } } } } @@ -961,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"); -- cgit v1.2.3-70-g09d2 From c5ae3ee0f24b980ac6d9669aded7cd3d51047cb4 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 24 Sep 2019 16:32:31 -0400 Subject: fixed moving to not redraw objects. pdf changes started. --- .../collectionFreeForm/CollectionFreeFormView.tsx | 260 +++++++++++---------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 16 +- src/client/views/nodes/PDFBox.scss | 12 +- src/client/views/nodes/PDFBox.tsx | 64 +++-- src/client/views/pdf/PDFViewer.scss | 7 +- src/client/views/pdf/PDFViewer.tsx | 184 +++------------ 6 files changed, 211 insertions(+), 332 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 36e62842c..438529596 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, faPaintBrush, faTable, faUpload } from "@fortawesome/free-solid-svg-icons"; -import { action, computed, IReactionDisposer, observable, reaction, trace } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, trace, ObservableMap, untracked } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCastAsync, Field, FieldResult, HeightSym, Opt, WidthSym, DocListCast } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -60,6 +60,17 @@ export interface ViewDefBounds { z?: number; width: number; height: number; + transition?: string; +} + +interface PivotData { + type: string; + text: string; + x: number; + y: number; + width: number; + height: number; + fontSize: number; } export interface ViewDefResult { @@ -67,116 +78,6 @@ export interface ViewDefResult { bounds?: ViewDefBounds; } -export namespace PivotView { - - export interface PivotData { - type: string; - text: string; - x: number; - y: number; - width: number; - height: number; - fontSize: number; - } - - export const elements = (target: CollectionFreeFormView) => { - let collection = target.Document; - const field = StrCast(collection.pivotField) || "title"; - const width = NumCast(collection.pivotWidth) || 200; - const groups = new Map, Doc[]>(); - - for (const doc of target.childDocs) { - const val = doc[field]; - if (val === undefined) continue; - - const l = groups.get(val); - if (l) { - l.push(doc); - } else { - groups.set(val, [doc]); - } - } - - let minSize = Infinity; - - groups.forEach((val, key) => minSize = Math.min(minSize, val.length)); - - const numCols = NumCast(collection.pivotNumColumns) || Math.ceil(Math.sqrt(minSize)); - const fontSize = NumCast(collection.pivotFontSize); - - const docMap = new Map(); - const groupNames: PivotData[] = []; - - let x = 0; - groups.forEach((val, key) => { - let y = 0; - let xCount = 0; - groupNames.push({ - type: "text", - text: String(key), - x, - y: width + 50, - width: width * 1.25 * numCols, - height: 100, fontSize: fontSize - }); - for (const doc of val) { - docMap.set(doc, { - x: x + xCount * width * 1.25, - y: -y, - width, - height: width - }); - xCount++; - if (xCount >= numCols) { - xCount = 0; - y += width * 1.25; - } - } - x += width * 1.25 * (numCols + 1); - }); - - let elements = target.viewDefsToJSX(groupNames); - let docViews = target.childDocs.reduce((prev, doc) => { - let minim = BoolCast(doc.isMinimized); - if (minim === undefined || !minim) { - let defaultPosition = (): ViewDefBounds => { - return { - x: NumCast(doc.x), - y: NumCast(doc.y), - z: NumCast(doc.z), - width: NumCast(doc.width), - height: NumCast(doc.height) - }; - }; - const pos = docMap.get(doc) || defaultPosition(); - prev.push({ - ele: , - bounds: { - x: pos.x, - y: pos.y, - z: pos.z, - width: NumCast(pos.width), - height: NumCast(pos.height) - } - }); - } - return prev; - }, elements); - - return docViews; - }; - -} - type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>; const PanZoomDocument = makeInterface(panZoomSchema, documentSchema, positionSchema, pageSchema); @@ -300,7 +201,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let y = (z ? ypo : yp) - de.data.offset[1]; let dropX = NumCast(de.data.droppedDocuments[0].x); let dropY = NumCast(de.data.droppedDocuments[0].y); - de.data.droppedDocuments.forEach(d => { + de.data.droppedDocuments.forEach(action((d: Doc) => { d.x = x + NumCast(d.x) - dropX; d.y = y + NumCast(d.y) - dropY; if (!NumCast(d.width)) { @@ -312,7 +213,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { d.height = nw && nh ? nh / nw * NumCast(d.width) : 300; } this.bringToFront(d); - }); + })); de.data.droppedDocuments.length === 1 && this.updateCluster(de.data.droppedDocuments[0]); } @@ -645,6 +546,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getScale = () => this.Document.scale ? this.Document.scale : 1; getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { + trace(); return { DataDoc: childData, Document: childLayout, @@ -742,9 +644,99 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } - @computed.struct - get elements() { - if (this.Document.usePivotLayout) return PivotView.elements(this); + lookupLayout = (doc: Doc, dataDoc?: Doc) => { + let data: any = undefined; + let compute = this.Document.usePivotLayout ? this.doPivotComputation.map : this._doComputation.map; + compute.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 } : undefined; + } + + @computed get doPivotComputation() { + let layoutPoolData: Map<{ layout: Doc, data?: Doc }, any> = new Map(); + const field = StrCast(this.props.Document.pivotField) || "title"; + const width = NumCast(this.props.Document.pivotWidth) || 200; + const groups = new Map, Doc[]>(); + + for (const doc of this.childDocs) { + const val = doc[field]; + if (val === undefined) continue; + + const l = groups.get(val); + if (l) { + l.push(doc); + } else { + groups.set(val, [doc]); + } + } + + let minSize = Infinity; + + groups.forEach((val, key) => minSize = Math.min(minSize, val.length)); + + const numCols = NumCast(this.props.Document.pivotNumColumns) || Math.ceil(Math.sqrt(minSize)); + const fontSize = NumCast(this.props.Document.pivotFontSize); + + const docMap = new Map(); + const groupNames: PivotData[] = []; + + let x = 0; + groups.forEach((val, key) => { + let y = 0; + let xCount = 0; + groupNames.push({ + type: "text", + text: String(key), + x, + y: width + 50, + width: width * 1.25 * numCols, + height: 100, fontSize: fontSize + }); + for (const doc of val) { + docMap.set(doc, { + x: x + xCount * width * 1.25, + y: -y, + width, + height: width, + }); + xCount++; + if (xCount >= numCols) { + xCount = 0; + y += width * 1.25; + } + } + x += width * 1.25 * (numCols + 1); + }); + + let elements = this.viewDefsToJSX(groupNames); + let pairs = this.childLayoutPairs; + pairs.map((pair, i) => { + let minim = BoolCast(pair.layout.isMinimized); + if (minim === undefined || !minim) { + let defaultPosition = (): ViewDefBounds => { + return { + x: NumCast(pair.layout.x), + y: NumCast(pair.layout.y), + z: NumCast(pair.layout.z), + width: NumCast(pair.layout.width), + height: NumCast(pair.layout.height), + transition: "transform 1s" + }; + }; + const pos = docMap.get(pair.layout) || defaultPosition(); + layoutPoolData.set(pair, { transition: "transform 1s", ...pos }); + } + }); + return { map: layoutPoolData, elements: elements }; + }; + + + @computed + get _doComputation() { + let layoutPoolData: Map<{ layout: Doc, data?: Doc }, any> = new Map(); let curPage = FieldValue(this.Document.curPage, -1); const initScript = this.Document.arrangeInit; let state: any = undefined; @@ -759,24 +751,40 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { elements = this.viewDefsToJSX(views); } } - let docviews = pairs.reduce((prev, pair) => { + pairs.map((pair, i) => { var page = NumCast(pair.layout.page, -1); if (!pair.layout.isMinimized && ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1)) { - const pos = this.getCalculatedPositions({ doc: pair.layout, index: prev.length, collection: this.Document, docs: pairs.map(pair => pair.layout), state }); + const pos = this.getCalculatedPositions({ doc: pair.layout, index: i, collection: this.Document, docs: pairs.map(pair => pair.layout), state }); state = pos.state === undefined ? state : pos.state; + layoutPoolData.set(pair, pos); + } + }); + return { map: layoutPoolData, elements: elements }; + } + + @computed + get doComputation() { + let dc = this.Document.usePivotLayout ? this.doPivotComputation : this._doComputation; + let curPage = FieldValue(this.Document.curPage, -1); + let pairs = this.childLayoutPairs; + let docviews = pairs.reduce((prev, pair) => { + var page = NumCast(pair.layout.page, -1); + if (!pair.layout.isMinimized && ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1)) { prev.push({ - ele: , - bounds: { x: pos.x || 0, y: pos.y || 0, z: pos.z, width: pos.width || 0, height: pos.height || 0 } + jitterRotation={NumCast(this.props.Document.jitterRotation)} {...this.getChildDocumentViewProps(pair.layout, pair.data)} />, + bounds: this.lookupLayout(pair.layout, pair.data) }); } return prev; - }, elements); + }, dc.elements); - return docviews; + return { map: dc.map, elements: docviews }; + } + @computed.struct + get elements() { + return this.doComputation.elements; } @computed.struct @@ -945,7 +953,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { - + {this.childViews} diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9685f9bca..0d9ace473 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,11 +33,12 @@ export const PositionDocument = makeInterface(documentSchema, positionSchema); @observer export class CollectionFreeFormDocumentView extends DocComponent(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); } @@ -99,6 +101,8 @@ export class CollectionFreeFormDocumentView extends DocComponent(PdfDocumen componentDidMount() { this.props.setPdfBox && this.props.setPdfBox(this); - this.props.Document.curPage = ComputedField.MakeFunction("Math.floor(Number(this.panY) / Number(this.nativeHeight) + 1)"); + this.props.Document.curPage = 1; // 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() { @@ -63,36 +59,36 @@ export class PDFBox extends DocComponent(PdfDocumen } public GetPage() { - return Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1; + return 1;//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); - } + // 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); - } + // if (p > 0 && p <= NumCast(this.dataDoc.numPages)) { + // this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0); + // } } @action public ForwardPage() { - let cp = this.GetPage() + 1; - if (cp <= NumCast(this.dataDoc.numPages)) { - this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0); - } + // 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; + //this.Document.panY = y; } @action @@ -120,8 +116,7 @@ export class PDFBox extends DocComponent(PdfDocumen settingsPanel() { return !this.props.active() ? (null) : (
e.stopPropagation()}> -
- -
- ); - } - onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 ContextMenu.Instance.addItem({ description: "PDFOptions", event: emptyFunction, icon: "file-pdf" }); } } - setPdfBox = (pdfBox: PDFBox) => { this._pdfBox = pdfBox; }; - subView = (_type: CollectionViewType, renderProps: CollectionRenderProps) => { - return (<> - - {renderProps.active() ? this.uIButtons : (null)} - ); + return (); } render() { diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index cbea47e20..4ceda1986 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -106,4 +106,86 @@ grid-template-columns: 47.5% 5% 47.5%; } } -} \ No newline at end of file +} + +.pdfViewer-overlayCont { + position: absolute; + width: 100%; + height: 100px; + background: #121721; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + padding: 20px; + overflow: hidden; + transition: left .5s; + + .pdfViewer-overlaySearchBar { + width: 70%; + height: 100%; + font-size: 30px; + padding: 5px; + } +} + +.pdfViewer-overlayButton { + border-bottom-left-radius: 50%; + display: flex; + justify-content: space-evenly; + align-items: center; + height: 70px; + background: none; + padding: 0; + position: absolute; + + .pdfViewer-overlayButton-arrow { + width: 0; + height: 0; + border-top: 25px solid transparent; + border-bottom: 25px solid transparent; + border-right: 25px solid #121721; + transition: all 0.5s; + } + + .pdfViewer-overlayButton-iconCont { + background: #121721; + height: 50px; + width: 70px; + display: flex; + justify-content: center; + align-items: center; + margin-left: -2px; + border-radius: 3px; + } +} + +.pdfViewer-overlayButton:hover { + background: none; +} +.collectionPdfView-buttonTray { + top: 15px; + left: 20px; + position: relative; + transform-origin: left top; + position: absolute; +} +.collectionPdfView-backward { + color: white; + font-size: 24px; + top: 0px; + left: 0px; + position: absolute; + background-color: rgba(50, 50, 50, 0.2); +} + +.collectionPdfView-forward { + color: white; + font-size: 24px; + top: 0px; + left: 45px; + position: absolute; + background-color: rgba(50, 50, 50, 0.2); +} + + diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 12a5bc492..f57ec406c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -62,6 +62,12 @@ export class PDFBox extends DocComponent(PdfDocumen public search(string: string) { this._pdfViewer && this._pdfViewer.search(string); } + public prevAnnotation() { + this._pdfViewer && this._pdfViewer.prevAnnotation(); + } + public nextAnnotation() { + this._pdfViewer && this._pdfViewer.nextAnnotation(); + } setPdfViewer = (pdfViewer: PDFViewer) => { this._pdfViewer = pdfViewer; @@ -122,46 +128,88 @@ export class PDFBox extends DocComponent(PdfDocumen private newValueChange = (e: React.ChangeEvent) => this._valueValue = e.currentTarget.value; private newScriptChange = (e: React.ChangeEvent) => this._scriptValue = e.currentTarget.value; + searchStringChanged = (e: React.ChangeEvent) => this._searchString = e.currentTarget.value; + private _searchString: string = ""; settingsPanel() { return !this.props.active() ? (null) : - (
e.stopPropagation()}> - +
+ -
-
- Annotation View Settings -
-
- - -
-
- -
-
- - + + + + +
e.stopPropagation()}> + +
+
+ Annotation View Settings +
+
+ + +
+
+ +
+
+ + +
-
); + ); } loaded = (nw: number, nh: number, np: number) => { diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 4388bc64c..0ca3fa2d3 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -33,6 +33,8 @@ } } + + .pdfViewer-overlayButton { border-bottom-left-radius: 50%; display: flex; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 899a0f5aa..01f19ebf6 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -238,8 +238,7 @@ export class PDFViewer extends React.Component { } @action - prevAnnotation = (e: React.MouseEvent) => { - e.stopPropagation(); + prevAnnotation = () => { this.Index = Math.max(this.Index - 1, 0); let scrollToAnnotation = this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]; this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); @@ -248,8 +247,7 @@ export class PDFViewer extends React.Component { } @action - nextAnnotation = (e: React.MouseEvent) => { - e.stopPropagation(); + nextAnnotation = () => { this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1); let scrollToAnnotation = this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]; this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); @@ -529,16 +527,6 @@ export class PDFViewer extends React.Component { {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )}
- -
); } } -- cgit v1.2.3-70-g09d2 From 6d01b67aab6a6169b189002fc9c00477d55ca113 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 25 Sep 2019 14:26:26 -0400 Subject: pdf rendering is working, I think. Annotation documents aren't aligned at all. --- .../views/collections/CollectionDockingView.tsx | 12 +++- src/client/views/nodes/PDFBox.scss | 69 +++++++--------------- src/client/views/nodes/PDFBox.tsx | 61 ++++++++----------- src/client/views/pdf/PDFViewer.scss | 37 ------------ src/client/views/pdf/PDFViewer.tsx | 9 +-- 5 files changed, 56 insertions(+), 132 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8fcba99e3..e5d652648 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -9,7 +9,7 @@ import * as ReactDOM from 'react-dom'; import Measure from "react-measure"; import * as GoldenLayout from "../../../client/goldenLayout"; import { DateField } from '../../../new_fields/DateField'; -import { Doc, DocListCast, Field, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Field, Opt, WidthSym, HeightSym } from "../../../new_fields/Doc"; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { FieldId } from "../../../new_fields/RefField"; @@ -30,6 +30,7 @@ import "./CollectionDockingView.scss"; import { SubCollectionViewProps } from "./CollectionSubView"; import React = require("react"); import { ButtonSelector } from './ParentDocumentSelector'; +import { DocumentType } from '../../documents/DocumentTypes'; library.add(faFile); @observer @@ -595,12 +596,17 @@ export class DockedFrameRenderer extends React.Component { } panelWidth = () => this._document!.ignoreAspect ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); - panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), NumCast(this._document!.nativeHeight, this._panelHeight))); + panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight())); nativeWidth = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; nativeHeight = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; contentScaling = () => { + if (this._document!.type === DocumentType.PDF) { + if (this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) + return this._panelWidth / NumCast(this._document!.nativeWidth); + else return this._panelHeight / NumCast(this._document!.nativeHeight); + } const nativeH = this.nativeHeight(); const nativeW = this.nativeWidth(); if (!nativeW || !nativeH) return 1; @@ -619,6 +625,7 @@ export class DockedFrameRenderer extends React.Component { get previewPanelCenteringOffset() { return this.nativeWidth() && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth() / this.ScreenToLocalTransform().Scale) / 2 : 0; } addDocTab = (doc: Doc, dataDoc: Opt, location: string) => { + SelectionManager.DeselectAll(); if (doc.dockingConfig) { MainView.Instance.openWorkspace(doc); return true; @@ -635,6 +642,7 @@ export class DockedFrameRenderer extends React.Component { return (null); } let resolvedDataDoc = this._document.layout instanceof Doc ? this._document : this._dataDoc; + console.log("Wid = " + this.panelWidth() + " " + this.panelHeight()); return (PdfDocumen componentDidMount() { this.props.setPdfBox && this.props.setPdfBox(this); - this.props.Document.curPage = 1; // ComputedField.MakeFunction("Math.floor(Number(this.panY) / Number(this.nativeHeight) + 1)"); const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); if (pdfUrl instanceof PdfField) { @@ -74,31 +73,25 @@ export class PDFBox extends DocComponent(PdfDocumen } public GetPage() { - return Math.floor((this.Document.panY || 0) / (this.Document.nativeHeight || 0)) + 1; + return this._pdfViewer!._pdfViewer.currentPageNumber; } @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); - } + this._pdfViewer!._pdfViewer.scrollPageIntoView({ pageNumber: Math.max(1, this.GetPage() - 1) }); + this.props.Document.curPage = this.GetPage(); } @action public GotoPage = (p: number) => { - if (p > 0 && p <= NumCast(this.dataDoc.numPages)) { - this.Document.panY = (p - 1) * (this.Document.nativeHeight || 0); - } + this._pdfViewer!._pdfViewer.scrollPageIntoView(p); + this.props.Document.curPage = this.GetPage(); } @action public ForwardPage() { - let cp = this.GetPage() + 1; - if (cp <= NumCast(this.dataDoc.numPages)) { - this.Document.panY = (cp - 1) * (this.Document.nativeHeight || 0); - } + this._pdfViewer!._pdfViewer.scrollPageIntoView({ pageNumber: Math.min(this._pdfViewer!._pdfViewer.pagesCount, this.GetPage() + 1) }); + this.props.Document.curPage = this.GetPage(); } @action @@ -133,39 +126,39 @@ export class PDFBox extends DocComponent(PdfDocumen settingsPanel() { return !this.props.active() ? (null) : (<> -
e.stopPropagation()} +
e.stopPropagation()} style={{ bottom: 0, left: `${this._searching ? 0 : 100}%` }}> - +
- - - - -
e.stopPropagation()}> -
+
Annotation View Settings
@@ -213,8 +200,6 @@ export class PDFBox extends DocComponent(PdfDocumen } loaded = (nw: number, nh: number, np: number) => { - // nh *= .33.33333; - // nw *= 1.33333; this.dataDoc.numPages = np; if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) { let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 0ca3fa2d3..456eea7a1 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -32,41 +32,4 @@ opacity: 0.1; } } - - - - .pdfViewer-overlayButton { - border-bottom-left-radius: 50%; - display: flex; - justify-content: space-evenly; - align-items: center; - height: 70px; - background: none; - padding: 0; - position: absolute; - - .pdfViewer-overlayButton-arrow { - width: 0; - height: 0; - border-top: 25px solid transparent; - border-bottom: 25px solid transparent; - border-right: 25px solid #121721; - transition: all 0.5s; - } - - .pdfViewer-overlayButton-iconCont { - background: #121721; - height: 50px; - width: 70px; - display: flex; - justify-content: center; - align-items: center; - margin-left: -2px; - border-radius: 3px; - } - } - - .pdfViewer-overlayButton:hover { - background: none; - } } \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 01f19ebf6..bbd40d970 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -54,7 +54,6 @@ export class PDFViewer extends React.Component { @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; - private _resizeReaction: IReactionDisposer | undefined; private _annotationLayer: React.RefObject = React.createRef(); private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; @@ -109,7 +108,6 @@ export class PDFViewer extends React.Component { } componentWillUnmount = () => { - this._resizeReaction && this._resizeReaction(); this._reactionDisposer && this._reactionDisposer(); this._annotationReactionDisposer && this._annotationReactionDisposer(); this._filterReactionDisposer && this._filterReactionDisposer(); @@ -153,9 +151,7 @@ export class PDFViewer extends React.Component { @action setupPdfJsViewer = () => { - this._reactionDisposer = reaction(() => this.props.Document[WidthSym](), - () => this._pdfViewer.currentScaleValue = (this.props.Document[WidthSym]() / this._pageSizes[0].width)); - document.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = (this.props.Document[WidthSym]() / this._pageSizes[0].width)); + document.addEventListener("pagesinit", () => this._pdfViewer.currentScaleValue = 1); document.addEventListener("pagerendered", () => console.log("rendered")); var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ @@ -511,9 +507,8 @@ export class PDFViewer extends React.Component { } render() { - let scaling = this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width / this.props.Document[WidthSym]() : 1; return (
-
+
Date: Wed, 25 Sep 2019 17:53:05 -0400 Subject: now working with annotations. --- src/client/documents/Documents.ts | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 6 +- src/client/views/nodes/PDFBox.scss | 5 +- src/client/views/nodes/PDFBox.tsx | 3 +- src/client/views/pdf/PDFViewer.scss | 9 +++ src/client/views/pdf/PDFViewer.tsx | 84 ++++++++++++++++------ 6 files changed, 82 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4ae770e25..ea7a3a8b6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -144,7 +144,7 @@ export namespace Docs { options: { height: 32 } }], [DocumentType.PDF, { - layout: { view: PDFBox, collectionView: [CollectionPDFView, data, anno] as CollectionViewType }, + layout: { view: PDFBox }, options: { nativeWidth: 1200, curPage: 1 } }], [DocumentType.ICON, { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index af84a1d73..45c021c5f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble) { + if (!e.cancelBubble && this.props.layoutKey) { if (this._hitCluster && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); @@ -339,7 +339,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.props.Document.lockedPosition) return; + if (this.props.Document.lockedPosition || this.isAnnotationOverlay) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -699,7 +699,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document Doc.UpdateDocumentExtensionForField(this.props.DataDoc || this.props.Document, this.props.fieldKey); return ( -
(PdfDocumen e.button === 0 && e.stopPropagation(); } }}> - {this.settingsPanel()}
); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 456eea7a1..a561be94d 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -6,6 +6,15 @@ overflow-y: scroll; overflow-x: hidden; + // .canvasWrapper { + // transform: scale(0.75); + // transform-origin: top left; + // } + // .textLayer { + // transform: scale(0.75); + // transform-origin: top left; + // } + .page { position: relative; } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index bbd40d970..ea5e00d73 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -20,6 +20,10 @@ import PDFMenu from "./PDFMenu"; import "./PDFViewer.scss"; import React = require("react"); import requestPromise = require("request-promise"); +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { CollectionView } from "../collections/CollectionView"; +import { listSpec } from "../../../new_fields/Schema"; +import { Transform } from "../../util/Transform"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); interface IViewerProps { @@ -37,6 +41,8 @@ interface IViewerProps { pinToPres: (document: Doc) => void; addDocument?: (doc: Doc, allowDuplicates?: boolean) => boolean; setPdfViewer: (view: PDFViewer) => void; + ScreenToLocalTransform: () => Transform; + } /** @@ -438,22 +444,22 @@ export class PDFViewer extends React.Component { this._marqueeHeight = this._marqueeWidth = 0; } - else { - let sel = window.getSelection(); - if (sel && sel.type === "Range") { - let selRange = sel.getRangeAt(0); - this.createTextAnnotation(sel, selRange); - PDFMenu.Instance.jumpTo(e.clientX, e.clientY); - } - } - - if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined, "goldenrod"); - } - else { - PDFMenu.Instance.StartDrag = this.startDrag; - PDFMenu.Instance.Highlight = this.highlight; - } + // else { + // let sel = window.getSelection(); + // if (sel && sel.type === "Range") { + // let selRange = sel.getRangeAt(0); + // this.createTextAnnotation(sel, selRange); + // PDFMenu.Instance.jumpTo(e.clientX, e.clientY); + // } + // } + + // if (PDFMenu.Instance.Highlighting) { + // this.highlight(undefined, "goldenrod"); + // } + // else { + // PDFMenu.Instance.StartDrag = this.startDrag; + // PDFMenu.Instance.Highlight = this.highlight; + // } document.removeEventListener("pointermove", this.onSelectStart); document.removeEventListener("pointerup", this.onSelectEnd); } @@ -506,11 +512,36 @@ export class PDFViewer extends React.Component { DragManager.StartDocumentDrag([], new DragManager.DocumentDragData([view]), 0, 0); } + // this is called with the document that was dragged and the collection to move it into. + // if the target collection is the same as this collection, then the move will be allowed. + // otherwise, the document being moved must be able to be removed from its container before + // moving it into the target. + @action.bound + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { + return true; + } + return this.removeDocument(doc) ? addDocument(doc) : false; + } + + + @action.bound + removeDocument(doc: Doc): boolean { + //TODO This won't create the field if it doesn't already exist + let targetDataDoc = this.props.fieldExtensionDoc; + let targetField = "annotations"; + let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); + let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); + index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); + index !== -1 && value.splice(index, 1); + return true; + } + scrollXf = () => { + return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._mainCont.current.scrollTop) : this.props.ScreenToLocalTransform(); + } render() { - return (
-
-
-
+ return (
e.stopPropagation()} ref={this._mainCont}> +
{ {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )}
+ this._pageSizes.length && this._pageSizes[0] ? this.props.pdf.numPages * this._pageSizes[0].height : 300} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.AddDocToList(this.props.fieldExtensionDoc, "annotations", doc); return true; }} + CollectionView={this.props.ContainingCollectionView} + ScreenToLocalTransform={this.scrollXf} + ruleProvider={this.props.ruleProvider} + chromeCollapsed={true} + layoutKey={undefined} + backgroundLayout={undefined} > +
); } } -- cgit v1.2.3-70-g09d2 From f4b628c2a6810c1af51508685f12287a300d6e6f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 25 Sep 2019 22:26:54 -0400 Subject: all pdf annotations work? --- src/client/documents/Documents.ts | 7 +- src/client/views/DocumentButtonBar.tsx | 5 +- src/client/views/MainView.tsx | 7 +- src/client/views/TemplateMenu.tsx | 4 +- .../views/collections/CollectionDockingView.tsx | 6 +- .../views/collections/CollectionSchemaCells.tsx | 6 +- .../views/collections/CollectionSchemaView.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 8 +- .../CollectionStackingViewFieldColumn.tsx | 6 +- src/client/views/collections/CollectionSubView.tsx | 5 +- .../CollectionFreeFormLayoutEngines.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 18 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 13 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 3 +- src/client/views/nodes/FormattedTextBox.tsx | 6 +- src/client/views/nodes/PDFBox.scss | 3 + src/client/views/nodes/PDFBox.tsx | 20 ++-- src/client/views/pdf/PDFViewer.scss | 5 +- src/client/views/pdf/PDFViewer.tsx | 132 +++++++++++++-------- 20 files changed, 159 insertions(+), 109 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ea7a3a8b6..392dca373 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -95,12 +95,13 @@ export namespace Docs { export namespace Prototypes { - type LayoutSource = { LayoutString: () => string }; + type LayoutSource = { LayoutString: (ext?: string) => string }; type CollectionLayoutSource = { LayoutString: (fieldStr: string, fieldExt?: string) => string }; type CollectionViewType = [CollectionLayoutSource, string, string?]; type PrototypeTemplate = { layout: { view: LayoutSource, + ext?: string, // optional extension field for layout source collectionView?: CollectionViewType }, options?: Partial @@ -144,7 +145,7 @@ export namespace Docs { options: { height: 32 } }], [DocumentType.PDF, { - layout: { view: PDFBox }, + layout: { view: PDFBox, ext: anno }, options: { nativeWidth: 1200, curPage: 1 } }], [DocumentType.ICON, { @@ -254,7 +255,7 @@ export namespace Docs { // synthesize the default options, the type and title from computed values and // whatever options pertain to this specific prototype let options = { title: title, type: type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; - let primary = layout.view.LayoutString(); + let primary = layout.view.LayoutString(layout.ext); let collectionView = layout.collectionView; if (collectionView) { options.layout = collectionView[0].LayoutString(collectionView[1], collectionView[2]); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index b482e3298..9ca54f738 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -23,6 +23,7 @@ import React = require("react"); import { DocumentView } from './nodes/DocumentView'; import { ParentDocSelector } from './collections/ParentDocumentSelector'; import { CollectionDockingView } from './collections/CollectionDockingView'; +import { DocumentDecorations } from './DocumentDecorations'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -225,7 +226,7 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[], return (
{ - DocumentDecorations.hasPushedHack = false; + DocumentButtonBar.hasPushedHack = false; this.targetDoc[Pushes] = NumCast(this.targetDoc[Pushes]) + 1; }}> @@ -259,7 +260,7 @@ export class DocumentButtonBar extends React.Component<{ views: DocumentView[], window.open(`https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`); } else { this.clearPullColor(); - DocumentDecorations.hasPulledHack = false; + DocumentButtonBar.hasPulledHack = false; this.targetDoc[Pulls] = NumCast(this.targetDoc[Pulls]) + 1; dataDoc.unchanged && runInAction(() => this.isAnimatingFetch = true); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 003919866..244b217ed 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -230,7 +230,7 @@ export class MainView extends React.Component { } else { DocServer.GetRefField(CurrentUserUtils.MainDocId).then(field => { field instanceof Doc ? this.openWorkspace(field) : - this.createNewWorkspace(CurrentUserUtils.MainDocId) + this.createNewWorkspace(CurrentUserUtils.MainDocId); }); } } @@ -371,8 +371,9 @@ export class MainView extends React.Component { } flyoutWidthFunc = () => this.flyoutWidth; addDocTabFunc = (doc: Doc, data: Opt, where: string) => { - if (where === "close") + if (where === "close") { return CollectionDockingView.CloseRightSplit(doc); + } if (doc.dockingConfig) { this.openWorkspace(doc); return true; @@ -564,7 +565,7 @@ export class MainView extends React.Component { let next = () => PresBox.CurrentPresentation.next(); let back = () => PresBox.CurrentPresentation.back(); let startOrResetPres = () => PresBox.CurrentPresentation.startOrResetPres(); - let closePresMode = action(() => { PresBox.CurrentPresentation.presMode = false; this.addDocTabFunc(PresBox.CurrentPresentation.props.Document); }); + let closePresMode = action(() => { PresBox.CurrentPresentation.presMode = false; this.addDocTabFunc(PresBox.CurrentPresentation.props.Document, undefined, "onRight"); }); return !PresBox.CurrentPresentation || !PresBox.CurrentPresentation.presMode ? (null) : ; } diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index e4ef8313d..9e5e62e03 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -117,13 +117,13 @@ export class TemplateMenu extends React.Component { @action toggleChrome = (): void => { this.props.docs.map(dv => { - let layout = dv.Document.layout instanceof Doc ? dv.Document.layout as Doc : dv.Document; + let layout = dv.Document.layout instanceof Doc ? dv.Document.layout : dv.Document; layout.chromeStatus = (layout.chromeStatus !== "disabled" ? "disabled" : "enabled"); }); } render() { - let layout = this.props.docs[0].Document.layout instanceof Doc ? this.props.docs[0].Document.layout as Doc : this.props.docs[0].Document; + let layout = this.props.docs[0].Document.layout instanceof Doc ? this.props.docs[0].Document.layout : this.props.docs[0].Document; let templateMenu: Array = []; this.props.templates.forEach((checked, template) => templateMenu.push()); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index e5d652648..2d9faee6b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -603,9 +603,11 @@ export class DockedFrameRenderer extends React.Component { contentScaling = () => { if (this._document!.type === DocumentType.PDF) { - if (this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) + if (this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) { return this._panelWidth / NumCast(this._document!.nativeWidth); - else return this._panelHeight / NumCast(this._document!.nativeHeight); + } else { + return this._panelHeight / NumCast(this._document!.nativeHeight); + } } const nativeH = this.nativeHeight(); const nativeW = this.nativeWidth(); diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 4dac27e60..179e44266 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -34,7 +34,7 @@ export interface CellProps { row: number; col: number; rowProps: CellInfo; - CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionView: Opt; ContainingCollection: Opt; Document: Doc; fieldKey: string; @@ -151,7 +151,7 @@ export class CollectionSchemaCell extends React.Component { fieldExt: "", ruleProvider: undefined, ContainingCollectionView: this.props.CollectionView, - ContainingCollectionDoc: this.props.CollectionView.props.Document, + ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document, isSelected: returnFalse, select: emptyFunction, renderDepth: this.props.renderDepth + 1, @@ -301,7 +301,7 @@ export class CollectionSchemaCheckboxCell extends CollectionSchemaCell { render() { let reference = React.createRef(); let onItemDown = (e: React.PointerEvent) => { - (!this.props.CollectionView.props.isSelected() ? undefined : + (!this.props.CollectionView || !this.props.CollectionView.props.isSelected() ? undefined : SetupDrag(reference, () => this._document, this.props.moveDocument, this.props.Document.schemaDoc ? "copy" : undefined)(e)); }; return ( diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 7bd2a1971..8d931f812 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -246,7 +246,7 @@ export interface SchemaTableProps { PanelHeight: () => number; PanelWidth: () => number; childDocs?: Doc[]; - CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionView: Opt; ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; fieldKey: string; @@ -804,7 +804,7 @@ export class SchemaTable extends React.Component { csv.substring(0, csv.length - 1); let dbName = StrCast(this.props.Document.title); let res = await Gateway.Instance.PostSchema(csv, dbName); - if (self.props.CollectionView.props.addDocument) { + if (self.props.CollectionView && self.props.CollectionView.props.addDocument) { let schemaDoc = await Docs.Create.DBDocument("https://www.cs.brown.edu/" + dbName, { title: dbName }, { dbDoc: self.props.Document }); if (schemaDoc) { //self.props.CollectionView.props.addDocument(schemaDoc, false); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index ccf131797..597f3f745 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -42,7 +42,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } @computed get isStackingView() { return BoolCast(this.props.Document.singleColumn, true); } @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; } - @computed get showAddAGroup() { return (this.sectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')); } + @computed get showAddAGroup() { return (this.sectionFilter && this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.ContainingCollectionDoc.chromeStatus !== 'disabled')); } @computed get columnWidth() { return Math.min(this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : NumCast(this.props.Document.columnWidth, 250)); @@ -347,7 +347,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } onToggle = (checked: Boolean) => { - this.props.CollectionView.props.Document.chromeStatus = checked ? "collapsed" : "view-mode"; + this.props.ContainingCollectionDoc && (this.props.ContainingCollectionDoc.chromeStatus = checked ? "collapsed" : "view-mode"); } onContextMenu = (e: React.MouseEvent): void => { @@ -391,10 +391,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { style={{ width: this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
} - {this.props.CollectionView.props.Document.chromeStatus !== 'disabled' ? : null} diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index b3b7b40dd..240adf428 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -266,7 +266,7 @@ export class CollectionStackingViewFieldColumn extends React.Component {/* the default bucket (no key value) has a tooltip that describes what it is. Further, it does not have a color and cannot be deleted. */} @@ -297,7 +297,7 @@ export class CollectionStackingViewFieldColumn extends React.Component : (null); for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth / style.numGroupColumns}px `; return ( -
{headingView}
- {(this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.parent.props.CollectionView.props.Document.chromeStatus !== 'disabled') ? + {(this.props.parent.props.ContainingCollectionDoc && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'view-mode' && this.props.parent.props.ContainingCollectionDoc.chromeStatus !== 'disabled') ?
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 774e6b1b9..ce80526b2 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -1,7 +1,7 @@ import { action, computed, IReactionDisposer, reaction } from "mobx"; import * as rp from 'request-promise'; import CursorField from "../../../new_fields/CursorField"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; @@ -30,10 +30,11 @@ export interface CollectionViewProps extends FieldViewProps { PanelWidth: () => number; PanelHeight: () => number; chromeCollapsed: boolean; + setPreviewCursor?: (func: (x: number, y: number) => void) => void; } export interface SubCollectionViewProps extends CollectionViewProps { - CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionView: Opt; ruleProvider: Doc | undefined; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 21855b168..6135f3e45 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -90,9 +90,7 @@ export function computePivotLayout(pivotDoc: Doc, childDocs: Doc[], childPairs: layoutPoolData.set(pair, { transition: "transform 1s", ...pos }); }); return { map: layoutPoolData, elements: viewDefsToJSX(groupNames) }; -}; - - +} export function AddCustomFreeFormLayout(doc: Doc, dataKey: string): () => void { return () => { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 45c021c5f..075914e29 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -50,7 +50,7 @@ export const panZoomSchema = createSchema({ useClusters: "boolean", isRuleProvider: "boolean", fitToBox: "boolean", - panTransformType: "string" + panTransformType: "string", }); type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof documentSchema, typeof positionSchema, typeof pageSchema]>; @@ -61,6 +61,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private _lastX: number = 0; private _lastY: number = 0; private _clusterDistance: number = 75; + private _hitCluster = false; @observable _clusterSets: (Doc[])[] = []; @computed get fitToContent() { return (this.props.fitToBox || this.Document.fitToBox) && !this.isAnnotationOverlay; } @@ -265,7 +266,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return clusterColor; } - _hitCluster = false; @action onPointerDown = (e: React.PointerEvent): void => { this._hitCluster = this.props.Document.useClusters ? this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)) !== -1 : false; @@ -286,7 +286,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action onPointerMove = (e: PointerEvent): void => { - if (!e.cancelBubble && this.props.layoutKey) { + if (!e.cancelBubble && !this.isAnnotationOverlay) { if (this._hitCluster && this.tryDragCluster(e)) { e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers e.preventDefault(); @@ -451,7 +451,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { PanelHeight: childLayout[HeightSym], ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, - ContainingCollectionDoc: this.props.CollectionView.props.Document, + ContainingCollectionDoc: this.props.ContainingCollectionDoc, focus: this.focusDocument, backgroundColor: this.getClusterColor, parentActive: this.props.active, @@ -478,7 +478,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { PanelHeight: layoutDoc[HeightSym], ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, - ContainingCollectionDoc: this.props.CollectionView.props.Document, + ContainingCollectionDoc: this.props.ContainingCollectionDoc, focus: this.focusDocument, backgroundColor: returnEmptyString, parentActive: this.props.active, @@ -699,10 +699,10 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document Doc.UpdateDocumentExtensionForField(this.props.DataDoc || this.props.Document, this.props.fieldKey); return ( -
@@ -725,7 +725,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { class CollectionFreeFormOverlayView extends React.Component boolean }> { render() { return + renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />; } } @@ -734,7 +734,7 @@ class CollectionFreeFormBackgroundView extends React.Component) + renderDepth={this.props.renderDepth} isSelected={this.props.isSelected} select={emptyFunction} />); } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index c85c3e55b..689a55ec4 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -31,6 +31,7 @@ interface MarqueeViewProps { addLiveTextDocument: (doc: Doc) => void; isSelected: () => boolean; isAnnotationOverlay: boolean; + setPreviewCursor?: (func: (x: number, y: number) => void) => void; } @observer @@ -44,6 +45,10 @@ export class MarqueeView extends React.Component @observable _visible: boolean = false; _commandExecuted = false; + componentDidMount() { + this.props.setPreviewCursor && this.props.setPreviewCursor(this.setPreviewCursor); + } + @action cleanupInteractions = (all: boolean = false) => { if (all) { @@ -203,11 +208,17 @@ export class MarqueeView extends React.Component } } + setPreviewCursor = (x: number, y: number) => { + this._downX = x; + this._downY = y; + PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); + } + @action onClick = (e: React.MouseEvent): void => { if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); + this.setPreviewCursor(e.clientX, e.clientY); // let the DocumentView stopPropagation of this event when it selects this document } else { // why do we get a click event when the cursor have moved a big distance? // let's cut it off here so no one else has to deal with it. diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index bcb26b4c4..cd183a984 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -12,7 +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 + dataProvider?: (doc: Doc, dataDoc?: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined; x?: number; y?: number; width?: number; @@ -99,8 +99,8 @@ export class CollectionFreeFormDocumentView extends DocComponent { return this.dataProvider ? this.dataProvider.width : this.panelWidth(); } - finalPanelHeight = () => { return this.dataProvider ? this.dataProvider.height : this.panelHeight(); } + finalPanelWidh = () => this.dataProvider ? this.dataProvider.width : this.panelWidth(); + finalPanelHeight = () => this.dataProvider ? this.dataProvider.height : this.panelHeight(); render() { trace(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e89fddd25..759c064b4 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -264,7 +264,8 @@ export class DocumentView extends DocComponent(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); } - 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)) { document.removeEventListener("pointermove", this.onPointerMove); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 47b64e260..923dd1544 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -153,7 +153,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } }); return { frag: Fragment.fromArray(nodes), start: start }; - } + }; let findLinkNode = (node: Node, editor: EditorView) => { if (!node.isText) { const content = findLinkFrag(node.content, editor); @@ -162,7 +162,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe 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; @@ -748,7 +748,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe 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 as any).getBoundingClientRect(); + 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; diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index b7ff84d4a..2147292d6 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -16,6 +16,9 @@ user-select: none; } } + .collectionFreeFormView-none { + pointer-events: none; + } } .pdfBox-cont-interactive { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e00635408..69e438d4f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked } from 'mobx'; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; @@ -25,7 +25,7 @@ const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer export class PDFBox extends DocComponent(PdfDocument) { - public static LayoutString() { return FieldView.LayoutString(PDFBox); } + public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(PDFBox, "data", fieldExt); } private _reactionDisposer?: IReactionDisposer; private _keyValue: string = ""; private _valueValue: string = ""; @@ -73,24 +73,24 @@ export class PDFBox extends DocComponent(PdfDocumen } public GetPage() { - return this._pdfViewer!._pdfViewer.currentPageNumber; + return this._pdfViewer!.pdfViewer.currentPageNumber; } @action public BackPage() { - this._pdfViewer!._pdfViewer.scrollPageIntoView({ pageNumber: Math.max(1, this.GetPage() - 1) }); + this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.max(1, this.GetPage() - 1) }); this.props.Document.curPage = this.GetPage(); } @action public GotoPage = (p: number) => { - this._pdfViewer!._pdfViewer.scrollPageIntoView(p); + this._pdfViewer!.pdfViewer.scrollPageIntoView(p); this.props.Document.curPage = this.GetPage(); } @action public ForwardPage() { - this._pdfViewer!._pdfViewer.scrollPageIntoView({ pageNumber: Math.min(this._pdfViewer!._pdfViewer.pagesCount, this.GetPage() + 1) }); + this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(this._pdfViewer!.pdfViewer.pagesCount, this.GetPage() + 1) }); this.props.Document.curPage = this.GetPage(); } @@ -153,7 +153,7 @@ export class PDFBox extends DocComponent(PdfDocumen + + return !this.props.active() ? (null) : - (<> + (
e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true} style={{ display: this.active() ? "flex" : "none" }}>
e.stopPropagation()} style={{ bottom: 0, left: `${this._searching ? 0 : 100}%` }}> + +
- - - - + this.GotoPage(Number(e.currentTarget.textContent))} + style={{ left: 20, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }} + onClick={action(() => this._pageControls = !this._pageControls)}> + {`${NumCast(this.props.Document.curPage)}`} + + {this._pageControls ? pageBtns : (null)}
e.stopPropagation()}>
- ); +
); } loaded = (nw: number, nh: number, np: number) => { @@ -211,7 +221,7 @@ export class PDFBox extends DocComponent(PdfDocumen render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); - let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.props.isSelected() ? "" : "-interactive"); + let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); return (!(pdfUrl instanceof PdfField) || !this._pdf ?
{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}
:
e.stopPropagation()} onPointerDown={(e: React.PointerEvent) => { @@ -222,12 +232,12 @@ export class PDFBox extends DocComponent(PdfDocumen }}> {this.settingsPanel()}
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 783495e5a..848f1ddcd 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -34,6 +34,9 @@ interface IViewerProps { fieldExtensionDoc: Doc; fieldKey: string; fieldExt: string; + PanelWidth: () => number; + PanelHeight: () => number; + ContentScaling: () => number; renderDepth: number; isSelected: () => boolean; loaded: (nw: number, nh: number, np: number) => void; @@ -46,6 +49,7 @@ interface IViewerProps { setPdfViewer: (view: PDFViewer) => void; ScreenToLocalTransform: () => Transform; ContainingCollectionView: Opt; + whenActiveChanged: (isActive: boolean) => void; } /** @@ -62,6 +66,7 @@ export class PDFViewer extends React.Component { @observable private _marqueeY: number = 0; @observable private _marqueeWidth: number = 0; @observable private _marqueeHeight: number = 0; + @observable private _marqueeing: boolean = false; public pdfViewer: any; private _isChildActive = false; @@ -74,7 +79,6 @@ export class PDFViewer extends React.Component { private _mainCont: React.RefObject = React.createRef(); private _selectionText: string = ""; private _marquee: React.RefObject = React.createRef(); - private _marqueeing: boolean = false; private _startX: number = 0; private _startY: number = 0; private _downX: number = 0; @@ -165,7 +169,7 @@ export class PDFViewer extends React.Component { @action setupPdfJsViewer = () => { document.addEventListener("pagesinit", () => this.pdfViewer.currentScaleValue = 1); - document.addEventListener("pagerendered", () => console.log("rendered")); + // document.addEventListener("pagerendered", () => console.log("rendered")); // bcz: works, but not needed except to debug var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, @@ -249,19 +253,23 @@ export class PDFViewer extends React.Component { @action prevAnnotation = () => { this.Index = Math.max(this.Index - 1, 0); - let scrollToAnnotation = this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]; - this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); - Doc.BrushDoc(scrollToAnnotation); - this.props.scrollTo(NumCast(scrollToAnnotation.y)); + this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]); } @action nextAnnotation = () => { this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1); - let scrollToAnnotation = this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]; + this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]); + } + + @action + scrollToAnnotation = (scrollToAnnotation: Doc) => { this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); + let windowHgt = this.props.PanelHeight() / this.props.ContentScaling(); + let scrollRange = this._mainCont.current!.scrollHeight - windowHgt; + let pgScroll = scrollRange / this._pageSizes.length; + this._mainCont.current!.scrollTo(0, NumCast(scrollToAnnotation.y) - pgScroll / 2); Doc.BrushDoc(scrollToAnnotation); - this.props.scrollTo(NumCast(scrollToAnnotation.y)); } sendAnnotations = (page: number) => { @@ -278,6 +286,11 @@ export class PDFViewer extends React.Component { } } + @action + onScroll = (e: React.UIEvent) => { + this.props.Document.curPage = this.pdfViewer.currentPageNumber; + } + // get the page index that the vertical offset passed in is on getPageFromScroll = (vOffset: number) => { let index = 0; @@ -288,10 +301,6 @@ export class PDFViewer extends React.Component { return index; } - getScrollFromPage = (index: number): number => { - return numberRange(Math.min(this.props.pdf.numPages, index)).reduce((counter, i) => counter + this._pageSizes[i].height, 0); - } - @action createAnnotation = (div: HTMLDivElement, page: number) => { if (this._annotationLayer.current) { @@ -311,11 +320,14 @@ export class PDFViewer extends React.Component { } @action - search = (searchString: string) => { - if (this.pdfViewer._pageViewsReady) { + search = (searchString: string, fwd: boolean) => { + if (!searchString) { + fwd ? this.nextAnnotation() : this.prevAnnotation(); + } + else if (this.pdfViewer._pageViewsReady) { this.pdfViewer.findController.executeCommand('findagain', { caseSensitive: false, - findPrevious: undefined, + findPrevious: !fwd, highlightAll: true, phraseSearch: true, query: searchString @@ -325,7 +337,7 @@ export class PDFViewer extends React.Component { let executeFind = () => { this.pdfViewer.findController.executeCommand('find', { caseSensitive: false, - findPrevious: undefined, + findPrevious: !fwd, highlightAll: true, phraseSearch: true, query: searchString @@ -383,6 +395,7 @@ export class PDFViewer extends React.Component { this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth); this._marqueeY = Math.min(this._startY, this._startY + this._marqueeHeight); this._marqueeWidth = Math.abs(this._marqueeWidth); + this._marqueeHeight = Math.abs(this._marqueeHeight); e.stopPropagation(); e.preventDefault(); } @@ -399,7 +412,6 @@ export class PDFViewer extends React.Component { for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); if (rect/* && rect.width !== this._mainCont.current.getBoundingClientRect().width && rect.height !== this._mainCont.current.getBoundingClientRect().height / this.props.pdf.numPages*/) { - let page = this.getPageFromScroll(rect.top); let scaleY = this._mainCont.current.offsetHeight / boundingRect.height; let scaleX = this._mainCont.current.offsetWidth / boundingRect.width; if (rect.width !== this._mainCont.current.clientWidth) { @@ -552,26 +564,29 @@ export class PDFViewer extends React.Component { this._setPreviewCursor = func; } onClick = (e: React.MouseEvent) => { - this._setPreviewCursor && this._marqueeing && Math.abs(e.clientX - this._downX) < 3 && Math.abs(e.clientY - this._downY) < 3 && + this._setPreviewCursor && + this._marqueeing && + Math.abs(e.clientX - this._downX) < 3 && + Math.abs(e.clientY - this._downY) < 3 && this._setPreviewCursor(e.clientX, e.clientY); } whenActiveChanged = (isActive: boolean) => { this._isChildActive = isActive; - //this.props.whenActiveChanged(isActive); // bcz: is this needed here? + this.props.whenActiveChanged(isActive); // bcz: is this needed here? } active = () => { return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; } render() { - return (
e.stopPropagation()} onClick={this.onClick} ref={this._mainCont}> + return (
e.stopPropagation()} onClick={this.onClick} ref={this._mainCont}>
-
-
+
}
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )} -- cgit v1.2.3-70-g09d2 From 0c0b5697957af3c1aa4560a707d37b1073b743a5 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 26 Sep 2019 12:02:35 -0400 Subject: more pdf cleanup/fixes --- src/client/views/nodes/DocumentView.tsx | 1 + src/client/views/nodes/FieldView.tsx | 1 - src/client/views/nodes/PDFBox.scss | 41 +++++------ src/client/views/nodes/PDFBox.tsx | 124 ++++++++++---------------------- src/client/views/pdf/PDFViewer.tsx | 6 +- 5 files changed, 59 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 759c064b4..d1d150027 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -261,6 +261,7 @@ export class DocumentView extends DocComponent(Docu document.addEventListener("pointerup", this.onPointerUp); } onPointerMove = (e: PointerEvent): void => { + console.log("Move " + e.clientX + " " + this.props.Document.title); if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); } 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/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 2147292d6..d82bcf02f 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -10,15 +10,16 @@ .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 { @@ -32,21 +33,6 @@ } } -.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; @@ -124,7 +110,7 @@ overflow: hidden; transition: left .5s; - .pdfBox-overlaySearchBar { + .pdfBox-searchBar { width: 70%; font-size: 14px; } @@ -149,7 +135,9 @@ transition: all 0.5s; } - .pdfBox-overlayButton-iconCont { + .pdfBox-overlayButton-iconCont, + .pdfBox-nextIcon, + .pdfBox-prevIcon { background: #121721; height: 30px; width: 70px; @@ -165,4 +153,9 @@ 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 30f4ce392..6aa8aded9 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -6,7 +6,7 @@ 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,7 +19,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); -import { CollectionSchemaBooleanCell } from '../collections/CollectionSchemaCells'; +import { undoBatch } from '../../util/UndoManager'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -27,73 +27,48 @@ const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer export class PDFBox extends DocComponent(PdfDocument) { public static LayoutString(fieldExt?: string) { return FieldView.LayoutString(PDFBox, "data", fieldExt); } - private _reactionDisposer?: IReactionDisposer; private _keyValue: string = ""; private _valueValue: string = ""; private _scriptValue: string = ""; private _searchString: string = ""; - @observable private _searching: boolean = false; + private _isChildActive = false; private _pdfViewer: PDFViewer | undefined; private _keyRef: React.RefObject = React.createRef(); private _valueRef: React.RefObject = React.createRef(); private _scriptRef: React.RefObject = React.createRef(); + @observable private _searching: boolean = false; @observable private _flyout: boolean = false; - @observable private _alt = false; @observable private _pdf: Opt; + @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); - - 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)); } } - - componentWillUnmount() { - this._reactionDisposer && this._reactionDisposer(); - } - - 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(); - } - - setPdfViewer = (pdfViewer: PDFViewer) => { - this._pdfViewer = pdfViewer; - } - - @action - public BackPage() { - this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.max(1, NumCast(this.props.Document.curPage) - 1) }); - } - - @action - public GotoPage = (p: number) => { - this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: p }); - } - - @action - public ForwardPage() { - this._pdfViewer!.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(this._pdfViewer!.pdfViewer.pagesCount, NumCast(this.props.Document.curPage) + 1) }); + 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); + } } - @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 : @@ -101,10 +76,6 @@ export class PDFBox extends DocComponent(PdfDocumen this.props.Document.filterScript = ScriptField.MakeFunction(scriptText); } - scrollTo = (y: number) => { - - } - private resetFilters = () => { this._keyValue = this._valueValue = this._scriptValue = ""; this._keyRef.current && (this._keyRef.current.value = ""); @@ -116,51 +87,38 @@ export class PDFBox extends DocComponent(PdfDocumen private newValueChange = (e: React.ChangeEvent) => this._valueValue = e.currentTarget.value; private newScriptChange = (e: React.ChangeEvent) => this._scriptValue = e.currentTarget.value; - _isChildActive = false; - whenActiveChanged = (isActive: boolean) => { - this._isChildActive = isActive; - this.props.whenActiveChanged(isActive); - } - active = () => { - return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; - } + 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) => this._searchString = e.currentTarget.value; - @observable _pageControls = false; + settingsPanel() { - trace(); let pageBtns = <> return !this.props.active() ? (null) : - (
e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true} style={{ display: this.active() ? "flex" : "none" }}> -
e.stopPropagation()} - style={{ bottom: 0, left: `${this._searching ? 0 : 100}%` }}> + (
e.keyCode === KeyCodes.BACKSPACE || e.keyCode === KeyCodes.DELETE ? e.stopPropagation() : true} + onPointerDown={e => e.stopPropagation()} style={{ display: this.active() ? "flex" : "none" }}> +
e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}> - -
@@ -170,7 +128,7 @@ export class PDFBox extends DocComponent(PdfDocumen
e.stopPropagation()}>
- this.GotoPage(Number(e.currentTarget.textContent))} + this.gotoPage(Number(e.currentTarget.textContent))} style={{ left: 20, top: 5, height: "30px", width: "30px", position: "absolute", pointerEvents: "all" }} onClick={action(() => this._pageControls = !this._pageControls)}> {`${NumCast(this.props.Document.curPage)}`} @@ -209,16 +167,6 @@ export class PDFBox extends DocComponent(PdfDocumen
); } - 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); - } - } - render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); @@ -226,15 +174,15 @@ export class PDFBox extends DocComponent(PdfDocumen
{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}
:
e.stopPropagation()} onPointerDown={(e: React.PointerEvent) => { let hit = document.elementFromPoint(e.clientX, e.clientY); - if (hit && hit.localName === "span") { + if (hit && hit.localName === "span" && this.props.isSelected()) { e.button === 0 && e.stopPropagation(); } }}> - boolean; loaded: (nw: number, nh: number, np: number) => void; - scrollTo: (y: number) => void; active: () => boolean; GoToPage?: (n: number) => void; addDocTab: (document: Doc, dataDoc: Doc | undefined, where: string) => boolean; @@ -262,6 +261,11 @@ export class PDFViewer extends React.Component { this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]); } + @action + gotoPage = (p: number) => { + this.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); + } + @action scrollToAnnotation = (scrollToAnnotation: Doc) => { this.allAnnotations.forEach(d => Doc.UnBrushDoc(d)); -- cgit v1.2.3-70-g09d2 From 288a3a1368ab7de11007080e54cd1879196342a4 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 26 Sep 2019 14:37:49 -0400 Subject: hopefully last fixes for pdf interactions -- this time for marquee dragging with right button. --- src/client/views/collections/CollectionSubView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 36 +++++++++++++--------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 10 +++--- src/client/views/pdf/PDFViewer.tsx | 15 +++++---- 5 files changed, 36 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index ce80526b2..9ffb7fa6d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -30,7 +30,7 @@ export interface CollectionViewProps extends FieldViewProps { PanelWidth: () => number; PanelHeight: () => number; chromeCollapsed: boolean; - setPreviewCursor?: (func: (x: number, y: number) => void) => void; + setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } export interface SubCollectionViewProps extends CollectionViewProps { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 689a55ec4..44611869e 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -31,7 +31,7 @@ interface MarqueeViewProps { addLiveTextDocument: (doc: Doc) => void; isSelected: () => boolean; isAnnotationOverlay: boolean; - setPreviewCursor?: (func: (x: number, y: number) => void) => void; + setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } @observer @@ -151,15 +151,10 @@ export class MarqueeView extends React.Component } @action onPointerDown = (e: React.PointerEvent): void => { - this._downX = this._lastX = e.pageX; - this._downY = this._lastY = e.pageY; - this._commandExecuted = false; - PreviewCursor.Visible = false; - this.cleanupInteractions(true); + this._downX = this._lastX = e.clientX; + this._downY = this._lastY = e.clientY; if (e.button === 2 || (e.button === 0 && e.altKey)) { - document.addEventListener("pointermove", this.onPointerMove, true); - document.addEventListener("pointerup", this.onPointerUp, true); - document.addEventListener("keydown", this.marqueeCommand, true); + this.setPreviewCursor(e.clientX, e.clientY, true); if (e.altKey) { //e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors. e.preventDefault(); @@ -182,6 +177,8 @@ export class MarqueeView extends React.Component e.stopPropagation(); e.preventDefault(); } + } else { + this.cleanupInteractions(true); // stop listening for events if another lower-level handle (e.g. another Marquee) has stopPropagated this } if (e.altKey) { e.preventDefault(); @@ -208,17 +205,28 @@ export class MarqueeView extends React.Component } } - setPreviewCursor = (x: number, y: number) => { - this._downX = x; - this._downY = y; - PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); + setPreviewCursor = (x: number, y: number, drag: boolean) => { + if (drag) { + this._downX = this._lastX = x; + this._downY = this._lastY = y; + this._commandExecuted = false; + PreviewCursor.Visible = false; + this.cleanupInteractions(true); + document.addEventListener("pointermove", this.onPointerMove, true); + document.addEventListener("pointerup", this.onPointerUp, true); + document.addEventListener("keydown", this.marqueeCommand, true); + } else { + this._downX = x; + this._downY = y; + PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument); + } } @action onClick = (e: React.MouseEvent): void => { if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - this.setPreviewCursor(e.clientX, e.clientY); + this.setPreviewCursor(e.clientX, e.clientY, false); // let the DocumentView stopPropagation of this event when it selects this document } else { // why do we get a click event when the cursor have moved a big distance? // let's cut it off here so no one else has to deal with it. diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index cd183a984..49b6f22db 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -103,7 +103,6 @@ export class CollectionFreeFormDocumentView extends DocComponent this.dataProvider ? this.dataProvider.height : this.panelHeight(); render() { - trace(); return (
(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); document.addEventListener("pointerup", this.onPointerUp); } onPointerMove = (e: PointerEvent): void => { - console.log("Move " + e.clientX + " " + this.props.Document.title); 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 && (SelectionManager.IsSelected(this) || - this.props.parentActive())) { + 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); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 427da1d9b..9ef311c86 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -69,7 +69,7 @@ export class PDFViewer extends React.Component { public pdfViewer: any; private _isChildActive = false; - private _setPreviewCursor: undefined | ((x: number, y: number) => void); + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _annotationLayer: React.RefObject = React.createRef(); private _reactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; @@ -355,7 +355,12 @@ export class PDFViewer extends React.Component { @action onPointerDown = (e: React.PointerEvent): void => { // if alt+left click, drag and annotate + this._downX = e.clientX; + this._downY = e.clientY; if (NumCast(this.props.Document.scale, 1) !== 1) return; + if (e.button !== 0 && this.active()) { + this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true); + } this._marqueeing = false; if (!e.altKey && e.button === 0 && this.active()) { PDFMenu.Instance.StartDrag = this.startDrag; @@ -370,8 +375,6 @@ export class PDFViewer extends React.Component { } } else { - this._downX = e.clientX; - this._downY = e.clientY; // set marquee x and y positions to the spatially transformed position if (this._mainCont.current) { let boundingRect = this._mainCont.current.getBoundingClientRect(); @@ -564,15 +567,15 @@ export class PDFViewer extends React.Component { scrollXf = () => { return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._mainCont.current.scrollTop) : this.props.ScreenToLocalTransform(); } - setPreviewCursor = (func?: (x: number, y: number) => void) => { + setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => { this._setPreviewCursor = func; } onClick = (e: React.MouseEvent) => { this._setPreviewCursor && - this._marqueeing && + e.button === 0 && Math.abs(e.clientX - this._downX) < 3 && Math.abs(e.clientY - this._downY) < 3 && - this._setPreviewCursor(e.clientX, e.clientY); + this._setPreviewCursor(e.clientX, e.clientY, false); } whenActiveChanged = (isActive: boolean) => { this._isChildActive = isActive; -- cgit v1.2.3-70-g09d2 From 29dec8b7e547d75983d4533862726692fabbabd1 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 26 Sep 2019 14:49:50 -0400 Subject: fixed warnings with PDF page numbering. --- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 10 ++++------ src/client/views/pdf/PDFViewer.tsx | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 9ffb7fa6d..954a27cbd 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -6,7 +6,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; -import { BoolCast, Cast } from "../../../new_fields/Types"; +import { Cast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { RouteStore } from "../../../server/RouteStore"; import { Utils } from "../../../Utils"; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 6aa8aded9..9e8478ffa 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -122,17 +122,15 @@ export class PDFBox extends DocComponent(PdfDocumen
- - this.gotoPage(Number(e.currentTarget.textContent))} + 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)}> - {`${NumCast(this.props.Document.curPage)}`} - + onClick={action(() => this._pageControls = !this._pageControls)} /> {this._pageControls ? pageBtns : (null)}
e.stopPropagation()}> - + ; return !this.props.active() ? (null) : (
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" }}> diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 058e94f77..d651d0025 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -1,4 +1,4 @@ -import { action, computed, IReactionDisposer, observable, reaction, trace } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, trace, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; @@ -9,7 +9,7 @@ import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnOne } from "../../../Utils"; +import { emptyFunction, returnOne, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; @@ -20,9 +20,11 @@ import Annotation from "./Annotation"; import PDFMenu from "./PDFMenu"; import "./PDFViewer.scss"; import React = require("react"); +import * as rp from "request-promise"; import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; +import { JSXElement } from "babel-types"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -96,6 +98,7 @@ export class PDFViewer extends React.Component { } componentDidMount = async () => { + let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${2}.PNG`))); this.props.setPdfViewer(this); await this.initialLoad(); @@ -124,6 +127,8 @@ export class PDFViewer extends React.Component { document.removeEventListener("copy", this.copy); document.addEventListener("copy", this.copy); this.setupPdfJsViewer(); + setTimeout(() => this.getCoverImage(res)); + } componentWillUnmount = () => { @@ -587,8 +592,20 @@ export class PDFViewer extends React.Component { active = () => { return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; } + + @observable _coverPage: JSX.Element | null = (null); + // change the address to be the file address of the PNG version of each page + // file address of the pdf + @action + getCoverImage = (res: any, page: number = 2) => { + this._coverPage =
; + // ; + } + render() { trace(); + return (
e.stopPropagation()} onClick={this.onClick} ref={this._mainCont}>
{!this._marqueeing ? (null) :
{ ContainingCollectionDoc={this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document} chromeCollapsed={true}> + {this._coverPage ? this._coverPage : (null)} {this._showWaiting ? Date: Fri, 27 Sep 2019 11:37:39 -0400 Subject: pdf loading now works the way it should. --- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 128 +++++++++++++++++--------------- 2 files changed, 71 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0d11afbe1..366c3142a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -601,7 +601,7 @@ export class DocumentView extends DocComponent(Docu ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document); const nativeWidth = this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; - const nativeHeight = this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + const nativeHeight = this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : nativeWidth !== "100%" ? nativeWidth : "100%"; const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle"); const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption"); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index d651d0025..c734f0ede 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react"; import * as Pdfjs from "pdfjs-dist"; import "pdfjs-dist/web/pdf_viewer.css"; import { Dictionary } from "typescript-collections"; -import { Doc, DocListCast, FieldResult, WidthSym, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, FieldResult, WidthSym, Opt, HeightSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; @@ -24,7 +24,7 @@ import * as rp from "request-promise"; import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; -import { JSXElement } from "babel-types"; +import { SelectionManager } from "../../util/SelectionManager"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -71,22 +71,25 @@ export class PDFViewer extends React.Component { @observable private _marqueeHeight: number = 0; @observable private _marqueeing: boolean = false; @observable private _showWaiting = true; + @observable private _showCover = false; public pdfViewer: any; private _isChildActive = false; private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); private _annotationLayer: React.RefObject = React.createRef(); private _reactionDisposer?: IReactionDisposer; + private _selectionReactionDisposer?: IReactionDisposer; private _annotationReactionDisposer?: IReactionDisposer; private _filterReactionDisposer?: IReactionDisposer; private _viewer: React.RefObject = React.createRef(); private _mainCont: React.RefObject = React.createRef(); - private _selectionText: string = ""; private _marquee: React.RefObject = React.createRef(); + private _selectionText: string = ""; private _startX: number = 0; private _startY: number = 0; private _downX: number = 0; private _downY: number = 0; + private _coverPath: any; @computed get allAnnotations() { return DocListCast(this.props.fieldExtensionDoc.annotations).filter( @@ -98,43 +101,22 @@ export class PDFViewer extends React.Component { } componentDidMount = async () => { - let res = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${2}.PNG`))); - this.props.setPdfViewer(this); - await this.initialLoad(); - - this._annotationReactionDisposer = reaction( - () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations), - annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), - { fireImmediately: true }); - - this._filterReactionDisposer = reaction( - () => ({ scriptField: Cast(this.props.Document.filterScript, ScriptField), annos: this._annotations.slice() }), - action(({ scriptField, annos }: { scriptField: FieldResult, annos: Doc[] }) => { - let oldScript = this._script.originalScript; - this._script = scriptField && scriptField.script.compiled ? scriptField.script : CompileScript("return true") as CompiledScript; - if (this._script.originalScript !== oldScript) { - this.Index = -1; - } - annos.forEach(d => d.opacity = this._script.run({ this: d }, console.log, 1).result ? 1 : 0); - }), - { fireImmediately: true } - ); - this._reactionDisposer = reaction( - () => this.props.Document.panY, - () => this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.props.Document.panY) || 0, behavior: "auto" }) - ); - - document.removeEventListener("copy", this.copy); - document.addEventListener("copy", this.copy); - this.setupPdfJsViewer(); - setTimeout(() => this.getCoverImage(res)); - + // change the address to be the file address of the PNG version of each page + // file address of the pdf + this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); + runInAction(() => this._showWaiting = this._showCover = true); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => { + this.setupPdfJsViewer(); + this._selectionReactionDisposer && this._selectionReactionDisposer(); + this._selectionReactionDisposer = undefined; + }) } componentWillUnmount = () => { this._reactionDisposer && this._reactionDisposer(); this._annotationReactionDisposer && this._annotationReactionDisposer(); this._filterReactionDisposer && this._filterReactionDisposer(); + this._selectionReactionDisposer && this._selectionReactionDisposer(); document.removeEventListener("copy", this.copy); } @@ -174,9 +156,40 @@ export class PDFViewer extends React.Component { } @action - setupPdfJsViewer = () => { - document.addEventListener("pagesinit", () => this.pdfViewer.currentScaleValue = 1); - document.addEventListener("pagerendered", action(() => this._showWaiting = false)); + setupPdfJsViewer = async () => { + this._showWaiting = true; + this.props.setPdfViewer(this); + await this.initialLoad(); + + this._annotationReactionDisposer = reaction( + () => this.props.fieldExtensionDoc && DocListCast(this.props.fieldExtensionDoc.annotations), + annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), + { fireImmediately: true }); + + this._filterReactionDisposer = reaction( + () => ({ scriptField: Cast(this.props.Document.filterScript, ScriptField), annos: this._annotations.slice() }), + action(({ scriptField, annos }: { scriptField: FieldResult, annos: Doc[] }) => { + let oldScript = this._script.originalScript; + this._script = scriptField && scriptField.script.compiled ? scriptField.script : CompileScript("return true") as CompiledScript; + if (this._script.originalScript !== oldScript) { + this.Index = -1; + } + annos.forEach(d => d.opacity = this._script.run({ this: d }, console.log, 1).result ? 1 : 0); + }), + { fireImmediately: true } + ); + this._reactionDisposer = reaction( + () => this.props.Document.panY, + () => this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.props.Document.panY) || 0, behavior: "auto" }) + ); + + document.removeEventListener("copy", this.copy); + document.addEventListener("copy", this.copy); + document.addEventListener("pagesinit", () => { + this.pdfViewer.currentScaleValue = 1; + this.gotoPage(NumCast(this.props.Document.curPage, 1)); + }); + document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, @@ -271,7 +284,7 @@ export class PDFViewer extends React.Component { @action gotoPage = (p: number) => { - this.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); + this.pdfViewer && this.pdfViewer.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) }); } @action @@ -300,7 +313,7 @@ export class PDFViewer extends React.Component { @action onScroll = (e: React.UIEvent) => { - this.props.Document.curPage = this.pdfViewer.currentPageNumber; + this.pdfViewer && (this.props.Document.curPage = this.pdfViewer.currentPageNumber); } // get the page index that the vertical offset passed in is on @@ -587,20 +600,21 @@ export class PDFViewer extends React.Component { } whenActiveChanged = (isActive: boolean) => { this._isChildActive = isActive; - this.props.whenActiveChanged(isActive); // bcz: is this needed here? + this.props.whenActiveChanged(isActive); } active = () => { return this.props.isSelected() || this._isChildActive || this.props.renderDepth === 0; } - @observable _coverPage: JSX.Element | null = (null); - // change the address to be the file address of the PNG version of each page - // file address of the pdf - @action - getCoverImage = (res: any, page: number = 2) => { - this._coverPage =
; - // ; + getCoverImage = () => { + let nativeWidth = NumCast(this.props.Document.nativeWidth); + if (!this.props.Document[HeightSym]()) { + this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; + this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width + } + let nativeHeight = NumCast(this.props.Document.nativeHeight); + return this._showWaiting = false)} + style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />; } render() { @@ -621,8 +635,8 @@ export class PDFViewer extends React.Component {
this._pageSizes.length && this._pageSizes[0] ? this.props.pdf.numPages * this._pageSizes[0].height : 300} - PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : 300} + PanelHeight={() => this._pageSizes.length && this._pageSizes[0] ? this.props.pdf.numPages * this._pageSizes[0].height : NumCast(this.props.Document.nativeHeight)} + PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} focus={emptyFunction} isSelected={this.props.isSelected} select={emptyFunction} @@ -639,19 +653,17 @@ export class PDFViewer extends React.Component { ContainingCollectionDoc={this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document} chromeCollapsed={true}> - {this._coverPage ? this._coverPage : (null)} - {this._showWaiting ? : (null)} + }} /> : (null)}
); } } -- cgit v1.2.3-70-g09d2 From ffe8282942e4c29f9b28b6423ca934fd17986270 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 27 Sep 2019 12:12:06 -0400 Subject: fixed freeformview bug. --- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 3 ++- src/client/views/pdf/PDFMenu.tsx | 7 +++---- src/client/views/pdf/PDFViewer.tsx | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 37851d924..0822e62da 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -698,7 +698,8 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document Doc.UpdateDocumentExtensionForField(this.props.DataDoc || this.props.Document, this.props.fieldKey); return ( -
,
- - + +
, , diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index c734f0ede..c28469fcc 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -152,6 +152,7 @@ export class PDFViewer extends React.Component { i === this.props.pdf.numPages - 1 && this.props.loaded((page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]), (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]), i); })))); + Doc.GetProto(this.props.Document).scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0); } } @@ -635,7 +636,7 @@ export class PDFViewer extends React.Component {
this._pageSizes.length && this._pageSizes[0] ? this.props.pdf.numPages * this._pageSizes[0].height : NumCast(this.props.Document.nativeHeight)} + PanelHeight={() => NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} focus={emptyFunction} isSelected={this.props.isSelected} -- cgit v1.2.3-70-g09d2 From d9f5a6f88d75fe4037a7dacf62188649a5dd7a55 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 27 Sep 2019 12:31:45 -0400 Subject: removed unneeded setTimeout in dragging --- src/client/util/DragManager.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 00fc752ca..43c352a90 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -416,10 +416,9 @@ export namespace DragManager { hideSource = options.hideSource(); } } - setTimeout(() => - eles.map(ele => (ele.hidden = hideSource) && - (ele.parentElement && ele.parentElement.className.indexOf("collectionFreeFormDocumentView") !== -1 && (ele.parentElement.hidden = hideSource))), - 0); + + // we're dragging a documentView, but it may be a child of a CollectionFreeFormDocumentView. If it is, we want to hide that as well -- this should be generalized somehow in case other draggable things might contain a DocumentView. + eles.map(ele => (ele.hidden = hideSource)); let lastX = downX; let lastY = downY; -- cgit v1.2.3-70-g09d2 From f18bff6a3c848e1afcec98bf4fa05b8edfaa9c57 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 27 Sep 2019 13:43:04 -0400 Subject: added initial zooming for PDFS --- needs more work. --- src/client/util/DragManager.ts | 14 ++--- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFViewer.scss | 20 +++++-- src/client/views/pdf/PDFViewer.tsx | 89 +++++++++++++++++++------------- 5 files changed, 76 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 43c352a90..ddc8fb62c 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -417,8 +417,7 @@ export namespace DragManager { } } - // we're dragging a documentView, but it may be a child of a CollectionFreeFormDocumentView. If it is, we want to hide that as well -- this should be generalized somehow in case other draggable things might contain a DocumentView. - eles.map(ele => (ele.hidden = hideSource)); + eles.map(ele => ele.hidden = hideSource); let lastX = downX; let lastY = downY; @@ -446,12 +445,9 @@ export namespace DragManager { ); }; - let hideDragElements = () => { + let hideDragShowOriginalElements = () => { dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement)); - eles.map(ele => { - ele.hidden = false; - (ele.parentElement && ele.parentElement.className.indexOf("collectionFreeFormDocumentView") !== -1 && (ele.parentElement.hidden = false)); - }); + eles.map(ele => ele.hidden = false); }; let endDrag = () => { document.removeEventListener("pointermove", moveHandler, true); @@ -462,12 +458,12 @@ export namespace DragManager { }; AbortDrag = () => { - hideDragElements(); + hideDragShowOriginalElements(); SelectionManager.SetIsDragging(false); endDrag(); }; const upHandler = (e: PointerEvent) => { - hideDragElements(); + hideDragShowOriginalElements(); dispatchDrag(eles, e, dragData, options, finishDrag); SelectionManager.SetIsDragging(false); endDrag(); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7ec316bf9..cacaddcc8 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -617,7 +617,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
(PdfDocumen let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); return (!(pdfUrl instanceof PdfField) || !this._pdf ?
{`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}
: -
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(); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index 0b74a8ad4..c5a397691 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -1,9 +1,10 @@ -.pdfViewer-viewer { + +.pdfViewer-viewer, .pdfViewer-viewer-zoomed { pointer-events: inherit; width: 100%; height: 100%; position: absolute; - overflow-y: scroll; + overflow-y: auto; overflow-x: hidden; // .canvasWrapper { @@ -28,6 +29,15 @@ opacity: 0.1; } + .pdfViewer-overlay { + transform: scale(2.14359); + transform-origin: left top; + position: absolute; + top: 0px; + left: 0px; + display: inline-block; + width:100%; + } .pdfViewer-annotationLayer { position: absolute; top: 0; @@ -40,4 +50,8 @@ opacity: 0.1; } } -} \ No newline at end of file +} +.pdfViewer-viewer-zoomed { + overflow-x: scroll; +} + \ No newline at end of file diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index c28469fcc..016445538 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -72,6 +72,7 @@ export class PDFViewer extends React.Component { @observable private _marqueeing: boolean = false; @observable private _showWaiting = true; @observable private _showCover = false; + @observable private _zoomed = 1; public pdfViewer: any; private _isChildActive = false; @@ -186,10 +187,10 @@ export class PDFViewer extends React.Component { document.removeEventListener("copy", this.copy); document.addEventListener("copy", this.copy); - document.addEventListener("pagesinit", () => { - this.pdfViewer.currentScaleValue = 1; + document.addEventListener("pagesinit", action(() => { + this.pdfViewer.currentScaleValue = this._zoomed = 1; this.gotoPage(NumCast(this.props.Document.curPage, 1)); - }); + })); document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ @@ -618,10 +619,20 @@ export class PDFViewer extends React.Component { style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />; } + + @action + onZoomWheel = (e: React.WheelEvent) => { + e.stopPropagation(); + if (e.ctrlKey) { + let curScale = Number(this.pdfViewer.currentScaleValue); + this.pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale + curScale * e.deltaY / 1000)); + this._zoomed = Number(this.pdfViewer.currentScaleValue); + } + } render() { trace(); - return (
e.stopPropagation()} onClick={this.onClick} ref={this._mainCont}> + return (
{!this._marqueeing ? (null) :
{ border: `${this._marqueeWidth === 0 ? "" : "2px dashed black"}` }}>
} -
- {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => - )} +
+
+ {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => + )} +
+ NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} + PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} + focus={emptyFunction} + isSelected={this.props.isSelected} + select={emptyFunction} + active={this.active} + ContentScaling={returnOne} + whenActiveChanged={this.whenActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} + CollectionView={this.props.ContainingCollectionView} + ScreenToLocalTransform={this.scrollXf} + ruleProvider={undefined} + renderDepth={this.props.renderDepth + 1} + ContainingCollectionDoc={this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document} + chromeCollapsed={true}> +
- NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} - PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} - focus={emptyFunction} - isSelected={this.props.isSelected} - select={emptyFunction} - active={this.active} - ContentScaling={returnOne} - whenActiveChanged={this.whenActiveChanged} - removeDocument={this.removeDocument} - moveDocument={this.moveDocument} - addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} - CollectionView={this.props.ContainingCollectionView} - ScreenToLocalTransform={this.scrollXf} - ruleProvider={undefined} - renderDepth={this.props.renderDepth + 1} - ContainingCollectionDoc={this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document} - chromeCollapsed={true}> - {this._showCover ? this.getCoverImage() : (null)} - {this._showWaiting ? : (null)} + { + this._showWaiting ? : (null) + }
); } } -- cgit v1.2.3-70-g09d2 From 9d845e78593b6923c8dc3224bcd89715f39aa070 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 27 Sep 2019 14:59:48 -0400 Subject: fixed borderRounding. fixed pdf marqueeing for efficiency. --- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 +- src/client/views/pdf/PDFViewer.scss | 9 ++ src/client/views/pdf/PDFViewer.tsx | 99 ++++++++++++++-------- 3 files changed, 76 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 49b6f22db..c3d2c9e51 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -77,7 +77,8 @@ export class CollectionFreeFormDocumentView extends DocComponent { let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; - let br = StrCast(((this.layoutDoc.layout as Doc) || this.Document).borderRounding); + let ld = this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout as Doc : undefined; + let br = StrCast((ld || this.props.Document).borderRounding); br = !br && ruleRounding ? ruleRounding : br; if (br.endsWith("%")) { let nativeDim = Math.min(NumCast(this.layoutDoc.nativeWidth), NumCast(this.layoutDoc.nativeHeight)); diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index c5a397691..8027e93a3 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -50,6 +50,15 @@ opacity: 0.1; } } + .pdfViewer-waiting { + width: 70%; + height: 70%; + margin : 15%; + transition: 0.4s opacity ease; + opacity: 0.7; + position: absolute; + z-index: 10; + } } .pdfViewer-viewer-zoomed { overflow-x: scroll; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 016445538..5ad4ffd48 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -392,7 +392,6 @@ export class PDFViewer extends React.Component { PDFMenu.Instance.Status = "pdf"; PDFMenu.Instance.fadeOut(true); if (e.target && (e.target as any).parentElement.className === "textLayer") { - e.stopPropagation(); if (!e.ctrlKey) { this.receiveAnnotations([], -1); } @@ -405,7 +404,11 @@ export class PDFViewer extends React.Component { this._startY = this._marqueeY = (e.clientY - boundingRect.top) * (this._mainCont.current.offsetHeight / boundingRect.height) + this._mainCont.current.scrollTop; } this._marqueeing = true; - this._marquee.current && (this._marquee.current.style.opacity = "0.2"); + let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox"); + if (marquees && marquees.length) { // make a copy of the marquee + let marquee = marquees[0] as HTMLDivElement; + marquee.style.opacity = "0.2"; + } this.receiveAnnotations([], -1); } document.removeEventListener("pointermove", this.onSelectMove); @@ -472,9 +475,11 @@ export class PDFViewer extends React.Component { onSelectEnd = (e: PointerEvent): void => { if (this._marqueeing) { if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { - if (this._marquee.current) { // make a copy of the marquee + let marquees = this._mainCont.current!.getElementsByClassName("pdfViewer-dragAnnotationBox"); + if (marquees && marquees.length) { // make a copy of the marquee let copy = document.createElement("div"); - let style = this._marquee.current.style; + let marquee = marquees[0] as HTMLDivElement; + let style = marquee.style; copy.style.left = style.left; copy.style.top = style.top; copy.style.width = style.width; @@ -483,7 +488,7 @@ export class PDFViewer extends React.Component { copy.style.opacity = style.opacity; copy.className = "pdfPage-annotationBox"; this.createAnnotation(copy, this.getPageFromScroll(this._marqueeY)); - this._marquee.current.style.opacity = "0"; + marquee.style.opacity = "0"; } if (!e.ctrlKey) { @@ -609,11 +614,13 @@ export class PDFViewer extends React.Component { } getCoverImage = () => { - let nativeWidth = NumCast(this.props.Document.nativeWidth); if (!this.props.Document[HeightSym]()) { - this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; - this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width + setTimeout(() => { + this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; + this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width; + }, 0); } + let nativeWidth = NumCast(this.props.Document.nativeWidth); let nativeHeight = NumCast(this.props.Document.nativeHeight); return this._showWaiting = false)} style={{ position: "absolute", display: "inline-block", top: 0, left: 0, width: `${nativeWidth}px`, height: `${nativeHeight}px` }} />; @@ -629,23 +636,37 @@ export class PDFViewer extends React.Component { this._zoomed = Number(this.pdfViewer.currentScaleValue); } } + + @computed get annotationLayer() { + trace(); + return
+ {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => + )} +
; + } + @computed get pdfViewerDiv() { + trace(); + return
; + } + @computed get standinViews() { + trace(); + return <> + {this._showCover ? this.getCoverImage() : (null)} + {this._showWaiting ? : (null)} + ; + } + marqueeWidth = () => this._marqueeWidth; + marqueeHeight = () => this._marqueeHeight; + marqueeX = () => this._marqueeX; + marqueeY = () => this._marqueeY; + marqueeing = () => this._marqueeing; render() { trace(); - return (
-
- {!this._marqueeing ? (null) :
-
} + {this.pdfViewerDiv} +
-
- {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => - )} -
+ {this.annotationLayer} NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} @@ -667,21 +688,31 @@ export class PDFViewer extends React.Component { chromeCollapsed={true}>
- {this._showCover ? this.getCoverImage() : (null)} - { - this._showWaiting ? : (null) - } + {this.standinViews}
); } } +interface PdfViewerMarqueeProps { + isMarqueeing: () => boolean; + width: () => number; + height: () => number; + x: () => number; + y: () => number; +} + +@observer +class PdfViewerMarquee extends React.Component { + render() { + return !this.props.isMarqueeing() ? (null) :
+
+ } +} + + export enum AnnotationTypes { Region } -- cgit v1.2.3-70-g09d2 From b16581b509a8b74ebd3b4e9d0a598e305e4249b5 Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 27 Sep 2019 15:27:55 -0400 Subject: fixed lockedPosition to allow move events to pass through (ie, for panning). --- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0822e62da..721732774 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -355,7 +355,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let safeScale = Math.min(Math.max(0.15, localTransform.Scale), 40); this.props.Document.scale = Math.abs(safeScale); this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale); - e.preventDefault(); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 366c3142a..6ee88f834 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -254,7 +254,7 @@ export class DocumentView extends DocComponent(Docu this._hitTemplateDrag = true; } } - 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); + if (this.active && e.button === 0 && !this.Document.lockedPosition) 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); @@ -264,9 +264,9 @@ export class DocumentView extends DocComponent(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive())) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive()) && !this.Document.lockedPosition) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { - if (!e.altKey && !this.topMost && e.buttons === 1 && !this.Document.lockedPosition) { + if (!e.altKey && !this.topMost && e.buttons === 1) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); this.startDragging(this._downX, this._downY, e.ctrlKey || e.altKey ? "alias" : undefined, this._hitTemplateDrag); @@ -296,7 +296,7 @@ export class DocumentView extends DocComponent(Docu await swapViews(this.props.Document, "", "layoutNative"); let options = { title: "data", width: (this.Document.width || 0), x: -(this.Document.width || 0) / 2, y: - (this.Document.height || 0) / 2, }; - let fieldTemplate = this.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : + let fieldTemplate = this.Document.type === DocumentType.TEXT ? Docs.Create.TextDocument(options) : this.Document.type === DocumentType.PDF ? Docs.Create.PdfDocument("http://www.msn.com", options) : this.Document.type === DocumentType.VID ? Docs.Create.VideoDocument("http://www.cs.brown.edu", options) : Docs.Create.ImageDocument("http://www.cs.brown.edu", options); -- cgit v1.2.3-70-g09d2 From a80f0867032a4735b319c87c1c7c045f062a7d4f Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 27 Sep 2019 17:36:22 -0400 Subject: got rid of opacity 0.99 --- src/client/views/InkingCanvas.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index 2d1142f38..b3edad459 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -1,7 +1,7 @@ @import "globalCssVariables"; .inkingCanvas { - opacity: 0.99; + // opacity: 0.99; touch-action: none; .jsx-parser { -- cgit v1.2.3-70-g09d2 From 8c018a7a686a752ef1b86b852ec2d298792aa354 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Sat, 28 Sep 2019 17:00:47 -0400 Subject: toggle --- src/client/views/animationtimeline/Keyframe.tsx | 100 ++++------------- src/client/views/animationtimeline/Timeline.scss | 20 ++++ src/client/views/animationtimeline/Timeline.tsx | 121 ++++++--------------- .../views/animationtimeline/TimelineMenu.tsx | 11 +- .../views/animationtimeline/TimelineOverview.tsx | 2 + src/client/views/animationtimeline/Track.tsx | 40 +++---- .../views/nodes/CollectionFreeFormDocumentView.tsx | 3 +- 7 files changed, 102 insertions(+), 195 deletions(-) (limited to 'src') diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx index 9728c2462..66ad6a76d 100644 --- a/src/client/views/animationtimeline/Keyframe.tsx +++ b/src/client/views/animationtimeline/Keyframe.tsx @@ -8,7 +8,6 @@ import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { Cast, NumCast } from "../../../new_fields/Types"; import { List } from "../../../new_fields/List"; import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../new_fields/Schema"; -import { FlyoutProps } from "./Timeline"; import { Transform } from "../../util/Transform"; import { InkField, StrokeData } from "../../../new_fields/InkField"; import { TimelineMenu } from "./TimelineMenu"; @@ -138,48 +137,13 @@ export class Keyframe extends React.Component { @observable private _mouseToggled = false; @observable private _doubleClickEnabled = false; - @computed - private get regiondata() { - let index = this.regions.indexOf(this.props.RegionData); - return RegionData(this.regions[index] as Doc); - } - - @computed - private get regions() { - return Cast(this.props.node.regions, listSpec(Doc)) as List; - } - - @computed - private get firstKeyframe() { - let first: (Doc | undefined) = undefined; - DocListCast(this.regiondata.keyframes!).forEach(kf => { - if (kf.type !== KeyframeFunc.KeyframeType.fade) { - if (!first || first && NumCast(kf.time) < NumCast(first.time)) { - first = kf; - } - } - }); - return first; - } - - @computed - private get lastKeyframe() { - let last: (Doc | undefined) = undefined; - DocListCast(this.regiondata.keyframes!).forEach(kf => { - if (kf.type !== KeyframeFunc.KeyframeType.fade) { - if (!last || last && NumCast(kf.time) > NumCast(last.time)) { - last = kf; - } - } - }); - return last; - } - - @computed - private get keyframes(){ - return DocListCast(this.regiondata.keyframes); - } - + @computed private get regiondata() { return RegionData(this.regions[this.regions.indexOf(this.props.RegionData)] as Doc);} + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} + @computed private get keyframes(){ return DocListCast(this.regiondata.keyframes); } + @computed private get pixelPosition(){ return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement);} + @computed private get pixelDuration(){ return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } + @computed private get pixelFadeIn() { return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } + @computed private get pixelFadeOut(){ return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); } @computed private get inks() { if (this.props.collection.data_ext) { @@ -191,38 +155,18 @@ export class Keyframe extends React.Component { } } - @computed - private get pixelPosition(){ - return KeyframeFunc.convertPixelTime(this.regiondata.position, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - @computed - private get pixelDuration(){ - return KeyframeFunc.convertPixelTime(this.regiondata.duration, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - @computed - private get pixelFadeIn() { - return KeyframeFunc.convertPixelTime(this.regiondata.fadeIn, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - @computed - private get pixelFadeOut(){ - return KeyframeFunc.convertPixelTime(this.regiondata.fadeOut, "mili", "pixel", this.props.tickSpacing, this.props.tickIncrement); - } - - async componentWillMount() { - if (!this.regiondata.keyframes) { - this.regiondata.keyframes = new List(); - } - let fadeIn = await this.makeKeyData(this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade)!; - let fadeOut = await this.makeKeyData(this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade)!; - let start = await this.makeKeyData(this.regiondata.position, KeyframeFunc.KeyframeType.fade)!; - let finish = await this.makeKeyData(this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.fade)!; - (fadeIn.key! as Doc).opacity = 1; - (fadeOut.key! as Doc).opacity = 1; - (start.key! as Doc).opacity = 0.1; - (finish.key! as Doc).opacity = 0.1; + componentWillMount() { + runInAction(async () => { + if (!this.regiondata.keyframes) this.regiondata.keyframes = new List(); + let fadeIn = await this.makeKeyData(this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade)!; + let fadeOut = await this.makeKeyData(this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade)!; + let start = await this.makeKeyData(this.regiondata.position, KeyframeFunc.KeyframeType.fade)!; + let finish = await this.makeKeyData(this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.fade)!; + (fadeIn.key! as Doc).opacity = 1; + (fadeOut.key! as Doc).opacity = 1; + (start.key! as Doc).opacity = 0.1; + (finish.key! as Doc).opacity = 0.1; + }); } @action @@ -336,7 +280,7 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().left) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let leftRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.left, this.regiondata, this.regions); - let firstkf: (Doc | undefined) = this.firstKeyframe; + let firstkf: (Doc | undefined) = this.keyframes[0]; if (firstkf && this.regiondata.position + this.regiondata.fadeIn + offset >= NumCast(firstkf!.time)) { let dif = NumCast(firstkf!.time) - (this.pixelPosition + this.pixelFadeIn); this.regiondata.position = NumCast(firstkf!.time) - this.regiondata.fadeIn; @@ -364,8 +308,8 @@ export class Keyframe extends React.Component { let bar = this._bar.current!; let offset = KeyframeFunc.convertPixelTime(Math.round((e.clientX - bar.getBoundingClientRect().right) * this.props.transform.Scale), "mili", "time", this.props.tickSpacing, this.props.tickIncrement); let rightRegion = KeyframeFunc.findAdjacentRegion(KeyframeFunc.Direction.right, this.regiondata, this.regions); - if (this.lastKeyframe! && this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= NumCast((this.lastKeyframe! as Doc).time)) { - let dif = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut - NumCast((this.lastKeyframe! as Doc).time); + if (this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut + offset <= NumCast((this.keyframes[this.keyframes.length - 1]).time)) { + let dif = this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut - NumCast((this.keyframes[this.keyframes.length - 1]).time); this.regiondata.duration -= dif; } else if (this.regiondata.duration + offset < this.regiondata.fadeIn + this.regiondata.fadeOut) { // nokeyframes, just fades this.regiondata.duration = this.regiondata.fadeIn + this.regiondata.fadeOut; diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 1457d5a84..990bc681c 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -119,4 +119,24 @@ width: 100%; background-color: grey; } +} + +.round-toggle { + position: absolute; + height: 40px; + width: 80px; + background-color: white; + border: 2px solid purple; + border-radius: 20px; + input{ + opacity: 0; + height: 0; + width: 0; + } + .slider{ + height: 40px; + width: 40px; + background-color: black; + border-radius: 20px; + } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 875a0b8f3..8f96315a0 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -15,17 +15,6 @@ import { TimelineOverview } from "./TimelineOverview"; import { FieldViewProps } from "../nodes/FieldView"; import { KeyframeFunc } from "./Keyframe"; - - -export interface FlyoutProps { - x?: number; - y?: number; - display?: string; - regiondata?: Doc; - regions?: List; -} - - @observer export class Timeline extends React.Component { @@ -35,26 +24,21 @@ export class Timeline extends React.Component { private readonly MAX_CONTAINER_HEIGHT: number = 800; private readonly DEFAULT_TICK_INCREMENT: number = 1000; - @observable private _isMinimized = false; - @observable private _tickSpacing = this.DEFAULT_TICK_SPACING; - @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; - @observable private _scrubberbox = React.createRef(); - @observable private _scrubber = React.createRef(); @observable private _trackbox = React.createRef(); @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); - @observable private _timelineWrapper = React.createRef(); @observable private _infoContainer = React.createRef(); @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @observable private _isPlaying: boolean = false; //scrubber playing - @observable private _isFrozen: boolean = true; //timeline freeze @observable private _totalLength: number = 0; @observable private _visibleLength: number = 0; @observable private _visibleStart: number = 0; - @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; + @observable private _containerHeight: number = this.DEFAULT_CONTAINER_HEIGHT; + @observable private _tickSpacing = this.DEFAULT_TICK_SPACING; + @observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT; @observable private _time = 100000; //DEFAULT @observable private _ticks: number[] = []; @observable private _playButton = faPlayCircle; @@ -273,39 +257,7 @@ export class Timeline extends React.Component { } } - @action - onTimelineDown = (e: React.PointerEvent) => { - e.preventDefault(); - if (e.nativeEvent.which === 1 && !this._isFrozen) { - document.addEventListener("pointermove", this.onTimelineMove); - document.addEventListener("pointerup", () => { document.removeEventListener("pointermove", this.onTimelineMove); }); - } - } - @action - onTimelineMove = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let timelineContainer = this._timelineWrapper.current!; - let left = parseFloat(timelineContainer.style.left!); - let top = parseFloat(timelineContainer.style.top!); - timelineContainer.style.left = `${left + e.movementX}px`; - timelineContainer.style.top = `${top + e.movementY}px`; - } - - @action - minimize = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - let timelineContainer = this._timelineContainer.current!; - if (this._isMinimized) { - this._isMinimized = false; - timelineContainer.style.visibility = "visible"; - } else { - this._isMinimized = true; - timelineContainer.style.visibility = "hidden"; - } - } @action toReadTime = (time: number): string => { @@ -321,21 +273,6 @@ export class Timeline extends React.Component { timelineContextMenu = (e:MouseEvent): void => { let subitems: ContextMenuProps[] = []; - let timelineContainer = this._timelineWrapper.current!; - subitems.push({ - description: "Pin to Top", event: action(() => { - if (!this._isFrozen) { - timelineContainer.style.left = "0px"; - timelineContainer.style.top = "0px"; - timelineContainer.style.transition = "none"; - } - }), icon: faArrowUp - }); - subitems.push({ - description: this._isFrozen ? "Unfreeze Timeline" : "Freeze Timeline", event: action(() => { - this._isFrozen = !this._isFrozen; - }), icon: "thumbtack" - }); subitems.push({ description: this._timelineVisible ? "Hide Timeline" : "Show Timeline", event: action(() => { this._timelineVisible = !this._timelineVisible; @@ -358,7 +295,8 @@ export class Timeline extends React.Component { let currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement); let currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement); this._infoContainer.current!.scrollLeft = currPixel - offset; - this._visibleStart = currPixel - offset; + this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0; + this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) :0; this.changeCurrentBarX(currCurrent); } @@ -402,35 +340,42 @@ export class Timeline extends React.Component {
); } + render() { return ( -
-
- -
- {this.timelineToolBox(0.5)} -
-
- {this._ticks.map(element => { - if(element % this._tickIncrement === 0) return

{this.toReadTime(element)}

; - })} +
+
+
+
+ {this.timelineToolBox(0.5)} +
+
+ {this._ticks.map(element => { + if(element % this._tickIncrement === 0) return

{this.toReadTime(element)}

; + })} +
+
+
+
+
+ {DocListCast(this.children).map(doc => )} +
-
-
+
+ {DocListCast(this.children).map(doc =>
{Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

{doc.title}

)}
-
- {DocListCast(this.children).map(doc => )} +
+
-
- {DocListCast(this.children).map(doc =>
{Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

{doc.title}

)} -
-
- -
+ {BoolCast(this.props.Document.isAnimating) ?
: this.timelineToolBox(1) }
- {BoolCast(this.props.Document.isAnimating) ?
: this.timelineToolBox(1) } + +
); } diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx index f3b985297..59c25596e 100644 --- a/src/client/views/animationtimeline/TimelineMenu.tsx +++ b/src/client/views/animationtimeline/TimelineMenu.tsx @@ -4,6 +4,7 @@ import {observer} from "mobx-react"; import "./TimelineMenu.scss"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons"; +import { Utils } from "../../../Utils"; @observer @@ -40,7 +41,7 @@ export class TimelineMenu extends React.Component { if (type === "input"){ let inputRef = React.createRef(); let text = ""; - this._currentMenu.push(
{ + this._currentMenu.push(
{ e.stopPropagation(); text = e.target.value; }} onKeyDown={(e) => { @@ -52,23 +53,23 @@ export class TimelineMenu extends React.Component { }}/>
); } else if (type === "button") { let buttonRef = React.createRef(); - this._currentMenu.push(

{ + this._currentMenu.push(

{ e.preventDefault(); e.stopPropagation(); event(e); this.closeMenu(); }}>{title}

); - } + } } @action addMenu = (title:string) => { - this._currentMenu.unshift(

{title}

); + this._currentMenu.unshift(

{title}

); } render() { return ( -
+
{this._currentMenu}
); diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index 38b823cbc..a8f7faba8 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -78,6 +78,8 @@ export class TimelineOverview extends React.Component{ } render(){ + console.log("rendered"); + console.log(this.props.visibleStart); return(
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index 274b215d9..3ec410216 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -32,18 +32,24 @@ export class Track extends React.Component { @observable private _onKeyframe: (Doc | undefined) = undefined; @observable private _onRegionData: (Doc | undefined) = undefined; @observable private _storedState: (Doc | undefined) = undefined; - - @computed - private get regions() { - return Cast(this.props.node.regions, listSpec(Doc)) as List; - } + @observable private filterList = [ + "regions", + "cursors", + "hidden", + "nativeHeight", + "nativeWidth", + "schemaColumns", + "baseLayout", + "backgroundLayout", + "layout", + ]; + + @computed private get regions() { return Cast(this.props.node.regions, listSpec(Doc)) as List;} componentWillMount() { - if (!this.props.node.regions) { - this.props.node.regions = new List(); - } - - + runInAction(() => { + if (!this.props.node.regions) this.props.node.regions = new List(); + }); } componentDidMount() { @@ -54,11 +60,11 @@ export class Track extends React.Component { this.props.node.hidden = false; this.props.node.opacity = 1; }); - } componentWillUnmount() { runInAction(() => { + //disposing reactions if (this._currentBarXReaction) this._currentBarXReaction(); if (this._timelineVisibleReaction) this._timelineVisibleReaction(); }); @@ -166,17 +172,7 @@ export class Track extends React.Component { }); } - private filterList = [ - "regions", - "cursors", - "hidden", - "nativeHeight", - "nativeWidth", - "schemaColumns", - "baseLayout", - "backgroundLayout", - "layout", - ]; + @action private filterKeys = (keys: string[]): string[] => { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index af2651dc8..bbfc5eb51 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -115,8 +115,7 @@ export class CollectionFreeFormDocumentView extends DocComponent Date: Sat, 28 Sep 2019 23:43:25 -0400 Subject: lots of fixes to pdfs and link following. --- src/Utils.ts | 30 +++++++++ src/client/util/DocumentManager.ts | 4 +- src/client/views/DocumentDecorations.tsx | 8 +-- src/client/views/MainView.tsx | 5 +- .../views/collections/CollectionDockingView.scss | 5 +- .../views/collections/CollectionDockingView.tsx | 63 ++++++++---------- .../collectionFreeForm/CollectionFreeFormView.tsx | 51 ++++++++------- .../collections/collectionFreeForm/MarqueeView.tsx | 3 - .../views/nodes/CollectionFreeFormDocumentView.tsx | 6 +- src/client/views/nodes/DocumentView.tsx | 9 ++- src/client/views/nodes/FormattedTextBox.tsx | 75 +++++++++++----------- src/client/views/nodes/ImageBox.tsx | 1 + src/client/views/nodes/PDFBox.tsx | 20 ++++-- src/client/views/pdf/PDFViewer.tsx | 45 +++++++------ src/new_fields/Doc.ts | 2 +- 15 files changed, 184 insertions(+), 143 deletions(-) (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index 4fac53c7d..2b00a6530 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -307,4 +307,34 @@ export function PostToServer(relativeRoute: string, body: any) { body: body }; return requestPromise.post(options); +} + +const easeInOutQuad = (currentTime: number, start: number, change: number, duration: number) => { + let newCurrentTime = currentTime / (duration / 2); + + if (newCurrentTime < 1) { + return (change / 2) * newCurrentTime * newCurrentTime + start; + } + + newCurrentTime -= 1; + return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start; +}; + +export default function smoothScroll(duration: number, element: HTMLElement, to: number) { + const start = element.scrollTop; + const change = to - start; + const startDate = new Date().getTime(); + + const animateScroll = () => { + const currentDate = new Date().getTime(); + const currentTime = currentDate - startDate; + element.scrollTop = easeInOutQuad(currentTime, start, change, duration); + + if (currentTime < duration) { + requestAnimationFrame(animateScroll); + } else { + element.scrollTop = to; + } + }; + animateScroll(); } \ No newline at end of file diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index e60ab09bb..c048125c5 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -132,7 +132,7 @@ export class DocumentManager { let doc = Doc.GetProto(docDelegate); const contextDoc = await Cast(doc.annotationOn, Doc); if (contextDoc) { - contextDoc.panY = doc.y; + contextDoc.scrollY = NumCast(doc.y) - NumCast(contextDoc.height) / 2; } let docView: DocumentView | null; @@ -178,7 +178,7 @@ export class DocumentManager { (dockFunc || CollectionDockingView.AddRightSplit)(contextDoc, undefined); setTimeout(() => { this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage); - }, 10); + }, 1000); } } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index cacaddcc8..944ae586c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -515,8 +515,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); let proto = doc.isTemplate ? doc : Doc.GetProto(element.props.Document); // bcz: 'doc' didn't work here... - let fixedAspect = e.ctrlKey || (!BoolCast(doc.ignoreAspect) && nwidth && nheight); - if (fixedAspect && e.ctrlKey && BoolCast(doc.ignoreAspect)) { + let fixedAspect = e.ctrlKey || (!doc.ignoreAspect && nwidth && nheight); + if (fixedAspect && e.ctrlKey && doc.ignoreAspect) { doc.ignoreAspect = false; proto.nativeWidth = nwidth = doc.width || 0; proto.nativeHeight = nheight = doc.height || 0; @@ -531,7 +531,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> Doc.SetInPlace(element.props.Document, "nativeWidth", actualdW / (doc.width || 1) * (doc.nativeWidth || 0), true); } doc.width = actualdW; - if (fixedAspect) doc.height = nheight / nwidth * doc.width; + if (fixedAspect && !doc.fitWidth) doc.height = nheight / nwidth * doc.width; else doc.height = actualdH; } else { @@ -539,7 +539,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> Doc.SetInPlace(element.props.Document, "nativeHeight", actualdH / (doc.height || 1) * (doc.nativeHeight || 0), true); } doc.height = actualdH; - if (fixedAspect) doc.width = nwidth / nheight * doc.height; + if (fixedAspect && !doc.fitWidth) doc.width = nwidth / nheight * doc.height; else doc.width = actualdW; } } else { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 244b217ed..0d1546e68 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -505,6 +505,7 @@ export class MainView extends React.Component {
  • +
  • ; @@ -520,12 +521,8 @@ export class MainView extends React.Component { /* @TODO this should really be moved into a moveable toolbar component, but for now let's put it here to meet the deadline */ @computed get miscButtons() { - let logoutRef = React.createRef(); - return [ this.isSearchVisible ?
    : null, -
    -
    ]; } diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 0e7e0afa7..6f5abd05b 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -1,8 +1,5 @@ @import "../../views/globalCssVariables.scss"; -.collectiondockingview-content { - height: 100%; -} .lm_active .messageCounter{ color:white; background: #999999; @@ -21,7 +18,7 @@ .collectiondockingview-container { width: 100%; - height: 100%; + height:100%; border-style: solid; border-width: $COLLECTION_BORDER_WIDTH; position: absolute; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index b6bc4b4ba..b047e77a8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -32,6 +32,7 @@ import React = require("react"); import { ButtonSelector } from './ParentDocumentSelector'; import { DocumentType } from '../../documents/DocumentTypes'; library.add(faFile); +const _global = (window /* browser */ || global /* node */) as any; @observer export class CollectionDockingView extends React.Component { @@ -534,12 +535,11 @@ interface DockedFrameProps { } @observer export class DockedFrameRenderer extends React.Component { - _mainCont: HTMLDivElement | undefined = undefined; + _mainCont: HTMLDivElement | null = null; @observable private _panelWidth = 0; @observable private _panelHeight = 0; @observable private _document: Opt; @observable private _dataDoc: Opt; - @observable private _isActive: boolean = false; get _stack(): any { @@ -577,6 +577,13 @@ export class DockedFrameRenderer extends React.Component { } componentDidMount() { + let observer = new _global.ResizeObserver(action((entries: any) => { + for (let entry of entries) { + this._panelWidth = entry.contentRect.width; + this._panelHeight = entry.contentRect.height; + } + })); + observer.observe(this.props.glContainer._element[0]); this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); this.props.glContainer.on("tab", this.onActiveContentItemChanged); this.onActiveContentItemChanged(); @@ -595,15 +602,16 @@ export class DockedFrameRenderer extends React.Component { } } - panelWidth = () => this._document!.ignoreAspect ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); - panelHeight = () => this._document!.ignoreAspect ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight())); + panelWidth = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); + panelHeight = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight())); - nativeWidth = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; - nativeHeight = () => !this._document!.ignoreAspect ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; + nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; + nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; contentScaling = () => { if (this._document!.type === DocumentType.PDF) { - if (this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) { + if ((this._document && this._document.fitWidth) || + this._panelHeight / NumCast(this._document!.nativeHeight) > this._panelWidth / NumCast(this._document!.nativeWidth)) { return this._panelWidth / NumCast(this._document!.nativeWidth); } else { return this._panelHeight / NumCast(this._document!.nativeHeight); @@ -639,13 +647,10 @@ export class DockedFrameRenderer extends React.Component { return CollectionDockingView.Instance.AddTab(this._stack, doc, dataDoc); } } - @computed get docView() { - if (!this._document) { - return (null); - } - let resolvedDataDoc = this._document.layout instanceof Doc ? this._document : this._dataDoc; - return { getScale={returnOne} />; } - @computed get content() { - return ( -
    { - this._mainCont = ref; - if (ref) { - this._panelWidth = Number(getComputedStyle(ref).width!.replace("px", "")); - this._panelHeight = Number(getComputedStyle(ref).height!.replace("px", "")); - } - })} - style={{ transform: `translate(${this.previewPanelCenteringOffset}px, 0px)` }}> - {this.docView} -
    ); - } - render() { - if (!this._isActive || !this._document) return null; - let theContent = this.content; - return !this._document ? (null) : - { this._panelWidth = r.offset.width; this._panelHeight = r.offset.height; })}> - {({ measureRef }) =>
    - {theContent} -
    } -
    ; + return (!this._isActive || !this._document) ? (null) : + (
    this._mainCont = ref} + style={{ + transform: `translate(${this.previewPanelCenteringOffset}px, 0px)`, + height: this._document && this._document.fitWidth ? undefined : "100%" + }}> + {this.docView(this._document)} +
    ); } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 721732774..4b260d111 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -401,27 +401,34 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } SelectionManager.DeselectAll(); - const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; - const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; - const newState = HistoryUtil.getState(); - newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY }; - HistoryUtil.pushState(newState); - - let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType }; - - this.setPan(newPanX, newPanY); - this.Document.panTransformType = "Ease"; - this.props.focus(this.props.Document); - willZoom && this.setScaleToZoom(doc, scale); - - afterFocus && setTimeout(() => { - if (afterFocus && afterFocus()) { - this.Document.panX = savedState.px; - this.Document.panY = savedState.py; - this.Document.scale = savedState.s; - this.Document.panTransformType = savedState.pt; - } - }, 1000); + if (this.props.Document.scrollHeight) { + let annotOn = Cast(doc.annotationOn, Doc) as Doc; + let offset = annotOn && (NumCast(annotOn.height) / 2); + this.props.Document.scrollY = NumCast(doc.y) - offset; + } else { + const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; + const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; + const newState = HistoryUtil.getState(); + newState.initializers![this.Document[Id]] = { panX: newPanX, panY: newPanY }; + HistoryUtil.pushState(newState); + + let savedState = { px: this.Document.panX, py: this.Document.panY, s: this.Document.scale, pt: this.Document.panTransformType }; + + this.setPan(newPanX, newPanY); + this.Document.panTransformType = "Ease"; + this.props.focus(this.props.Document); + willZoom && this.setScaleToZoom(doc, scale); + + afterFocus && setTimeout(() => { + if (afterFocus && afterFocus()) { + this.Document.panX = savedState.px; + this.Document.panY = savedState.py; + this.Document.scale = savedState.s; + this.Document.panTransformType = savedState.pt; + } + }, 1000); + } + } setScaleToZoom = (doc: Doc, scale: number = 0.5) => { @@ -449,7 +456,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { PanelHeight: childLayout[HeightSym], ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, - ContainingCollectionDoc: this.props.ContainingCollectionDoc, + ContainingCollectionDoc: this.props.Document, focus: this.focusDocument, backgroundColor: this.getClusterColor, parentActive: this.props.active, diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 44611869e..82193aefa 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -188,16 +188,13 @@ export class MarqueeView extends React.Component @action onPointerUp = (e: PointerEvent): void => { if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]); - // console.log("pointer up!"); if (this._visible) { - // console.log("visible"); let mselect = this.marqueeSelect(); if (!e.shiftKey) { SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document); } this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]); } - //console.log("invisible"); this.cleanupInteractions(true); if (e.altKey) { diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index c3d2c9e51..c4fed200f 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -77,7 +77,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { let ruleRounding = this.props.ruleProvider ? StrCast(this.props.ruleProvider["ruleRounding_" + this.Document.heading]) : undefined; - let ld = this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout as Doc : undefined; + let ld = this.layoutDoc.layout instanceof Doc ? this.layoutDoc.layout : undefined; let br = StrCast((ld || this.props.Document).borderRounding); br = !br && ruleRounding ? ruleRounding : br; if (br.endsWith("%")) { @@ -100,7 +100,7 @@ export class CollectionFreeFormDocumentView extends DocComponent this.dataProvider ? this.dataProvider.width : this.panelWidth(); + finalPanelWidth = () => this.dataProvider ? this.dataProvider.width : this.panelWidth(); finalPanelHeight = () => this.dataProvider ? this.dataProvider.height : this.panelHeight(); render() { @@ -124,7 +124,7 @@ export class CollectionFreeFormDocumentView extends DocComponent
    diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6ee88f834..bd702c6a2 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -223,15 +223,18 @@ export class DocumentView extends DocComponent(Docu else if (linkedDocs.length) { SelectionManager.DeselectAll(); let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored); + let second = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor2 as Doc, this.props.Document) && !d.anchor2anchored); let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); + let secondUnshown = second.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); if (firstUnshown.length) first = [firstUnshown[0]]; - let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]]; + if (secondUnshown.length) second = [secondUnshown[0]]; + let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor1 as Doc] : undefined; // @TODO: shouldn't always follow target context let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined]; let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined]; - if (!linkedFwdDocs.some(l => l instanceof Promise)) { + if (linkedFwdDocs && !linkedFwdDocs.some(l => l instanceof Promise)) { let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab"); let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, @@ -601,7 +604,7 @@ export class DocumentView extends DocComponent(Docu ruleColor && !colorSet ? ruleColor : StrCast(this.layoutDoc.backgroundColor) || this.props.backgroundColor(this.Document); const nativeWidth = this.nativeWidth > 0 && !this.Document.ignoreAspect ? `${this.nativeWidth}px` : "100%"; - const nativeHeight = this.Document.ignoreAspect ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : nativeWidth !== "100%" ? nativeWidth : "100%"; + const nativeHeight = this.Document.ignoreAspect || this.props.Document.fitWidth ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : nativeWidth !== "100%" ? nativeWidth : "100%"; const showOverlays = this.props.showOverlays ? this.props.showOverlays(this.Document) : undefined; const showTitle = showOverlays && "title" in showOverlays ? showOverlays.title : this.getLayoutPropStr("showTitle"); const showCaption = showOverlays && "caption" in showOverlays ? showOverlays.caption : this.getLayoutPropStr("showCaption"); diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 923dd1544..565fb0505 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -815,6 +815,40 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); } + let ctrlKey = e.ctrlKey; + if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { + e.preventDefault(); + } + } + + onPointerUp = (e: React.PointerEvent): void => { + FormattedTextBoxComment.textBox = this; + if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { + e.stopPropagation(); + } + } + + @action + onFocused = (e: React.FocusEvent): void => { + document.removeEventListener("keypress", this.recordKeyHandler); + document.addEventListener("keypress", this.recordKeyHandler); + this.tryUpdateHeight(); + if (!this.props.isOverlay) { + FormattedTextBox.InputBoxOverlay = this; + } else { + if (this._ref.current) { + this._ref.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll; + } + } + } + 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) { + e.stopPropagation(); + } + } + + onClick = (e: React.MouseEvent): void => { let ctrlKey = e.ctrlKey; if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) { let href = (e.target as any).href; @@ -829,8 +863,10 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let node = pcords && this._editorView!.state.doc.nodeAt(pcords.pos); if (node) { let link = node.marks.find(m => m.type === this._editorView!.state.schema.marks.link); - href = link && link.attrs.href; - location = link && link.attrs.location; + if (link && !(link.attrs.docref && link.attrs.title)) { // bcz: getting hacky. this indicates that we clicked on a PDF excerpt quotation. In this case, we don't want to follow the link (we follow only the actual hyperlink for the quotation which is handled above). + href = link && link.attrs.href; + location = link && link.attrs.location; + } } if (href) { if (href.indexOf(Utils.prepend("/doc/")) === 0) { @@ -848,7 +884,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe return; } } - if (targetContext) { + if (targetContext && (!jumpToDoc || targetContext !== await jumpToDoc.annotationOn)) { DocumentManager.Instance.jumpToDocument(targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); } else if (jumpToDoc) { DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); @@ -870,39 +906,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } - if (e.button === 2 || (e.button === 0 && e.ctrlKey)) { - e.preventDefault(); - } - } - - onPointerUp = (e: React.PointerEvent): void => { - FormattedTextBoxComment.textBox = this; - if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { - e.stopPropagation(); - } - } - - @action - onFocused = (e: React.FocusEvent): void => { - document.removeEventListener("keypress", this.recordKeyHandler); - document.addEventListener("keypress", this.recordKeyHandler); - this.tryUpdateHeight(); - if (!this.props.isOverlay) { - FormattedTextBox.InputBoxOverlay = this; - } else { - if (this._ref.current) { - this._ref.current.scrollTop = FormattedTextBox.InputBoxOverlayScroll; - } - } - } - 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) { - e.stopPropagation(); - } - } - - onClick = (e: React.MouseEvent): void => { // 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. if (this.props.isSelected() && e.nativeEvent.offsetX < 40) { let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY }); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 624593245..004f50590 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -38,6 +38,7 @@ library.add(faFileAudio, faAsterisk); export const pageSchema = createSchema({ curPage: "number", + fitWidth: "boolean" }); interface Window { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 0fcbaaa7c..fe71e76fd 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -20,6 +20,9 @@ import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); import { undoBatch } from '../../util/UndoManager'; +import { ContextMenuProps } from '../ContextMenuItem'; +import { ContextMenu } from '../ContextMenu'; +import { Utils } from '../../../Utils'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @@ -58,7 +61,7 @@ export class PDFBox extends DocComponent(PdfDocumen 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); + !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); } public search(string: string, fwd: boolean) { this._pdfViewer && this._pdfViewer.search(string, fwd); } @@ -165,14 +168,23 @@ export class PDFBox extends DocComponent(PdfDocumen
    ); } + specificContextMenu = (e: React.MouseEvent): void => { + const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); + let funcs: ContextMenuProps[] = []; + pdfUrl && funcs.push({ description: "Copy path", event: () => Utils.CopyText(pdfUrl.url.pathname), icon: "expand-arrows-alt" }); + funcs.push({ description: "Toggle Fit Width " + (this.Document.fitWidth ? "Off" : "On"), event: () => this.Document.fitWidth = !this.Document.fitWidth, icon: "expand-arrows-alt" }); + + ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); + } + render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); return (!(pdfUrl instanceof PdfField) || !this._pdf ?
    {`pdf, ${this.dataDoc[this.props.fieldKey]}, not found`}
    : -
    { +
    { let hit = document.elementFromPoint(e.clientX, e.clientY); - if (hit && hit.localName === "span" && this.props.isSelected()) { + if (hit && hit.localName === "span" && this.props.isSelected()) { // drag selecting text stops propagation e.button === 0 && e.stopPropagation(); } }}> @@ -182,7 +194,7 @@ export class PDFBox extends DocComponent(PdfDocumen 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} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} /> {this.settingsPanel()} diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 5ad4ffd48..13fd8ea98 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -9,14 +9,12 @@ import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnOne, Utils } from "../../../Utils"; +import smoothScroll, { Utils, emptyFunction, returnOne } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; import { Transform } from "../../util/Transform"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; -import Annotation from "./Annotation"; import PDFMenu from "./PDFMenu"; import "./PDFViewer.scss"; import React = require("react"); @@ -24,7 +22,8 @@ import * as rp from "request-promise"; import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; -import { SelectionManager } from "../../util/SelectionManager"; +import Annotation from "./Annotation"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer"); const pdfjsLib = require("pdfjs-dist"); @@ -41,6 +40,7 @@ interface IViewerProps { PanelWidth: () => number; PanelHeight: () => number; ContentScaling: () => number; + select: (isCtrlPressed: boolean) => void; renderDepth: number; isSelected: () => boolean; loaded: (nw: number, nh: number, np: number) => void; @@ -106,11 +106,20 @@ export class PDFViewer extends React.Component { // file address of the pdf this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); runInAction(() => this._showWaiting = this._showCover = true); - this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => { - this.setupPdfJsViewer(); - this._selectionReactionDisposer && this._selectionReactionDisposer(); - this._selectionReactionDisposer = undefined; - }) + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.setupPdfJsViewer()); + this._reactionDisposer = reaction( + () => this.props.Document.scrollY, + (scrollY) => { + if (scrollY !== undefined) { + if (this._showCover || this._showWaiting) { + this.setupPdfJsViewer(); + } + this._mainCont.current && smoothScroll(1000, this._mainCont.current, NumCast(this.props.Document.scrollY) || 0); + this.props.Document.scrollY = undefined; + } + }, + { fireImmediately: true } + ); } componentWillUnmount = () => { @@ -153,12 +162,14 @@ export class PDFViewer extends React.Component { i === this.props.pdf.numPages - 1 && this.props.loaded((page.view[page.rotate === 0 || page.rotate === 180 ? 2 : 3] - page.view[page.rotate === 0 || page.rotate === 180 ? 0 : 1]), (page.view[page.rotate === 0 || page.rotate === 180 ? 3 : 2] - page.view[page.rotate === 0 || page.rotate === 180 ? 1 : 0]), i); })))); - Doc.GetProto(this.props.Document).scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0); + Doc.GetProto(this.props.Document).scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72; } } @action setupPdfJsViewer = async () => { + this._selectionReactionDisposer && this._selectionReactionDisposer(); + this._selectionReactionDisposer = undefined; this._showWaiting = true; this.props.setPdfViewer(this); await this.initialLoad(); @@ -180,10 +191,6 @@ export class PDFViewer extends React.Component { }), { fireImmediately: true } ); - this._reactionDisposer = reaction( - () => this.props.Document.panY, - () => this._mainCont.current && this._mainCont.current.scrollTo({ top: NumCast(this.props.Document.panY) || 0, behavior: "auto" }) - ); document.removeEventListener("copy", this.copy); document.addEventListener("copy", this.copy); @@ -228,10 +235,9 @@ export class PDFViewer extends React.Component { annoDocs.push(annoDoc); annoDoc.isButton = true; anno.remove(); - // this.props.addDocument && this.props.addDocument(annoDoc, false); mainAnnoDoc = annoDoc; + mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); mainAnnoDocProto.y = annoDoc.y; - mainAnnoDocProto = Doc.GetProto(annoDoc); } else { this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => value.map(anno => { let annoDoc = new Doc(); @@ -381,7 +387,7 @@ export class PDFViewer extends React.Component { this._downX = e.clientX; this._downY = e.clientY; if (NumCast(this.props.Document.scale, 1) !== 1) return; - if (e.button !== 0 && this.active()) { + if ((e.button !== 0 || e.altKey) && this.active()) { this._setPreviewCursor && this._setPreviewCursor(e.clientX, e.clientY, true); } this._marqueeing = false; @@ -638,18 +644,15 @@ export class PDFViewer extends React.Component { } @computed get annotationLayer() { - trace(); return
    {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => )}
    ; } @computed get pdfViewerDiv() { - trace(); return
    ; } @computed get standinViews() { - trace(); return <> {this._showCover ? this.getCoverImage() : (null)} {this._showWaiting ? : (null)} @@ -710,7 +713,7 @@ class PdfViewerMarquee extends React.Component { width: `${this.props.width()}px`, height: `${this.props.height()}px`, border: `${this.props.width() === 0 ? "" : "2px dashed black"}` }}> -
    +
    ; } } diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 1b3c8b0b0..79b73aba8 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -344,7 +344,7 @@ export namespace Doc { let list = Cast(target[key], listSpec(Doc)); if (list) { if (allowDuplicates !== true) { - let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1); + let pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1); if (pind !== -1) { list.splice(pind, 1); } -- cgit v1.2.3-70-g09d2 From c40480ec1a2da84b4223b0605bea2fe19df1104c Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 29 Sep 2019 11:49:53 -0400 Subject: fixed deleting pdf annotations. changed stacking view + fitToWidth to not be bigger than stacking view height. --- .../views/collections/CollectionStackingView.tsx | 4 ++-- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 13 ++++++------- src/client/views/pdf/Annotation.tsx | 18 +++++++++--------- 4 files changed, 18 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 597f3f745..45de0fefa 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -160,13 +160,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { if (!d) return 0; let nw = NumCast(d.nativeWidth); let nh = NumCast(d.nativeHeight); - if (!d.ignoreAspect && nw && nh) { + if (!d.ignoreAspect && !d.fitWidth && nw && nh) { let aspect = nw && nh ? nh / nw : 1; let wid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1); if (!(d.nativeWidth && !d.ignoreAspect && this.props.Document.fillColumn)) wid = Math.min(d[WidthSym](), wid); return wid * aspect; } - return d[HeightSym](); + return d.fitWidth ? Math.min(this.props.PanelHeight() - 2 * this.yMargin, d[HeightSym]()) : d[HeightSym](); } columnDividerDown = (e: React.PointerEvent) => { diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index ec1b03a40..b93c78cfd 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -95,7 +95,7 @@ export class FieldView extends React.Component { return

    {field.date.toLocaleString()}

    ; } else if (field instanceof Doc) { - return

    {field.title}

    ; + return

    {field.title && field.title.toString()}

    ; //return

    {field.title + " : id= " + field[Id]}

    ; // let returnHundred = () => 100; // return ( diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 565fb0505..63a16f90c 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -80,7 +80,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe private _nodeClicked: any; private _undoTyping?: UndoManager.Batch; private _searchReactionDisposer?: Lambda; - private _scroolToRegionReactionDisposer: Opt; + private _scrollToRegionReactionDisposer: Opt; private _reactionDisposer: Opt; private _textReactionDisposer: Opt; private _heightReactionDisposer: Opt; @@ -140,7 +140,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe DragManager.StartDragFunctions.push(() => FormattedTextBox.InputBoxOverlay = undefined); } - this._scroolToRegionReactionDisposer = reaction( + this._scrollToRegionReactionDisposer = reaction( () => StrCast(this.props.Document.scrollToLinkID), async (scrollToLinkID) => { let findLinkFrag = (frag: Fragment, editor: EditorView) => { @@ -165,7 +165,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe }; let start = -1; - if (this._editorView && scrollToLinkID) { let editor = this._editorView; let ret = findLinkFrag(editor.state.doc.content, editor); @@ -179,12 +178,12 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe 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; } + this.props.Document.scrollToLinkID = undefined; } - } + }, + { fireImmediately: true } ); } @@ -793,7 +792,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } componentWillUnmount() { - this._scroolToRegionReactionDisposer && this._scroolToRegionReactionDisposer(); + this._scrollToRegionReactionDisposer && this._scrollToRegionReactionDisposer(); this._rulesReactionDisposer && this._rulesReactionDisposer(); this._reactionDisposer && this._reactionDisposer(); this._proxyReactionDisposer && this._proxyReactionDisposer(); diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index a9fa883c8..3ed85f6a5 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -85,7 +85,15 @@ class RegionAnnotation extends React.Component { @action onPointerDown = async (e: React.PointerEvent) => { - if (e.button === 0) { + if (e.button === 2 || e.ctrlKey) { + PDFMenu.Instance.Status = "annotation"; + PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this); + PDFMenu.Instance.Pinned = false; + PDFMenu.Instance.AddTag = this.addTag.bind(this); + PDFMenu.Instance.PinToPres = this.pinToPres; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); + } + else if (e.button === 0) { let targetDoc = await Cast(this.props.document.target, Doc); if (targetDoc) { let context = await Cast(targetDoc.targetContext, Doc); @@ -96,14 +104,6 @@ class RegionAnnotation extends React.Component { } } } - if (e.button === 2) { - PDFMenu.Instance.Status = "annotation"; - PDFMenu.Instance.Delete = this.deleteAnnotation.bind(this); - PDFMenu.Instance.Pinned = false; - PDFMenu.Instance.AddTag = this.addTag.bind(this); - PDFMenu.Instance.PinToPres = this.pinToPres; - PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); - } } addTag = (key: string, value: string): boolean => { -- cgit v1.2.3-70-g09d2 From d2207f2fe6369edea33bec74812a8112b138aaee Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 1 Oct 2019 16:24:18 -0400 Subject: toggle button --- src/client/views/animationtimeline/Timeline.scss | 44 +++++++++++++++------ src/client/views/animationtimeline/Timeline.tsx | 45 ++++++++++++++++------ .../views/animationtimeline/TimelineOverview.tsx | 2 - 3 files changed, 65 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 990bc681c..09fc593fc 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -1,11 +1,6 @@ @import "./../globalCssVariables.scss"; -.minimize{ - position:relative; - z-index: 1000; - height: 30px; - width: 100px; -} + .timeline-toolbox{ position:absolute; @@ -17,6 +12,8 @@ margin-left:10px; } } + + .timeline-container{ width:100%; height:300px; @@ -37,12 +34,10 @@ background-color: transparent; height: 30px; width:100%; - .tick{ height:100%; width: 1px; background-color:black; - } } .scrubber{ @@ -128,15 +123,40 @@ background-color: white; border: 2px solid purple; border-radius: 20px; + animation-fill-mode: forwards; + animation-duration: 500ms; input{ + position:absolute; opacity: 0; height: 0; width: 0; } - .slider{ - height: 40px; - width: 40px; - background-color: black; + .round-toggle-slider{ + position:absolute; + height: 35px; + width: 35px; + top: 0.5px; + background-color: white; + border:1px solid grey; border-radius: 20px; + transition: transform 500ms ease-in-out; + + } + +} +@keyframes turnon{ + from{ + background-color: white; + } + to{ + background-color: purple; + } +} +@keyframes turnoff{ + from{ + background-color: purple; + } + to{ + background-color: white; } } \ No newline at end of file diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 8f96315a0..8127e4de2 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -29,6 +29,8 @@ export class Timeline extends React.Component { @observable private _titleContainer = React.createRef(); @observable private _timelineContainer = React.createRef(); @observable private _infoContainer = React.createRef(); + @observable private _roundToggleRef = React.createRef(); + @observable private _roundToggleContainerRef = React.createRef(); @observable private _currentBarX: number = 0; @observable private _windSpeed: number = 1; @@ -331,23 +333,45 @@ export class Timeline extends React.Component { private timelineToolBox = (scale:number) => { let size = 50 * scale; //50 is default - return ( + return ( +
    -
    -
    -
    - +
    +
    +
    + +
    +
    +
    ); } + @action + private toggleChecked = (e:React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + let roundToggle = this._roundToggleRef.current!; + let roundToggleContainer = this._roundToggleContainerRef.current!; + if (BoolCast(this.props.Document.isAnimating)){ + roundToggle.style.transform = "translate(0px, 0px)"; + roundToggle.style.animationName = "turnoff"; + roundToggleContainer.style.animationName = "turnoff"; + + this.props.Document.isAnimating = false; + } else { + roundToggle.style.transform = "translate(45px, 0px)"; + roundToggle.style.animationName = "turnon"; + roundToggleContainer.style.animationName = "turnon"; + this.props.Document.isAnimating = true; + } + } render() { return (
    - {this.timelineToolBox(0.5)}
    {this._ticks.map(element => { @@ -365,16 +389,13 @@ export class Timeline extends React.Component { {DocListCast(this.children).map(doc =>
    {Doc.BrushDoc(doc);}} onPointerOut={() => {Doc.UnBrushDoc(doc);}}>

    {doc.title}

    )}
    - +
    - {BoolCast(this.props.Document.isAnimating) ?
    : this.timelineToolBox(1) } + { this.timelineToolBox(1) }
    - +
    ); diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx index a8f7faba8..38b823cbc 100644 --- a/src/client/views/animationtimeline/TimelineOverview.tsx +++ b/src/client/views/animationtimeline/TimelineOverview.tsx @@ -78,8 +78,6 @@ export class TimelineOverview extends React.Component{ } render(){ - console.log("rendered"); - console.log(this.props.visibleStart); return(
    -- cgit v1.2.3-70-g09d2 From 6234baa41b8d83138372a1b719e1221019e2ba3b Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 1 Oct 2019 16:46:21 -0400 Subject: new changes --- src/client/documents/Documents.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 392dca373..64201f645 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -672,3 +672,4 @@ export namespace DocUtils { } Scripting.addGlobal("Docs", Docs); + -- cgit v1.2.3-70-g09d2 From 7ccce9564e11ede82bec01b9eea3889da4dc1bc2 Mon Sep 17 00:00:00 2001 From: andrewdkim Date: Tue, 1 Oct 2019 17:10:17 -0400 Subject: changes with toggling --- src/client/views/animationtimeline/Timeline.scss | 11 +++++------ src/client/views/animationtimeline/Timeline.tsx | 8 +++++--- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/client/views/animationtimeline/Timeline.scss b/src/client/views/animationtimeline/Timeline.scss index 09fc593fc..71fac876d 100644 --- a/src/client/views/animationtimeline/Timeline.scss +++ b/src/client/views/animationtimeline/Timeline.scss @@ -22,7 +22,7 @@ box-shadow: 0px 10px 20px; .info-container{ - margin-top: 50px; + margin-top: 80px; right:20px; position:absolute; height: calc(100% - 100px); @@ -72,7 +72,7 @@ } .title-container{ - margin-top: 80px; + margin-top: 110px; margin-left: 20px; height: calc(100% - 100px - 30px); width: 100px; @@ -117,7 +117,6 @@ } .round-toggle { - position: absolute; height: 40px; width: 80px; background-color: white; @@ -125,6 +124,7 @@ border-radius: 20px; animation-fill-mode: forwards; animation-duration: 500ms; + top: 20px; input{ position:absolute; opacity: 0; @@ -132,15 +132,14 @@ width: 0; } .round-toggle-slider{ - position:absolute; height: 35px; width: 35px; - top: 0.5px; background-color: white; border:1px solid grey; border-radius: 20px; transition: transform 500ms ease-in-out; - + margin-left: 0px; + margin-top: 0.5px; } } diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 8127e4de2..bc48f422a 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -18,7 +18,7 @@ import { KeyframeFunc } from "./Keyframe"; @observer export class Timeline extends React.Component { - private readonly DEFAULT_CONTAINER_HEIGHT: number = 300; + private readonly DEFAULT_CONTAINER_HEIGHT: number = 330; private readonly DEFAULT_TICK_SPACING: number = 50; private readonly MIN_CONTAINER_HEIGHT: number = 205; private readonly MAX_CONTAINER_HEIGHT: number = 800; @@ -353,16 +353,18 @@ export class Timeline extends React.Component { e.stopPropagation(); let roundToggle = this._roundToggleRef.current!; let roundToggleContainer = this._roundToggleContainerRef.current!; + let timelineContainer = this._timelineContainer.current!; if (BoolCast(this.props.Document.isAnimating)){ roundToggle.style.transform = "translate(0px, 0px)"; roundToggle.style.animationName = "turnoff"; roundToggleContainer.style.animationName = "turnoff"; - + timelineContainer.style.transform = `translate(0px, ${this._containerHeight}px)`; this.props.Document.isAnimating = false; } else { roundToggle.style.transform = "translate(45px, 0px)"; roundToggle.style.animationName = "turnon"; roundToggleContainer.style.animationName = "turnon"; + timelineContainer.style.transform = `translate(0px, ${-this._containerHeight}px)`; this.props.Document.isAnimating = true; } } @@ -371,7 +373,7 @@ export class Timeline extends React.Component {
    -
    +
    {this._ticks.map(element => { -- cgit v1.2.3-70-g09d2