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 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/client/views/pdf/PDFViewer.scss') 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 -- 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/client/views/pdf/PDFViewer.scss') 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/client/views/pdf/PDFViewer.scss') 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/client/views/pdf/PDFViewer.scss') 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/client/views/pdf/PDFViewer.scss') 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