From f60398d5db9041e09c809c16a0b885936ac11a3d Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 10:21:37 -0400 Subject: fixed multi-column stacking --- .../views/collections/CollectionStackingView.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index f5ad4ee95..f80ba0a4b 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -96,8 +96,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { get children() { return this.childDocs.filter(d => !d.isMinimized).map((d, i) => { let dref = React.createRef(); - let dxf = () => this.getDocTransform(d, dref.current!); - let rowSpan = Math.ceil((this.columnWidth / d[WidthSym]() * d[HeightSym]() + this.gridGap) / (this._gridSize + this.gridGap)); + let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]()); + let renderScale = this.columnWidth / NumCast(d.nativeWidth, this.columnWidth); + let aspect = NumCast(d.nativeWidth) / NumCast(d.nativeHeight); + let width = () => this.columnWidth; + let height = () => aspect ? width() / aspect : d[HeightSym]() + let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); let childFocus = (doc: Doc) => { doc.libraryBrush = true; this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered. @@ -106,12 +110,12 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { key={d[Id]} ref={dref} style={{ - width: NumCast(d.nativeWidth, d[WidthSym]()), - height: NumCast(d.nativeHeight, d[HeightSym]()), + width: width(), + height: height(), transformOrigin: "top left", gridRowEnd: `span ${rowSpan}`, gridColumnEnd: `span 1`, - transform: `scale(${this.columnWidth / NumCast(d.nativeWidth, d[WidthSym]())}, ${this.columnWidth / NumCast(d.nativeWidth, d[WidthSym]())})` + transform: `scale(${renderScale})` }} > doc) { ScreenToLocalTransform={dxf} focus={childFocus} ContentScaling={returnOne} - PanelWidth={d[WidthSym]} - PanelHeight={d[HeightSym]} + PanelWidth={width} + PanelHeight={height} selectOnLoad={false} parentActive={this.props.active} addDocTab={this.props.addDocTab} -- cgit v1.2.3-70-g09d2 From 0bb20528c8167b3ba1c4c88d97586d50ae183b4c Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 10:37:36 -0400 Subject: added highlight for expanded tree view items --- src/client/views/collections/CollectionTreeView.scss | 5 +++++ src/client/views/collections/CollectionTreeView.tsx | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index f6df96d92..2d5092980 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -68,6 +68,11 @@ display: none; } +.treeViewItem-border { + display:inherit; + border-left: dashed 1px #00000042; +} + .treeViewItem-header:hover { .treeViewItem-openRight { display: inline-block; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 5e690361c..d43402e7d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -302,7 +302,9 @@ class TreeView extends React.Component { {this.renderBullet(bulletType)} {this.renderTitle()} - {contentElement} +
+ {contentElement} +
; } -- cgit v1.2.3-70-g09d2 From 37f327ab659e6fa1221f9f4ed7649402c5dedc00 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 11:24:32 -0400 Subject: aspect ratio, dragging, and full screen scrolling fixed --- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/pdf/PDFMenu.tsx | 20 ++++++++++++++++---- src/client/views/pdf/PDFViewer.tsx | 15 ++++++++++----- src/client/views/pdf/Page.tsx | 10 +++------- 4 files changed, 30 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 7dd2b1dc5..d2de1cb1c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -97,7 +97,7 @@ export class PDFBox extends DocComponent(PdfDocumen render() { // uses mozilla pdf as default const pdfUrl = Cast(this.props.Document.data, PdfField, new PdfField(window.origin + RouteStore.corsProxy + "/https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf")); - let classname = "pdfBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); + let classname = "pdfBox-cont" + (this.props.active() && !InkingControl.Instance.selectedTool && !this._alt ? "-interactive" : ""); return (
; + private _dragging: boolean = false; constructor(props: Readonly<{}>) { super(props); @@ -35,19 +36,30 @@ export default class PDFMenu extends React.Component { } pointerDown = (e: React.PointerEvent) => { - document.removeEventListener("pointermove", this.StartDrag); - document.addEventListener("pointermove", this.StartDrag); + document.removeEventListener("pointermove", this.pointerMove); + document.addEventListener("pointermove", this.pointerMove); document.removeEventListener("pointerup", this.pointerUp); document.addEventListener("pointerup", this.pointerUp); - console.log(this.StartDrag); + e.stopPropagation(); + e.preventDefault(); + } + pointerMove = (e: PointerEvent) => { e.stopPropagation(); e.preventDefault(); + + if (this._dragging) { + return; + } + + this.StartDrag(e); + this._dragging = true; } pointerUp = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.StartDrag); + this._dragging = false; + document.removeEventListener("pointermove", this.pointerMove); document.removeEventListener("pointerup", this.pointerUp); e.stopPropagation(); e.preventDefault(); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 8c0aaea00..67278b1eb 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -90,7 +90,8 @@ class Viewer extends React.Component { @action componentDidMount = () => { this._reactionDisposer = reaction( - () => [this.props.parent.props.active(), this.startIndex, this.endIndex], + + () => [this.props.parent.props.active(), this.startIndex, this._pageSizes.length ? this.endIndex : 0], async () => { await this.initialLoad(); this.renderPages(); @@ -114,12 +115,16 @@ class Viewer extends React.Component { let pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages); this._isPage = Array(this.props.pdf.numPages); for (let i = 0; i < this.props.pdf.numPages; i++) { - await this.props.pdf.getPage(i + 1).then(page => runInAction(() => - pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale })); + await this.props.pdf.getPage(i + 1).then(page => runInAction(() => { + // pageSizes[i] = { width: page.view[2] * scale, height: page.view[3] * scale }; + let x = page.getViewport(scale); + pageSizes[i] = { width: x.width, height: x.height }; + })); } runInAction(() => Array.from(Array((this._pageSizes = pageSizes).length).keys()).map(this.getPlaceholderPage)); - this.props.loaded(pageSizes[0].width, pageSizes[0].height, this.props.pdf.numPages); + this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages); + // this.props.loaded(Math.max(...pageSizes.map(i => i.width)), pageSizes[0].height, this.props.pdf.numPages); } } @@ -236,7 +241,7 @@ class Viewer extends React.Component { // endIndex: where to end rendering pages @computed get endIndex(): number { - return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY) + this._pageBuffer); + return Math.min(this.props.pdf.numPages - 1, this.getPageFromScroll(this.scrollY + this._pageSizes[0].height) + this._pageBuffer); } @action diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index a19b64eda..2697c9eee 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -51,7 +51,6 @@ export default class Page extends React.Component { private _marquee: React.RefObject; private _curly: React.RefObject; private _marqueeing: boolean = false; - private _dragging: boolean = false; private _reactionDisposer?: IReactionDisposer; constructor(props: IPageProps) { @@ -151,13 +150,9 @@ export default class Page extends React.Component { */ @action startDrag = (e: PointerEvent): void => { - // the first 5 lines is a hack to prevent text selection while dragging e.preventDefault(); e.stopPropagation(); - if (this._dragging) { - return; - } - this._dragging = true; + console.log("dragging"); let thisDoc = this.props.parent.Document; // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); @@ -179,7 +174,6 @@ export default class Page extends React.Component { endDrag = (e: PointerEvent): void => { // document.removeEventListener("pointermove", this.startDrag); // document.removeEventListener("pointerup", this.endDrag); - this._dragging = false; e.stopPropagation(); } @@ -195,6 +189,8 @@ export default class Page extends React.Component { // document.addEventListener("pointerup", this.endDrag); } else if (e.button === 0) { + PDFMenu.Instance.StartDrag = this.startDrag; + PDFMenu.Instance.Highlight = this.highlight; PDFMenu.Instance.fadeOut(true); let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { -- cgit v1.2.3-70-g09d2 From c056adeca11f35972b5f75c6b1cc31292d5765d4 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 11:47:20 -0400 Subject: push --- src/client/views/pdf/PDFViewer.tsx | 316 ++++++++++++++++++------------------- src/client/views/pdf/Page.tsx | 67 +------- 2 files changed, 154 insertions(+), 229 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 67278b1eb..f0e55705d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -19,6 +19,7 @@ import Page from "./Page"; import "./PDFViewer.scss"; import React = require("react"); import PDFMenu from "./PDFMenu"; +import { UndoManager } from "../../util/UndoManager"; export const scale = 2; interface IPDFViewerProps { @@ -190,7 +191,7 @@ class Viewer extends React.Component { if (this._isPage[page] !== "none") { this._isPage[page] = "none"; this._visibleElements[page] = ( -
); } @@ -204,11 +205,11 @@ class Viewer extends React.Component { pdf={this.props.pdf} page={page} numPages={this.props.pdf.numPages} - key={`rendered-${page + 1}`} + key={`${this.props.url}-rendered-${page + 1}`} name={`${this.props.pdf.fingerprint + `-page${page + 1}`}`} pageLoaded={this.pageLoaded} parent={this.props.parent} - makePin={this.createPinAnnotation} + makePin={emptyFunction} renderAnnotations={this.renderAnnotations} createAnnotation={this.createAnnotation} sendAnnotations={this.receiveAnnotations} @@ -285,27 +286,27 @@ class Viewer extends React.Component { return this._savedAnnotations.getValue(page); } - createPinAnnotation = (x: number, y: number, page: number): void => { - let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" }); - let pinAnno = new Doc(); - pinAnno.x = x; - pinAnno.y = y + this.getScrollFromPage(page); - pinAnno.width = pinAnno.height = PinRadius; - pinAnno.page = page; - pinAnno.target = targetDoc; - pinAnno.type = AnnotationTypes.Pin; - // this._annotations.push(pinAnno); - let annoDoc = new Doc(); - annoDoc.annotations = new List([pinAnno]); - let annotations = DocListCast(this.props.parent.Document.annotations); - if (annotations && annotations.length) { - annotations.push(annoDoc); - this.props.parent.Document.annotations = new List(annotations); - } - else { - this.props.parent.Document.annotations = new List([annoDoc]); - } - } + // createPinAnnotation = (x: number, y: number, page: number): void => { + // let targetDoc = Docs.TextDocument({ width: 100, height: 50, title: "New Pin Annotation" }); + // let pinAnno = new Doc(); + // pinAnno.x = x; + // pinAnno.y = y + this.getScrollFromPage(page); + // pinAnno.width = pinAnno.height = PinRadius; + // pinAnno.page = page; + // pinAnno.target = targetDoc; + // pinAnno.type = AnnotationTypes.Pin; + // // this._annotations.push(pinAnno); + // let annoDoc = new Doc(); + // annoDoc.annotations = new List([pinAnno]); + // let annotations = DocListCast(this.props.parent.Document.annotations); + // if (annotations && annotations.length) { + // annotations.push(annoDoc); + // this.props.parent.Document.annotations = new List(annotations); + // } + // else { + // this.props.parent.Document.annotations = new List([annoDoc]); + // } + // } // get the page index that the vertical offset passed in is on getPageFromScroll = (vOffset: number) => { @@ -339,26 +340,6 @@ class Viewer extends React.Component { else { this._savedAnnotations.setValue(page, [div]); } - PDFMenu.Instance.StartDrag = this.startDrag; - } - } - - startDrag = (e: PointerEvent) => { - e.preventDefault(); - e.stopPropagation(); - let thisDoc = this.props.parent.Document; - // document that this annotation is linked to - let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); - targetDoc.targetPage = Math.min(...this._savedAnnotations.keys()); - let annotationDoc = this.makeAnnotationDocument(targetDoc, 1, "red"); - let dragData = new DragManager.AnnotationDragData(thisDoc, annotationDoc, targetDoc); - if (this._annotationLayer.current) { - DragManager.StartAnnotationDrag([this._annotationLayer.current], dragData, e.pageX, e.pageY, { - handlers: { - dragComplete: action(emptyFunction), - }, - hideSource: false - }); } } @@ -367,8 +348,8 @@ class Viewer extends React.Component { let res = annotationDocs.map(a => { let type = NumCast(a.type); switch (type) { - case AnnotationTypes.Pin: - return ; + // case AnnotationTypes.Pin: + // return ; case AnnotationTypes.Region: return ; default: @@ -399,7 +380,7 @@ class Viewer extends React.Component { } export enum AnnotationTypes { - Region, Pin + Region } interface IAnnotationProps { @@ -411,132 +392,139 @@ interface IAnnotationProps { document: Doc; } -@observer -class PinAnnotation extends React.Component { - @observable private _backgroundColor: string = "green"; - @observable private _display: string = "initial"; - - private _mainCont: React.RefObject; - - constructor(props: IAnnotationProps) { - super(props); - this._mainCont = React.createRef(); - } - - componentDidMount = () => { - let selected = this.props.document.selected; - if (!BoolCast(selected)) { - runInAction(() => { - this._backgroundColor = "red"; - this._display = "none"; - }); - } - if (selected) { - if (BoolCast(selected)) { - runInAction(() => { - this._backgroundColor = "green"; - this._display = "initial"; - }); - } - else { - runInAction(() => { - this._backgroundColor = "red"; - this._display = "none"; - }); - } - } - else { - runInAction(() => { - this._backgroundColor = "red"; - this._display = "none"; - }); - } - } - - @action - pointerDown = (e: React.PointerEvent) => { - let selected = this.props.document.selected; - if (selected && BoolCast(selected)) { - this._backgroundColor = "red"; - this._display = "none"; - this.props.document.selected = false; - } - else { - this._backgroundColor = "green"; - this._display = "initial"; - this.props.document.selected = true; - } - e.preventDefault(); - e.stopPropagation(); - } - - @action - doubleClick = (e: React.MouseEvent) => { - if (this._mainCont.current) { - let annotations = DocListCast(this.props.parent.props.parent.Document.annotations); - if (annotations && annotations.length) { - let index = annotations.indexOf(this.props.document); - annotations.splice(index, 1); - this.props.parent.props.parent.Document.annotations = new List(annotations); - } - // this._mainCont.current.childNodes.forEach(e => e.remove()); - this._mainCont.current.style.display = "none"; - // if (this._mainCont.current.parentElement) { - // this._mainCont.current.remove(); - // } - } - e.stopPropagation(); - } - - render() { - let targetDoc = Cast(this.props.document.target, Doc); - if (targetDoc instanceof Doc) { - return ( -
-
- 1} - PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} - PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} - focus={emptyFunction} - selectOnLoad={false} - parentActive={this.props.parent.props.parent.props.active} - whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} - bringToFront={emptyFunction} - addDocTab={this.props.parent.props.parent.props.addDocTab} - /> -
-
- ); - } - return null; - } -} +// @observer +// class PinAnnotation extends React.Component { +// @observable private _backgroundColor: string = "green"; +// @observable private _display: string = "initial"; + +// private _mainCont: React.RefObject; + +// constructor(props: IAnnotationProps) { +// super(props); +// this._mainCont = React.createRef(); +// } + +// componentDidMount = () => { +// let selected = this.props.document.selected; +// if (!BoolCast(selected)) { +// runInAction(() => { +// this._backgroundColor = "red"; +// this._display = "none"; +// }); +// } +// if (selected) { +// if (BoolCast(selected)) { +// runInAction(() => { +// this._backgroundColor = "green"; +// this._display = "initial"; +// }); +// } +// else { +// runInAction(() => { +// this._backgroundColor = "red"; +// this._display = "none"; +// }); +// } +// } +// else { +// runInAction(() => { +// this._backgroundColor = "red"; +// this._display = "none"; +// }); +// } +// } + +// @action +// pointerDown = (e: React.PointerEvent) => { +// let selected = this.props.document.selected; +// if (selected && BoolCast(selected)) { +// this._backgroundColor = "red"; +// this._display = "none"; +// this.props.document.selected = false; +// } +// else { +// this._backgroundColor = "green"; +// this._display = "initial"; +// this.props.document.selected = true; +// } +// e.preventDefault(); +// e.stopPropagation(); +// } + +// @action +// doubleClick = (e: React.MouseEvent) => { +// if (this._mainCont.current) { +// let annotations = DocListCast(this.props.parent.props.parent.Document.annotations); +// if (annotations && annotations.length) { +// let index = annotations.indexOf(this.props.document); +// annotations.splice(index, 1); +// this.props.parent.props.parent.Document.annotations = new List(annotations); +// } +// // this._mainCont.current.childNodes.forEach(e => e.remove()); +// this._mainCont.current.style.display = "none"; +// // if (this._mainCont.current.parentElement) { +// // this._mainCont.current.remove(); +// // } +// } +// e.stopPropagation(); +// } + +// render() { +// let targetDoc = Cast(this.props.document.target, Doc); +// if (targetDoc instanceof Doc) { +// return ( +//
+//
+// 1} +// PanelWidth={() => NumCast(this.props.parent.props.parent.Document.nativeWidth)} +// PanelHeight={() => NumCast(this.props.parent.props.parent.Document.nativeHeight)} +// focus={emptyFunction} +// selectOnLoad={false} +// parentActive={this.props.parent.props.parent.props.active} +// whenActiveChanged={this.props.parent.props.parent.props.whenActiveChanged} +// bringToFront={emptyFunction} +// addDocTab={this.props.parent.props.parent.props.addDocTab} +// /> +//
+//
+// ); +// } +// return null; +// } +// } class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; - onPointerDown = (e: React.MouseEvent) => { - let targetDoc = Cast(this.props.document.target, Doc, null); - if (targetDoc) { - DocumentManager.Instance.jumpToDocument(targetDoc); + onPointerDown = (e: React.PointerEvent) => { + if (e.button === 0) { + let targetDoc = Cast(this.props.document.target, Doc, null); + if (targetDoc) { + DocumentManager.Instance.jumpToDocument(targetDoc); + } } + // if (e.button === 2) { + // console.log("right"); + // e.stopPropagation(); + // e.preventDefault(); + // } } render() { return ( -
); } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 2697c9eee..39e737c32 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -15,6 +15,7 @@ import { listSpec } from "../../../new_fields/Schema"; import { menuBar } from "prosemirror-menu"; import { AnnotationTypes, PDFViewer, scale } from "./PDFViewer"; import PDFMenu from "./PDFMenu"; +import { UndoManager } from "../../util/UndoManager"; interface IPageProps { @@ -152,7 +153,6 @@ export default class Page extends React.Component { startDrag = (e: PointerEvent): void => { e.preventDefault(); e.stopPropagation(); - console.log("dragging"); let thisDoc = this.props.parent.Document; // document that this annotation is linked to let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); @@ -163,7 +163,7 @@ export default class Page extends React.Component { if (this._textLayer.current) { DragManager.StartAnnotationDrag([this._textLayer.current], dragData, e.pageX, e.pageY, { handlers: { - dragComplete: action(emptyFunction), + dragComplete: emptyFunction, }, hideSource: false }); @@ -325,68 +325,6 @@ export default class Page extends React.Component { PDFMenu.Instance.StartDrag = this.startDrag; PDFMenu.Instance.Highlight = this.highlight; } - // let x = (e.clientX - boundingRect.left) * (current.offsetWidth / boundingRect.width); - // let y = (e.clientY - boundingRect.top) * (current.offsetHeight / boundingRect.height); - // if (this._marqueeing) { - // this._marqueeing = false; - // if (this._marquee.current) { - // let copy = document.createElement("div"); - // // make a copy of the marquee - // copy.style.left = this._marquee.current.style.left; - // copy.style.top = this._marquee.current.style.top; - // copy.style.width = this._marquee.current.style.width; - // copy.style.height = this._marquee.current.style.height; - - // // apply the appropriate background, opacity, and transform - // let { background, opacity, transform } = this.getCurlyTransform(); - // copy.style.background = background; - // // if curly bracing, add a curly brace - // if (opacity === "1" && this._curly.current) { - // copy.style.opacity = opacity; - // let img = this._curly.current.cloneNode(); - // (img as any).style.opacity = opacity; - // (img as any).style.transform = transform; - // copy.appendChild(img); - // } - // else { - // copy.style.opacity = this._marquee.current.style.opacity; - // } - // copy.className = this._marquee.current.className; - // this.props.createAnnotation(copy, this.props.page); - // this._marquee.current.style.opacity = "0"; - // } - - // this._marqueeHeight = this._marqueeWidth = 0; - // } - // else { - // let sel = window.getSelection(); - // // if selecting over a range of things - // if (sel && sel.type === "Range") { - // let clientRects = sel.getRangeAt(0).getClientRects(); - // if (this._textLayer.current) { - // let boundingRect = this._textLayer.current.getBoundingClientRect(); - // for (let i = 0; i < clientRects.length; i++) { - // let rect = clientRects.item(i); - // if (rect) { - // let annoBox = document.createElement("div"); - // annoBox.className = "pdfViewer-annotationBox"; - // // transforms the positions from screen onto the pdf div - // annoBox.style.top = ((rect.top - boundingRect.top) * (this._textLayer.current.offsetHeight / boundingRect.height)).toString(); - // annoBox.style.left = ((rect.left - boundingRect.left) * (this._textLayer.current.offsetWidth / boundingRect.width)).toString(); - // annoBox.style.width = (rect.width * this._textLayer.current.offsetWidth / boundingRect.width).toString(); - // annoBox.style.height = (rect.height * this._textLayer.current.offsetHeight / boundingRect.height).toString(); - // this.props.createAnnotation(annoBox, this.props.page); - // } - // } - // } - // // clear selection - // if (sel.empty) { // Chrome - // sel.empty(); - // } else if (sel.removeAllRanges) { // Firefox - // sel.removeAllRanges(); - // } - // } - // } document.removeEventListener("pointermove", this.onSelectStart); document.removeEventListener("pointerup", this.onSelectEnd); } @@ -399,7 +337,6 @@ export default class Page extends React.Component { for (let i = 0; i < clientRects.length; i++) { let rect = clientRects.item(i); if (rect && rect.width !== this._textLayer.current.getBoundingClientRect().width && rect.height !== this._textLayer.current.getBoundingClientRect().height) { - console.log(rect); let annoBox = document.createElement("div"); annoBox.className = "pdfViewer-annotationBox"; // transforms the positions from screen onto the pdf div -- cgit v1.2.3-70-g09d2 From 46d57bc21cda4703855b85a4603bd471975d845b Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:25:47 -0400 Subject: deleting annotations --- src/client/views/pdf/PDFMenu.tsx | 40 +++++++++++++++++---- src/client/views/pdf/PDFViewer.tsx | 74 ++++++++++++++++++++++++++++++++------ src/client/views/pdf/Page.tsx | 1 + 3 files changed, 97 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2462a5f94..5dd7b4bcd 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -19,9 +19,11 @@ export default class PDFMenu extends React.Component { StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; - @observable Highlighting: boolean = false; + Delete: () => void = emptyFunction; + + @observable public Highlighting: boolean = false; + @observable public Status: "pdf" | "annotation" | "" = ""; - private _timeout: NodeJS.Timeout | undefined; private _offsetY: number = 0; private _offsetX: number = 0; private _mainCont: React.RefObject; @@ -66,8 +68,8 @@ export default class PDFMenu extends React.Component { } @action - jumpTo = (x: number, y: number) => { - if (!this._pinned) { + jumpTo = (x: number, y: number, forceJump: boolean = false) => { + if (!this._pinned || forceJump) { this._transition = this._transitionDelay = ""; this._opacity = 1; this._left = x; @@ -159,11 +161,35 @@ export default class PDFMenu extends React.Component { } } + deleteClicked = (e: React.PointerEvent) => { + this.Delete(); + } + + handleContextMenu = (e: React.MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + } + render() { + let buttons = this.Status === "pdf" ? [ + , + , + + ] : [ + + ]; + return ( -
- @@ -171,7 +197,7 @@ export default class PDFMenu extends React.Component { + */}
); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index f0e55705d..ea4c0bca2 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -7,7 +7,7 @@ import { Dictionary } from "typescript-collections"; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../new_fields/Types"; import { emptyFunction } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; @@ -138,6 +138,7 @@ class Viewer extends React.Component { makeAnnotationDocument = (sourceDoc: Doc | undefined, s: number, color: string): Doc => { let annoDocs: Doc[] = []; + let mainAnnoDoc = new Doc(); this._savedAnnotations.forEach((key: number, value: HTMLDivElement[]) => { for (let anno of value) { let annoDoc = new Doc(); @@ -147,6 +148,7 @@ class Viewer extends React.Component { if (anno.style.width) annoDoc.width = parseInt(anno.style.width) / scale; annoDoc.page = key; annoDoc.target = sourceDoc; + annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; annoDocs.push(annoDoc); @@ -154,13 +156,12 @@ class Viewer extends React.Component { } }); - let annoDoc = new Doc(); - annoDoc.annotations = new List(annoDocs); + mainAnnoDoc.annotations = new List(annoDocs); if (sourceDoc) { - DocUtils.MakeLink(sourceDoc, annoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); + DocUtils.MakeLink(sourceDoc, mainAnnoDoc, undefined, `Annotation from ${StrCast(this.props.parent.Document.title)}`, "", StrCast(this.props.parent.Document.title)); } this._savedAnnotations.clear(); - return annoDoc; + return mainAnnoDoc; } drop = async (e: Event, de: DragManager.DropEvent) => { @@ -508,6 +509,57 @@ interface IAnnotationProps { class RegionAnnotation extends React.Component { @observable private _backgroundColor: string = "red"; + private _reactionDisposer?: IReactionDisposer; + private _mainCont: React.RefObject; + + constructor(props: IAnnotationProps) { + super(props); + + this._mainCont = React.createRef(); + } + + componentDidMount() { + this._reactionDisposer = reaction( + () => BoolCast(this.props.document.delete), + () => { + if (BoolCast(this.props.document.delete)) { + if (this._mainCont.current) { + this._mainCont.current.style.display = "none"; + } + } + }, + { fireImmediately: true } + ); + } + + componentWillUnmount() { + this._reactionDisposer && this._reactionDisposer(); + } + + deleteAnnotation = () => { + let annotation = DocListCast(this.props.parent.props.parent.Document.annotations); + let group = FieldValue(Cast(this.props.document.group, Doc)); + if (group && annotation.indexOf(group) !== -1) { + let newAnnotations = annotation.filter(a => a !== FieldValue(Cast(this.props.document.group, Doc))); + this.props.parent.props.parent.Document.annotations = new List(newAnnotations); + } + + if (group) { + let groupAnnotations = DocListCast(group.annotations); + groupAnnotations.forEach(anno => anno.delete = true); + } + } + + + // annotateThis = (e: PointerEvent) => { + // e.preventDefault(); + // e.stopPropagation(); + // // document that this annotation is linked to + // let targetDoc = Docs.TextDocument({ width: 200, height: 200, title: "New Annotation" }); + // let group = FieldValue(Cast(this.props.document.group, Doc)); + // } + + @action onPointerDown = (e: React.PointerEvent) => { if (e.button === 0) { let targetDoc = Cast(this.props.document.target, Doc, null); @@ -515,16 +567,16 @@ class RegionAnnotation extends React.Component { DocumentManager.Instance.jumpToDocument(targetDoc); } } - // if (e.button === 2) { - // console.log("right"); - // e.stopPropagation(); - // e.preventDefault(); - // } + if (e.button === 2) { + PDFMenu.Instance.Status = "annotation"; + PDFMenu.Instance.Delete = this.deleteAnnotation; + PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); + } } render() { return ( -
); } diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 39e737c32..734dff7fc 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -191,6 +191,7 @@ export default class Page extends React.Component { else if (e.button === 0) { PDFMenu.Instance.StartDrag = this.startDrag; PDFMenu.Instance.Highlight = this.highlight; + PDFMenu.Instance.Status = "pdf"; PDFMenu.Instance.fadeOut(true); let target: any = e.target; if (target && target.parentElement === this._textLayer.current) { -- cgit v1.2.3-70-g09d2 From b960a876d6a31b3eaebb0ac6eca6f191a0d4c900 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:38:43 -0400 Subject: oop --- src/client/views/pdf/PDFMenu.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 5dd7b4bcd..39b15fb11 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -15,7 +15,8 @@ export default class PDFMenu extends React.Component { @observable private _opacity: number = 1; @observable private _transition: string = "opacity 0.5s"; @observable private _transitionDelay: string = ""; - @observable private _pinned: boolean = false; + + @observable public Pinned: boolean = false; StartDrag: (e: PointerEvent) => void = emptyFunction; Highlight: (d: Doc | undefined, color: string | undefined) => void = emptyFunction; @@ -69,7 +70,7 @@ export default class PDFMenu extends React.Component { @action jumpTo = (x: number, y: number, forceJump: boolean = false) => { - if (!this._pinned || forceJump) { + if (!this.Pinned || forceJump) { this._transition = this._transitionDelay = ""; this._opacity = 1; this._left = x; @@ -79,7 +80,7 @@ export default class PDFMenu extends React.Component { @action fadeOut = (forceOut: boolean) => { - if (!this._pinned) { + if (!this.Pinned) { if (this._opacity === 0.2) { this._transition = "opacity 0.1s"; this._transitionDelay = ""; @@ -98,7 +99,7 @@ export default class PDFMenu extends React.Component { @action pointerLeave = (e: React.PointerEvent) => { - if (!this._pinned) { + if (!this.Pinned) { this._transition = "opacity 0.5s"; this._transitionDelay = "1s"; this._opacity = 0.2; @@ -115,8 +116,8 @@ export default class PDFMenu extends React.Component { @action togglePin = (e: React.MouseEvent) => { - this._pinned = !this._pinned; - if (!this._pinned) { + this.Pinned = !this.Pinned; + if (!this.Pinned) { this.Highlighting = false; } } @@ -152,7 +153,7 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { - if (!this._pinned) { + if (!this.Pinned) { this.Highlight(undefined, "#f4f442"); } else { @@ -178,8 +179,8 @@ export default class PDFMenu extends React.Component { , , ] : [ @@ -198,7 +199,7 @@ export default class PDFMenu extends React.Component { style={this._pinned ? { backgroundColor: "#121212" } : {}}> */} -
+
); } -- cgit v1.2.3-70-g09d2 From b9849810231e540a5898a56012abd32c197b23b5 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:39:15 -0400 Subject: anna --- src/client/views/pdf/PDFViewer.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ea4c0bca2..f3aa2f5a0 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -570,6 +570,7 @@ class RegionAnnotation extends React.Component { if (e.button === 2) { PDFMenu.Instance.Status = "annotation"; PDFMenu.Instance.Delete = this.deleteAnnotation; + PDFMenu.Instance.Pinned = false; PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); } } -- cgit v1.2.3-70-g09d2 From 9ab47393a2ce3d174ad3238422c2c310764be9af Mon Sep 17 00:00:00 2001 From: yipstanley Date: Wed, 19 Jun 2019 14:40:28 -0400 Subject: interaction improvements with delete button --- src/client/views/pdf/PDFViewer.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index f3aa2f5a0..7000352e7 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -548,6 +548,8 @@ class RegionAnnotation extends React.Component { let groupAnnotations = DocListCast(group.annotations); groupAnnotations.forEach(anno => anno.delete = true); } + + PDFMenu.Instance.fadeOut(true); } -- cgit v1.2.3-70-g09d2 From 35e73f369a2145d8a042e0011a43e71763d57998 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 19 Jun 2019 15:02:48 -0400 Subject: added better search to context menu --- src/client/views/ContextMenu.scss | 23 ++++++++++ src/client/views/ContextMenu.tsx | 83 +++++++++++++++++++++++++----------- src/client/views/ContextMenuItem.tsx | 7 +-- src/client/views/MainView.tsx | 2 +- 4 files changed, 84 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index e363c5158..a1a2b06f1 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -53,6 +53,29 @@ font-size: 20px; } +.contextMenu-group { + // width: 11vw; //10vw + height: 30px; //2vh + background: rgb(200, 200, 200); + display: flex; //comment out to allow search icon to be inline with search text + justify-content: left; + align-items: center; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; + border-width: .11px; + border-style: none; + border-color: $intermediate-color; // rgb(187, 186, 186); + border-bottom-style: solid; + // padding: 10px 0px 10px 0px; + white-space: nowrap; + font-size: 20px; +} + .contextMenu-item:hover { transition: all 0.1s; background: $lighter-alt-accent; diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index eb1937683..1133f70a1 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,6 +1,6 @@ import React = require("react"); import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; -import { observable, action } from "mobx"; +import { observable, action, computed } from "mobx"; import { observer } from "mobx-react"; import "./ContextMenu.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; @@ -17,11 +17,12 @@ export class ContextMenu extends React.Component { @observable private _items: Array = [{ description: "test", event: (e: React.MouseEvent) => e.preventDefault(), icon: "smile" }]; @observable private _pageX: number = 0; @observable private _pageY: number = 0; - @observable private _display: string = "none"; + @observable private _display: boolean = false; @observable private _searchString: string = ""; // afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be @observable private _yRelativeToTop: boolean = true; + private _searchRef = React.createRef(); private ref: React.RefObject; @@ -36,7 +37,6 @@ export class ContextMenu extends React.Component { @action clearItems() { this._items = []; - this._display = "none"; } @action @@ -62,34 +62,70 @@ export class ContextMenu extends React.Component { this._searchString = ""; - this._display = "flex"; - } - - intersects = (x: number, y: number): boolean => { - if (this.ref.current && this._display !== "none") { - let menuSize = { width: this.ref.current.getBoundingClientRect().width, height: this.ref.current.getBoundingClientRect().height }; + this._display = true; - let upperLeft = { x: this._pageX, y: this._yRelativeToTop ? this._pageY : window.innerHeight - (this._pageY + menuSize.height) }; - let bottomRight = { x: this._pageX + menuSize.width, y: this._yRelativeToTop ? this._pageY + menuSize.height : window.innerHeight - this._pageY }; - - if (x >= upperLeft.x && x <= bottomRight.x) { - if (y >= upperLeft.y && y <= bottomRight.y) { - return true; - } - } + if (this._searchRef.current) { + this._searchRef.current.focus(); } - return false; } @action closeMenu = () => { this.clearItems(); + this._display = false; } - render() { - let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY, display: this._display } : - { left: this._pageX, bottom: this._pageY, display: this._display }; + @computed get filteredItems() { + const searchString = this._searchString.toLowerCase().split(" "); + const matches = (descriptions: string[]): boolean => { + return searchString.every(s => descriptions.some(desc => desc.includes(s))); + }; + const createGroupHeader = (contents: any) => { + return ( +
+
{contents}
+
+ ); + }; + const createItem = (item: ContextMenuProps) => ; + const flattenItems = (items: ContextMenuProps[], groupFunc: (contents: any) => JSX.Element, getPath: () => string[]) => { + let eles: JSX.Element[] = []; + + for (const item of items) { + const description = item.description.toLowerCase(); + const path = [...getPath(), description]; + if ("subitems" in item) { + const children = flattenItems(item.subitems, contents => groupFunc(<>{item.description} -> {contents}), () => path); + if (children.length || matches(path)) { + eles.push(groupFunc(item.description)); + eles = eles.concat(children); + } + } else { + if (!matches(path)) { + continue; + } + eles.push(createItem(item)); + } + } + return eles; + }; + return flattenItems(this._items, createGroupHeader, () => []); + } + + @computed get menuItems() { + if (!this._searchString) { + return this._items.map(item => ); + } + return this.filteredItems; + } + + render() { + if (!this._display) { + return null; + } + let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY } : + { left: this._pageX, bottom: this._pageY }; return (
@@ -97,10 +133,9 @@ export class ContextMenu extends React.Component { - + - {this._items.filter(prop => prop.description.toLowerCase().indexOf(this._searchString.toLowerCase()) !== -1). - map(prop => )} + {this.menuItems}
); } diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index dc0751049..88ebd95bc 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -21,9 +21,6 @@ export interface SubmenuProps { closeMenu?: () => void; } -export interface ContextMenuItemProps { - type: ContextMenuProps | SubmenuProps; -} export type ContextMenuProps = OriginalMenuProps | SubmenuProps; @observer @@ -67,7 +64,6 @@ export class ContextMenuItem extends React.Component { return; } this.currentTimeout = setTimeout(action(() => this.overItem = false), ContextMenuItem.timeout); - } render() { @@ -84,8 +80,7 @@ export class ContextMenuItem extends React.Component {
); - } - else { + } else if ("subitems" in this.props) { let submenu = !this.overItem ? (null) :
{this._items.map(prop => )} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7f979cd3b..e3d4ff8b5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -118,7 +118,7 @@ export class MainView extends React.Component { const targets = document.elementsFromPoint(e.x, e.y); if (targets && targets.length && targets[0].className.toString().indexOf("contextMenu") === -1) { - ContextMenu.Instance.clearItems(); + ContextMenu.Instance.closeMenu(); } }), true); } -- cgit v1.2.3-70-g09d2 From 358437eeafe42e029ffe27702bde15a3fad54a3b Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 19 Jun 2019 16:17:18 -0400 Subject: working version of embedded tree view docs. --- .../views/collections/CollectionStackingView.tsx | 2 + .../views/collections/CollectionTreeView.scss | 15 ++- .../views/collections/CollectionTreeView.tsx | 132 +++++++++++++++------ src/client/views/nodes/DocumentView.tsx | 5 +- src/client/views/nodes/FormattedTextBox.tsx | 1 + 5 files changed, 109 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index f80ba0a4b..368e94a8c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -10,6 +10,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; +import { auto } from "async"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -112,6 +113,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { style={{ width: width(), height: height(), + overflow: "auto", transformOrigin: "top left", gridRowEnd: `span ${rowSpan}`, gridColumnEnd: `span 1`, diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 2d5092980..ec78fdb80 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -12,7 +12,7 @@ padding-right: 0px; background: $light-color-secondary; font-size: 13px; - overflow: scroll; + overflow: auto; ul { list-style: none; @@ -50,10 +50,12 @@ font-size: 24px; } - .collectionTreeView-keyHeader { - font-style: italic; - font-size: 8pt; - } +} +.collectionTreeView-keyHeader { + font-style: italic; + font-size: 8pt; + margin-left: 3px; + display:none; } .docContainer { @@ -74,6 +76,9 @@ } .treeViewItem-header:hover { + .collectionTreeView-keyHeader { + display:inherit; + } .treeViewItem-openRight { display: inline-block; height:13px; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d43402e7d..1eab541b3 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,9 +1,9 @@ import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace } from "mobx"; +import { action, observable, trace, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from '../../../new_fields/Doc'; +import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { Document, listSpec } from '../../../new_fields/Schema'; import { BoolCast, Cast, NumCast, StrCast, PromiseValue } from '../../../new_fields/Types'; @@ -16,14 +16,18 @@ import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; import { CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from './CollectionDockingView'; -import { CollectionSubView } from "./CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { Transform } from '../../util/Transform'; import { SelectionManager } from '../../util/SelectionManager'; -import { emptyFunction } from '../../../Utils'; +import { emptyFunction, returnFalse, Utils, returnOne, returnZero } from '../../../Utils'; import { List } from '../../../new_fields/List'; import { Templates } from '../Templates'; +import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { number } from 'prop-types'; +import ReactTable from 'react-table'; +import { MainOverlayTextBox } from '../MainOverlayTextBox'; export interface TreeViewProps { @@ -32,9 +36,12 @@ export interface TreeViewProps { moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; addDocTab: (doc: Doc, where: string) => void; + panelWidth: () => number; + panelHeight: () => number; addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; ScreenToLocalTransform: () => Transform; + outerXf: () => number[]; treeViewId: string; parentKey: string; active: () => boolean; @@ -58,11 +65,13 @@ library.add(faCaretRight); class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); private treedropDisposer?: DragManager.DragDropDisposer; + private _mainEle?: HTMLDivElement; protected createTreeDropTarget = (ele: HTMLDivElement) => { this.treedropDisposer && this.treedropDisposer(); if (ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); } + this._mainEle = ele; } @observable _isOver: boolean = false; @@ -76,7 +85,7 @@ class TreeView extends React.Component { onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); - if (e.buttons === 1) { + if (e.buttons === 1 && SelectionManager.GetIsDragging()) { this._header!.current!.className = "treeViewItem-header"; document.addEventListener("pointermove", this.onDragMove, true); } @@ -166,6 +175,28 @@ class TreeView extends React.Component { let reference = React.createRef(); let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + let keyList: string[] = []; + let keys = Array.from(Object.keys(this.props.document)); + if (this.props.document.proto instanceof Doc) { + keys.push(...Array.from(Object.keys(this.props.document.proto))); + while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); + } + if (keys.indexOf("data") !== -1) { + keys.splice(keys.indexOf("data"), 1); + keys.splice(0, 0, "data"); + } + keys.map(key => { + let docList = Cast(this.props.document[key], listSpec(Doc)); + let doc = Cast(this.props.document[key], Doc); + if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { + keyList.push(key); + } + }); + let headerElements = this._bulletType === BulletType.List ? (null) : [this._chosenKey].map(key => + { this._chosenKey = key; this.props.document.embed = !BoolCast(this.props.document.embed, false) })} + style={{ background: key === this._chosenKey ? "lightgray" : undefined }}> + {key} + ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
@@ -182,6 +213,7 @@ class TreeView extends React.Component { {this.editableView("title")} {/* {
} */}
+ {headerElements} {openRight} ; } @@ -244,34 +276,20 @@ class TreeView extends React.Component { return true; } + docTransform = () => { + let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); + let outerXf = this.props.outerXf(); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf[0] - translateX, outerXf[1] - translateY); + let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); + return finalXf; + } @observable _chosenKey: string = "data"; _bulletType: BulletType = BulletType.List; + + _dref = React.createRef(); render() { let bulletType = BulletType.List; let contentElement: (JSX.Element | null)[] = []; - let keys = Array.from(Object.keys(this.props.document)); - if (this.props.document.proto instanceof Doc) { - keys.push(...Array.from(Object.keys(this.props.document.proto))); - while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); - } - if (keys.indexOf("data") !== -1) { - keys.splice(keys.indexOf("data"), 1); - keys.splice(0, 0, "data"); - } - let keyList: string[] = []; - keys.map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { - keyList.push(key); - } - }); - let headerElements =
{keyList.map(key => - this._chosenKey = key)} - style={{ display: "inline", marginRight: "3px", marginTop: "7px", background: key === this._chosenKey ? "lightgray" : undefined }}> - {key} - )} -
; [this._chosenKey].map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, key); @@ -280,14 +298,31 @@ class TreeView extends React.Component { if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { if (!this._collapsed) { bulletType = BulletType.Collapsible; - contentElement.push(
    - {headerElements} -
    - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, - this.indent, - this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active)} -
    -
); + if (this.props.document.embed) { + contentElement.push( +
+
); + } else + contentElement.push(
    +
    + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.panelHeight)} +
    +
); } else { bulletType = BulletType.Collapsed; } @@ -315,11 +350,13 @@ class TreeView extends React.Component { add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, remove: ((doc: Doc) => void), move: DragManager.MoveFunction, - indent: () => void, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void, screenToLocalXf: () => Transform, - active: () => boolean + outerXf: () => number[], + active: () => boolean, + panelWidth: () => number, + panelHeight: () => number ) { let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); return docList.map((child, i) => { @@ -338,10 +375,13 @@ class TreeView extends React.Component { indentDocument={indent} deleteDoc={remove} addDocument={add} + panelWidth={panelWidth} + panelHeight={panelHeight} moveDocument={move} dropAction={dropAction} addDocTab={addDocTab} ScreenToLocalTransform={screenToLocalXf} + outerXf={outerXf} parentKey={key} active={active} />; }); @@ -351,6 +391,7 @@ class TreeView extends React.Component { @observer export class CollectionTreeView extends CollectionSubView(Document) { private treedropDisposer?: DragManager.DragDropDisposer; + private _mainEle?: HTMLDivElement; protected createTreeDropTarget = (ele: HTMLDivElement) => { if (this.treedropDisposer) { this.treedropDisposer(); @@ -358,6 +399,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { if (ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } + this._mainEle = ele; } @action @@ -373,6 +415,10 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } + outerXf = () => { + let outerXf = Utils.GetScreenTransform(this._mainEle!); + return [outerXf.translateX, outerXf.translateY]; + } onTreeDrop = (e: React.DragEvent) => { this.onDrop(e, {}); } @@ -384,7 +430,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, - moveDoc, emptyFunction, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.active); + moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, () => 25); return (
StrCast(this.props.Document.title)} + OnFillDown={(value: string) => { + let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; + target.title = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); + TreeView.loadId = doc[Id]; + doc.templates = new List([Templates.Title.Layout]); + this.props.addDocument(doc); + }} SetValue={(value: string) => { let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; target.title = value; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4992669df..f7836d947 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,6 +72,7 @@ export interface DocumentViewProps { ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; + useActualDimensions?: boolean; PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc) => void; @@ -538,8 +539,8 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + var nativeWidth = this.props.useActualDimensions ? NumCast(this.props.Document.width) : this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + var nativeHeight = this.props.useActualDimensions ? NumCast(this.props.Document.height) : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
Date: Wed, 19 Jun 2019 16:21:03 -0400 Subject: started adding selection to context menu --- src/client/views/ContextMenu.scss | 4 +++ src/client/views/ContextMenu.tsx | 58 +++++++++++++++++++++++------------- src/client/views/ContextMenuItem.tsx | 6 ++-- 3 files changed, 45 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index a1a2b06f1..254163b53 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -53,6 +53,10 @@ font-size: 20px; } +.contextMenu-itemSelected { + background: rgb(136, 136, 136) +} + .contextMenu-group { // width: 11vw; //10vw height: 30px; //2vh diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 1133f70a1..33de57cfa 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -1,5 +1,5 @@ import React = require("react"); -import { ContextMenuItem, ContextMenuProps } from "./ContextMenuItem"; +import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem"; import { observable, action, computed } from "mobx"; import { observer } from "mobx-react"; import "./ContextMenu.scss"; @@ -21,6 +21,7 @@ export class ContextMenu extends React.Component { @observable private _searchString: string = ""; // afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be @observable private _yRelativeToTop: boolean = true; + @observable selectedIndex = -1; private _searchRef = React.createRef(); @@ -75,49 +76,63 @@ export class ContextMenu extends React.Component { this._display = false; } - @computed get filteredItems() { + @computed get filteredItems(): (OriginalMenuProps | string[])[] { const searchString = this._searchString.toLowerCase().split(" "); const matches = (descriptions: string[]): boolean => { - return searchString.every(s => descriptions.some(desc => desc.includes(s))); + return searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s))); }; - const createGroupHeader = (contents: any) => { - return ( -
-
{contents}
-
- ); - }; - const createItem = (item: ContextMenuProps) => ; - const flattenItems = (items: ContextMenuProps[], groupFunc: (contents: any) => JSX.Element, getPath: () => string[]) => { - let eles: JSX.Element[] = []; + const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: any) => string[]) => { + let eles: (OriginalMenuProps | string[])[] = []; + const leaves: OriginalMenuProps[] = []; for (const item of items) { - const description = item.description.toLowerCase(); - const path = [...getPath(), description]; + const description = item.description; + const path = groupFunc(description); if ("subitems" in item) { - const children = flattenItems(item.subitems, contents => groupFunc(<>{item.description} -> {contents}), () => path); + const children = flattenItems(item.subitems, name => [...groupFunc(description), name]); if (children.length || matches(path)) { - eles.push(groupFunc(item.description)); + eles.push(path); eles = eles.concat(children); } } else { if (!matches(path)) { continue; } - eles.push(createItem(item)); + leaves.push(item); } } + eles = [...leaves, ...eles]; + return eles; }; - return flattenItems(this._items, createGroupHeader, () => []); + return flattenItems(this._items, name => [name]); + } + + @computed get filteredViews() { + const createGroupHeader = (contents: any) => { + return ( +
+
{contents}
+
+ ); + }; + const createItem = (item: ContextMenuProps, selected: boolean) => ; + let itemIndex = 0; + return this.filteredItems.map(value => { + if (Array.isArray(value)) { + return createGroupHeader(value.join(" -> ")); + } else { + return createItem(value, ++itemIndex === this.selectedIndex); + } + }); } @computed get menuItems() { if (!this._searchString) { return this._items.map(item => ); } - return this.filteredItems; + return this.filteredViews; } render() { @@ -143,5 +158,8 @@ export class ContextMenu extends React.Component { @action onChange = (e: React.ChangeEvent) => { this._searchString = e.target.value; + if (this._searchString && this.selectedIndex === -1) { + this.selectedIndex = 0; + } } } \ No newline at end of file diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index 88ebd95bc..ebcac7428 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -24,7 +24,7 @@ export interface SubmenuProps { export type ContextMenuProps = OriginalMenuProps | SubmenuProps; @observer -export class ContextMenuItem extends React.Component { +export class ContextMenuItem extends React.Component { @observable private _items: Array = []; @observable private overItem = false; @@ -69,7 +69,7 @@ export class ContextMenuItem extends React.Component { render() { if ("event" in this.props) { return ( -
+
{this.props.icon ? ( @@ -86,7 +86,7 @@ export class ContextMenuItem extends React.Component { {this._items.map(prop => )}
; return ( -
+
{this.props.icon ? ( -- cgit v1.2.3-70-g09d2 From 05e50f27a15e8a02ffb27606c51026d1b85bc677 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Wed, 19 Jun 2019 17:36:52 -0400 Subject: Added basic keyboard controls to context menu --- src/client/views/ContextMenu.tsx | 40 ++++++++++++++++++++++++++++---- src/client/views/ContextMenuItem.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 41 +++++++++++++++++---------------- src/client/views/nodes/IconBox.tsx | 6 ++--- 4 files changed, 61 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 33de57cfa..59a0de2a0 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -14,7 +14,7 @@ library.add(faCircle); export class ContextMenu extends React.Component { static Instance: ContextMenu; - @observable private _items: Array = [{ description: "test", event: (e: React.MouseEvent) => e.preventDefault(), icon: "smile" }]; + @observable private _items: Array = []; @observable private _pageX: number = 0; @observable private _pageY: number = 0; @observable private _display: boolean = false; @@ -109,6 +109,10 @@ export class ContextMenu extends React.Component { return flattenItems(this._items, name => [name]); } + @computed get flatItems(): OriginalMenuProps[] { + return this.filteredItems.filter(item => !Array.isArray(item)) as OriginalMenuProps[]; + } + @computed get filteredViews() { const createGroupHeader = (contents: any) => { return ( @@ -123,7 +127,7 @@ export class ContextMenu extends React.Component { if (Array.isArray(value)) { return createGroupHeader(value.join(" -> ")); } else { - return createItem(value, ++itemIndex === this.selectedIndex); + return createItem(value, itemIndex++ === this.selectedIndex); } }); } @@ -148,18 +152,44 @@ export class ContextMenu extends React.Component { - + {this.menuItems}
); } + @action + onKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "ArrowDown") { + if (this.selectedIndex < this.flatItems.length - 1) { + this.selectedIndex++; + } + e.preventDefault(); + } else if (e.key === "ArrowUp") { + if (this.selectedIndex > 0) { + this.selectedIndex--; + } + e.preventDefault(); + } else if (e.key === "Enter") { + const item = this.flatItems[this.selectedIndex]; + item.event(); + this.closeMenu(); + } + } + @action onChange = (e: React.ChangeEvent) => { this._searchString = e.target.value; - if (this._searchString && this.selectedIndex === -1) { - this.selectedIndex = 0; + if (!this._searchString) { + this.selectedIndex = -1; + } + else { + if (this.selectedIndex === -1) { + this.selectedIndex = 0; + } else { + this.selectedIndex = Math.min(this.flatItems.length - 1, this.selectedIndex); + } } } } \ No newline at end of file diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx index ebcac7428..9bbb97d7e 100644 --- a/src/client/views/ContextMenuItem.tsx +++ b/src/client/views/ContextMenuItem.tsx @@ -9,7 +9,7 @@ library.add(faAngleRight); export interface OriginalMenuProps { description: string; - event: (e: React.MouseEvent) => void; + event: () => void; icon?: IconProp; //maybe should be optional (icon?) closeMenu?: () => void; } @@ -37,7 +37,7 @@ export class ContextMenuItem extends React.Component) => { if ("event" in this.props) { - this.props.event(e); + this.props.event(); this.props.closeMenu && this.props.closeMenu(); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f7836d947..2062fe1b5 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAlignCenter, faCaretSquareRight, faCompressArrowsAlt, faUnlock, faLock, faExpandArrowsAlt, faLayerGroup, faSquare, faTrash, faConciergeBell, faFolder, faShare, faMapPin, faLink, faFingerprint, faCrosshairs, faDesktop } from '@fortawesome/free-solid-svg-icons'; +import * as fa from '@fortawesome/free-solid-svg-icons'; import { action, computed, IReactionDisposer, reaction, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; @@ -34,23 +34,24 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { RouteStore } from '../../../server/RouteStore'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? -library.add(faTrash); -library.add(faShare); -library.add(faExpandArrowsAlt); -library.add(faCompressArrowsAlt); -library.add(faLayerGroup); -library.add(faAlignCenter); -library.add(faCaretSquareRight); -library.add(faSquare); -library.add(faConciergeBell); -library.add(faFolder); -library.add(faMapPin); -library.add(faLink); -library.add(faFingerprint); -library.add(faCrosshairs); -library.add(faDesktop); -library.add(faUnlock); -library.add(faLock); +library.add(fa.faTrash); +library.add(fa.faShare); +library.add(fa.faExpandArrowsAlt); +library.add(fa.faCompressArrowsAlt); +library.add(fa.faLayerGroup); +library.add(fa.faExternalLinkAlt); +library.add(fa.faAlignCenter); +library.add(fa.faCaretSquareRight); +library.add(fa.faSquare); +library.add(fa.faConciergeBell); +library.add(fa.faFolder); +library.add(fa.faMapPin); +library.add(fa.faLink); +library.add(fa.faFingerprint); +library.add(fa.faCrosshairs); +library.add(fa.faDesktop); +library.add(fa.faUnlock); +library.add(fa.faLock); const linkSchema = createSchema({ title: "string", @@ -447,7 +448,7 @@ export class DocumentView extends DocComponent(Docu this.templates = this.templates; } - freezeNativeDimensions = (e: React.MouseEvent): void => { + freezeNativeDimensions = (): void => { let proto = Doc.GetProto(this.props.Document); if (proto.ignoreAspect === undefined && !proto.nativeWidth) { proto.nativeWidth = this.props.PanelWidth(); @@ -476,7 +477,7 @@ export class DocumentView extends DocComponent(Docu subitems.push({ description: "Open Right", event: () => this.props.addDocTab && this.props.addDocTab(this.props.Document, "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Right Alias", event: () => this.props.addDocTab && this.props.addDocTab(Doc.MakeAlias(this.props.Document), "onRight"), icon: "caret-square-right" }); subitems.push({ description: "Open Fields", event: this.fieldsClicked, icon: "layer-group" }); - cm.addItem({ description: "Open...", subitems: subitems }); + cm.addItem({ description: "Open...", subitems: subitems, icon: "external-link-alt" }); cm.addItem({ description: BoolCast(this.props.Document.ignoreAspect, false) || !this.props.Document.nativeWidth || !this.props.Document.nativeHeight ? "Freeze" : "Unfreeze", event: this.freezeNativeDimensions, icon: "edit" }); cm.addItem({ description: "Pin to Pres", event: () => PresentationView.Instance.PinDoc(this.props.Document), icon: "map-pin" }); cm.addItem({ description: BoolCast(this.props.Document.lockedPosition) ? "Unlock Pos" : "Lock Pos", event: () => this.props.Document.lockedPosition = BoolCast(this.props.Document.lockedPosition) ? undefined : true, icon: BoolCast(this.props.Document.lockedPosition) ? "unlock" : "lock" }); diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 00021bc78..d6ab2a34a 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -37,14 +37,14 @@ export class IconBox extends React.Component { return ; } - setLabelField = (e: React.MouseEvent): void => { + setLabelField = (): void => { this.props.Document.hideLabel = !BoolCast(this.props.Document.hideLabel); } - setUseOwnTitleField = (e: React.MouseEvent): void => { + setUseOwnTitleField = (): void => { this.props.Document.useOwnTitle = !BoolCast(this.props.Document.useTargetTitle); } - specificContextMenu = (e: React.MouseEvent): void => { + specificContextMenu = (): void => { ContextMenu.Instance.addItem({ description: BoolCast(this.props.Document.hideLabel) ? "Show label with icon" : "Remove label from icon", event: this.setLabelField -- cgit v1.2.3-70-g09d2 From 96c26c57527d443784bde9752551bfa10b3ce4d2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 19 Jun 2019 18:34:45 -0400 Subject: removed marquee summarizing icon --- .../collections/collectionFreeForm/MarqueeView.tsx | 43 ++++++++-------------- 1 file changed, 15 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 05dc6f534..3f7efcb66 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -302,35 +302,22 @@ export class MarqueeView extends React.Component this.props.addLiveTextDocument(container); // }); } else if (e.key === "S") { - await htmlToImage.toPng(this._mainCont.current!, { width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, quality: 0.2 }).then((dataUrl) => { - selected.map(d => { - this.props.removeDocument(d); - d.x = NumCast(d.x) - bounds.left - bounds.width / 2; - d.y = NumCast(d.y) - bounds.top - bounds.height / 2; - d.page = -1; - return d; - }); - let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); - SearchBox.convertDataUri(dataUrl, "icon" + summary[Id] + "_image").then((returnedFilename) => { - if (returnedFilename) { - let url = DocServer.prepend(returnedFilename); - let imageSummary = Docs.ImageDocument(url, { - x: bounds.left, y: bounds.top + 100 / zoomBasis, - width: 150, height: bounds.height / bounds.width * 150, title: "-summary image-" - }); - summary.imageSummary = imageSummary; - this.props.addDocument(imageSummary, false); - } - }); - newCollection.proto!.summaryDoc = summary; - selected = [newCollection]; - newCollection.x = bounds.left + bounds.width; - //this.props.addDocument(newCollection, false); - summary.proto!.summarizedDocs = new List(selected); - summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" - - this.props.addLiveTextDocument(summary); + selected.map(d => { + this.props.removeDocument(d); + d.x = NumCast(d.x) - bounds.left - bounds.width / 2; + d.y = NumCast(d.y) - bounds.top - bounds.height / 2; + d.page = -1; + return d; }); + let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "#e2ad32" /* yellow */, title: "-summary-" }); + newCollection.proto!.summaryDoc = summary; + selected = [newCollection]; + newCollection.x = bounds.left + bounds.width; + //this.props.addDocument(newCollection, false); + summary.proto!.summarizedDocs = new List(selected); + summary.proto!.maximizeLocation = "inTab"; // or "inPlace", or "onRight" + + this.props.addLiveTextDocument(summary); } else { this.props.addDocument(newCollection, false); -- cgit v1.2.3-70-g09d2 From 118ecb14ce519bcbade12b3d52e11b22fcc371b3 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 19 Jun 2019 22:40:54 -0400 Subject: cleaned up and enhanced tree view --- src/client/documents/Documents.ts | 1 - .../views/collections/CollectionSchemaView.tsx | 2 +- .../views/collections/CollectionTreeView.scss | 5 +- .../views/collections/CollectionTreeView.tsx | 347 +++++++++------------ src/client/views/nodes/DocumentView.tsx | 5 +- 5 files changed, 158 insertions(+), 202 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fcd1010c6..de6c5bc6a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -18,7 +18,6 @@ import { action } from "mobx"; import { ColumnAttributeModel } from "../northstar/core/attribute/AttributeModel"; import { AttributeTransformationModel } from "../northstar/core/attribute/AttributeTransformationModel"; import { AggregateFunction } from "../northstar/model/idea/idea"; -import { Template } from "../views/Templates"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { IconBox } from "../views/nodes/IconBox"; import { Field, Doc, Opt } from "../../new_fields/Doc"; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index faea8d44d..9cc8961e3 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -385,7 +385,7 @@ interface CollectionSchemaPreviewProps { Document?: Doc; width: () => number; height: () => number; - CollectionView: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; moveDocument: (document: Doc, target: Doc, addDoc: ((doc: Doc) => boolean)) => boolean; diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index ec78fdb80..a85604e58 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -4,10 +4,10 @@ border-width: $COLLECTION_BORDER_WIDTH; border-color: transparent; border-style: solid; - border-radius: $border-radius; + border-radius: inherit; box-sizing: border-box; height: 100%; - padding: 20px; + padding-top: 20px; padding-left: 10px; padding-right: 0px; background: $light-color-secondary; @@ -56,6 +56,7 @@ font-size: 8pt; margin-left: 3px; display:none; + background: lightgray; } .docContainer { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 1eab541b3..856430036 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,38 +1,35 @@ -import { IconProp, library } from '@fortawesome/fontawesome-svg-core'; -import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable, trace, computed } from "mobx"; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, WidthSym, HeightSym } from '../../../new_fields/Doc'; +import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; +import { List } from '../../../new_fields/List'; import { Document, listSpec } from '../../../new_fields/Schema'; -import { BoolCast, Cast, NumCast, StrCast, PromiseValue } from '../../../new_fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types'; +import { emptyFunction, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; +import { SelectionManager } from '../../util/SelectionManager'; +import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; +import { Templates } from '../Templates'; import { CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from './CollectionDockingView'; -import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; +import { CollectionSchemaPreview } from './CollectionSchemaView'; +import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { Transform } from '../../util/Transform'; -import { SelectionManager } from '../../util/SelectionManager'; -import { emptyFunction, returnFalse, Utils, returnOne, returnZero } from '../../../Utils'; -import { List } from '../../../new_fields/List'; -import { Templates } from '../Templates'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; -import { number } from 'prop-types'; -import ReactTable from 'react-table'; -import { MainOverlayTextBox } from '../MainOverlayTextBox'; export interface TreeViewProps { document: Doc; - deleteDoc: (doc: Doc) => void; + deleteDoc: (doc: Doc) => boolean; moveDocument: DragManager.MoveFunction; dropAction: "alias" | "copy" | undefined; addDocTab: (doc: Doc, where: string) => void; @@ -41,22 +38,18 @@ export interface TreeViewProps { addDocument: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean; indentDocument?: () => void; ScreenToLocalTransform: () => Transform; - outerXf: () => number[]; + outerXf: () => { translateX: number, translateY: number }; treeViewId: string; parentKey: string; active: () => boolean; } -export enum BulletType { - Collapsed, - Collapsible, - List -} - library.add(faTrashAlt); library.add(faAngleRight); library.add(faCaretDown); library.add(faCaretRight); +library.add(faCaretSquareDown); +library.add(faCaretSquareRight); @observer /** @@ -64,25 +57,22 @@ library.add(faCaretRight); */ class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); - private treedropDisposer?: DragManager.DragDropDisposer; - private _mainEle?: HTMLDivElement; + private _treedropDisposer?: DragManager.DragDropDisposer; + private _dref = React.createRef(); + @observable _chosenKey: string = "data"; + @observable _collapsed: boolean = true; + protected createTreeDropTarget = (ele: HTMLDivElement) => { - this.treedropDisposer && this.treedropDisposer(); + this._treedropDisposer && this._treedropDisposer(); if (ele) { - this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); + this._treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.treeDrop.bind(this) } }); } - this._mainEle = ele; } - @observable _isOver: boolean = false; - @observable _collapsed: boolean = true; - @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - @action onMouseEnter = () => { this._isOver = true; }; - @action onMouseLeave = () => { this._isOver = false; }; - + onPointerDown = (e: React.PointerEvent) => e.stopPropagation() onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { @@ -101,80 +91,60 @@ class TreeView extends React.Component { let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; - let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); + let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); this._header!.current!.className = "treeViewItem-header"; - if (inside && this._bulletType !== BulletType.List) this._header!.current!.className += " treeViewItem-header-inside"; + if (inside) this._header!.current!.className += " treeViewItem-header-inside"; else if (before) this._header!.current!.className += " treeViewItem-header-above"; else if (!before) this._header!.current!.className += " treeViewItem-header-below"; e.stopPropagation(); } - onPointerDown = (e: React.PointerEvent) => { - e.stopPropagation(); - } @action - remove = (document: Document, key: string) => { + remove = (document: Document, key: string): boolean => { let children = Cast(this.props.document[key], listSpec(Doc), []); - children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1); + if (children.indexOf(document) !== -1) { + children.splice(children.indexOf(document), 1); + return true; + } + return false; } @action - move: DragManager.MoveFunction = (document: Doc, target: Doc, addDoc) => { - if (this.props.document !== target) { - //TODO This should check if it was removed - this.props.deleteDoc(document); - return addDoc(document); - } - return true; + move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => { + return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); } @action - indent = () => { - this.props.addDocument(this.props.document); - this.delete(); - } - + indent = () => this.props.addDocument(this.props.document) && this.delete(); - renderBullet(type: BulletType) { - let onClicked = action(() => this._collapsed = !this._collapsed); - let bullet: IconProp | undefined = undefined; - switch (type) { - case BulletType.Collapsed: bullet = "caret-right"; break; - case BulletType.Collapsible: bullet = "caret-down"; break; - } - return
{bullet ? : ""}
; + renderBullet() { + let docList = Cast(this.props.document["data"], listSpec(Doc)); + let doc = Cast(this.props.document["data"], Doc); + let isDoc = doc instanceof Doc || docList; + return
this._collapsed = !this._collapsed)}> + {} +
; } - static loadId = ""; - editableView = (key: string, style?: string) => - ( StrCast(this.props.document[key])} - OnFillDown={(value: string) => { - Doc.GetProto(this.props.document)[key] = value; - let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); - TreeView.loadId = doc[Id]; - doc.templates = new List([Templates.Title.Layout]); - this.props.addDocument(doc); - return true; - }} - OnTab={() => this.props.indentDocument && this.props.indentDocument()} - SetValue={(value: string) => { - Doc.GetProto(this.props.document)[key] = value; - return true; - }} - />) - /** - * Renders the EditableView title element for placement into the tree. - */ - renderTitle() { - let reference = React.createRef(); - let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + static loadId = ""; + editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} + SetValue={(value: string) => (Doc.GetProto(this.props.document)[key] = value) ? true : true} + OnFillDown={(value: string) => { + Doc.GetProto(this.props.document)[key] = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); + TreeView.loadId = doc[Id]; + return this.props.addDocument(doc); + }} + OnTab={() => this.props.indentDocument && this.props.indentDocument()} + />) + get keyList() { let keyList: string[] = []; let keys = Array.from(Object.keys(this.props.document)); if (this.props.document.proto instanceof Doc) { @@ -192,19 +162,26 @@ class TreeView extends React.Component { keyList.push(key); } }); - let headerElements = this._bulletType === BulletType.List ? (null) : [this._chosenKey].map(key => - { this._chosenKey = key; this.props.document.embed = !BoolCast(this.props.document.embed, false) })} - style={{ background: key === this._chosenKey ? "lightgray" : undefined }}> - {key} + return keyList; + } + /** + * Renders the EditableView title element for placement into the tree. + */ + renderTitle() { + let reference = React.createRef(); + let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); + + let headerElements = ( + this.props.document.embed = !BoolCast(this.props.document.embed))} > + {this._chosenKey} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; let openRight = dataDocs && dataDocs.indexOf(this.props.document) !== -1 ? (null) : (
- {/* */}
); return <> -
{ let rect = this._header!.current!.getBoundingClientRect(); let bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2); let before = x[1] < bounds[1]; - let inside = x[0] > bounds[0] + 75 || (!before && this._bulletType === BulletType.Collapsible); + let inside = x[0] > bounds[0] + 75 || (!before && !this._collapsed); if (de.data instanceof DragManager.DocumentDragData) { let addDoc = (doc: Doc) => this.props.addDocument(doc, this.props.document, before); if (inside) { @@ -250,18 +227,13 @@ class TreeView extends React.Component { addDoc = (doc: Doc) => { docList && docList.push(doc); return true; }; } } - let added = false; - if (de.data.dropAction || de.data.userDropAction) { - added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false); - } else if (de.data.moveDocument) { - let movedDocs = de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments; - added = movedDocs.reduce((added: boolean, d) => - de.data.moveDocument(d, this.props.document, addDoc) || added, false); - } else { - added = de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false); - } e.stopPropagation(); - return added; + let movedDocs = (de.data.options === this.props.treeViewId ? de.data.draggedDocuments : de.data.droppedDocuments); + return (de.data.dropAction || de.data.userDropAction) ? + de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before) || added, false) + : (de.data.moveDocument) ? + movedDocs.reduce((added: boolean, d) => de.data.moveDocument(d, this.props.document, addDoc) || added, false) + : de.data.droppedDocuments.reduce((added: boolean, d) => this.props.addDocument(d, this.props.document, before), false); } return false; } @@ -279,62 +251,47 @@ class TreeView extends React.Component { docTransform = () => { let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); let outerXf = this.props.outerXf(); - let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf[0] - translateX, outerXf[1] - translateY); + let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); let finalXf = this.props.ScreenToLocalTransform().translate(offset[0], offset[1]); return finalXf; } - @observable _chosenKey: string = "data"; - _bulletType: BulletType = BulletType.List; - _dref = React.createRef(); render() { - let bulletType = BulletType.List; - let contentElement: (JSX.Element | null)[] = []; - [this._chosenKey].map(key => { - let docList = Cast(this.props.document[key], listSpec(Doc)); - let remDoc = (doc: Doc) => this.remove(doc, key); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, key, doc, addBefore, before); - let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { - if (!this._collapsed) { - bulletType = BulletType.Collapsible; - if (this.props.document.embed) { - contentElement.push( -
-
); - } else - contentElement.push(
    -
    - {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, key, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.panelHeight)} -
    -
); - } else { - bulletType = BulletType.Collapsed; - } + let contentElement: (JSX.Element | null) = null; + let docList = Cast(this.props.document[this._chosenKey], listSpec(Doc)); + let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); + let doc = Cast(this.props.document[this._chosenKey], Doc); + let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; + if (!this._collapsed) { + if (!this.props.document.embed && (docList && (DocListCast(docList).length > 0 || this._chosenKey === "data"))) { + contentElement =
    + {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move, + this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)} +
; + } else { + contentElement =
+ + +
} - }); - this._bulletType = bulletType; - return
+ } + return
  • - {this.renderBullet(bulletType)} + {this.renderBullet()} {this.renderTitle()}
    @@ -348,17 +305,17 @@ class TreeView extends React.Component { treeViewId: string, key: string, add: (doc: Doc, relativeTo?: Doc, before?: boolean) => boolean, - remove: ((doc: Doc) => void), + remove: ((doc: Doc) => boolean), move: DragManager.MoveFunction, dropAction: dropActionType, addDocTab: (doc: Doc, where: string) => void, screenToLocalXf: () => Transform, - outerXf: () => number[], + outerXf: () => { translateX: number, translateY: number }, active: () => boolean, panelWidth: () => number, - panelHeight: () => number ) { let docList = docs.filter(child => !child.excludeFromLibrary && (key !== "data" || !child.isMinimized)); + let rowWidth = () => panelWidth() - 20; return docList.map((child, i) => { let indent = i === 0 ? undefined : () => { if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) { @@ -368,15 +325,22 @@ class TreeView extends React.Component { remove(child); } } + let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => { + return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false); + } + let rowHeight = () => { + let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); + return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym](); + } return { export class CollectionTreeView extends CollectionSubView(Document) { private treedropDisposer?: DragManager.DragDropDisposer; private _mainEle?: HTMLDivElement; + protected createTreeDropTarget = (ele: HTMLDivElement) => { - if (this.treedropDisposer) { - this.treedropDisposer(); - } - if (ele) { + this.treedropDisposer && this.treedropDisposer(); + if (this._mainEle = ele) { this.treedropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); } - this._mainEle = ele; + } + + componentWillUnmount() { + this.treedropDisposer && this.treedropDisposer(); } @action - remove = (document: Document) => { + remove = (document: Document): boolean => { let children = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); - children.indexOf(document) !== -1 && children.splice(children.indexOf(document), 1); + if (children.indexOf(document) !== -1) { + children.splice(children.indexOf(document), 1); + return true; + } + return false; } onContextMenu = (e: React.MouseEvent): void => { // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout @@ -415,26 +385,16 @@ export class CollectionTreeView extends CollectionSubView(Document) { } } - outerXf = () => { - let outerXf = Utils.GetScreenTransform(this._mainEle!); - return [outerXf.translateX, outerXf.translateY]; - } - onTreeDrop = (e: React.DragEvent) => { - this.onDrop(e, {}); - } + outerXf = () => Utils.GetScreenTransform(this._mainEle!); + onTreeDrop = (e: React.DragEvent) => this.onDrop(e, {}); + render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; - if (!this.childDocs) { - return (null); - } let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); - let childElements = TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, - moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth, () => 25); - return ( + return !this.childDocs ? (null) : (
    this.props.isSelected() && e.stopPropagation()} onDrop={this.onTreeDrop} @@ -445,22 +405,19 @@ export class CollectionTreeView extends CollectionSubView(Document) { display={"inline"} height={72} GetValue={() => StrCast(this.props.Document.title)} + SetValue={(value: string) => (Doc.GetProto(this.props.Document).title = value) ? true : true} OnFillDown={(value: string) => { - let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - target.title = value; - let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25 }); + Doc.GetProto(this.props.Document).title = value; + let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; - doc.templates = new List([Templates.Title.Layout]); - this.props.addDocument(doc); - }} - SetValue={(value: string) => { - let target = this.props.Document.proto ? this.props.Document.proto : this.props.Document; - target.title = value; - return true; + TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} />
      - {childElements} + { + TreeView.GetChildElements(this.childDocs, this.props.Document[Id], this.props.fieldKey, addDoc, this.remove, + moveDoc, dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.outerXf, this.props.active, this.props.PanelWidth) + }
    ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f7836d947..4992669df 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,7 +72,6 @@ export interface DocumentViewProps { ScreenToLocalTransform: () => Transform; isTopMost: boolean; ContentScaling: () => number; - useActualDimensions?: boolean; PanelWidth: () => number; PanelHeight: () => number; focus: (doc: Doc) => void; @@ -539,8 +538,8 @@ export class DocumentView extends DocComponent(Docu render() { var scaling = this.props.ContentScaling(); - var nativeWidth = this.props.useActualDimensions ? NumCast(this.props.Document.width) : this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; - var nativeHeight = this.props.useActualDimensions ? NumCast(this.props.Document.height) : BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; + var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; + var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; return (
    Date: Wed, 19 Jun 2019 23:43:47 -0400 Subject: slight tweaks. --- src/client/views/EditableView.tsx | 5 +++- .../views/collections/CollectionTreeView.tsx | 35 +++++++++++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 70d6c22bf..0f6281b5c 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -29,6 +29,7 @@ export interface EditableProps { display?: string; oneLine?: boolean; editing?: boolean; + onClick?: (e: React.MouseEvent) => boolean; } /** @@ -65,7 +66,9 @@ export class EditableView extends React.Component { @action onClick = (e: React.MouseEvent) => { - this._editing = true; + if (!this.props.onClick || !this.props.onClick(e)) { + this._editing = true; + } e.stopPropagation(); } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 856430036..c51c16883 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,7 +1,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faAngleRight, faCaretDown, faCaretRight, faTrashAlt, faCaretSquareRight, faCaretSquareDown } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from "mobx"; +import { action, observable, computed } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, HeightSym, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; @@ -125,12 +125,20 @@ class TreeView extends React.Component {
    ; } + titleClicked = (e: React.MouseEvent) => { + if (this._collapsed) return false; + else { + this.props.document.embed = !BoolCast(this.props.document.embed); + return true; + } + } static loadId = ""; editableView = (key: string, style?: string) => ( StrCast(this.props.document[key])} @@ -144,24 +152,24 @@ class TreeView extends React.Component { OnTab={() => this.props.indentDocument && this.props.indentDocument()} />) - get keyList() { - let keyList: string[] = []; + @computed get keyList() { let keys = Array.from(Object.keys(this.props.document)); if (this.props.document.proto instanceof Doc) { keys.push(...Array.from(Object.keys(this.props.document.proto))); while (keys.indexOf("proto") !== -1) keys.splice(keys.indexOf("proto"), 1); } - if (keys.indexOf("data") !== -1) { - keys.splice(keys.indexOf("data"), 1); - keys.splice(0, 0, "data"); - } + let keyList: string[] = []; keys.map(key => { let docList = Cast(this.props.document[key], listSpec(Doc)); let doc = Cast(this.props.document[key], Doc); - if (doc instanceof Doc || (docList && (DocListCast(docList).length > 0 || key === "data"))) { + if (doc instanceof Doc || docList) { keyList.push(key); } }); + if (keyList.indexOf("data") !== -1) { + keyList.splice(keyList.indexOf("data"), 1); + } + keyList.splice(0, 0, "data"); return keyList; } /** @@ -172,7 +180,12 @@ class TreeView extends React.Component { let onItemDown = SetupDrag(reference, () => this.props.document, this.move, this.props.dropAction, this.props.treeViewId, true); let headerElements = ( - this.props.document.embed = !BoolCast(this.props.document.embed))} > + { + let ind = this.keyList.indexOf(this._chosenKey); + ind = (ind + 1) % this.keyList.length; + this._chosenKey = this.keyList[ind]; + })} > {this._chosenKey} ); let dataDocs = CollectionDockingView.Instance ? Cast(CollectionDockingView.Instance.props.Document.data, listSpec(Doc), []) : []; @@ -209,7 +222,7 @@ class TreeView extends React.Component { } else { ContextMenu.Instance.addItem({ description: "Delete Workspace", event: undoBatch(() => this.props.deleteDoc(this.props.document)) }); } - ContextMenu.Instance.displayMenu(e.pageX - 156, e.pageY - 15); + ContextMenu.Instance.displayMenu(e.pageX > 156 ? e.pageX - 156 : 0, e.pageY - 15); e.stopPropagation(); } } @@ -264,7 +277,7 @@ class TreeView extends React.Component { let doc = Cast(this.props.document[this._chosenKey], Doc); let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; if (!this._collapsed) { - if (!this.props.document.embed && (docList && (DocListCast(docList).length > 0 || this._chosenKey === "data"))) { + if (!this.props.document.embed) { contentElement =
      {TreeView.GetChildElements(doc instanceof Doc ? [doc] : DocListCast(docList), this.props.treeViewId, this._chosenKey, addDoc, remDoc, this.move, this.props.dropAction, this.props.addDocTab, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth)} -- cgit v1.2.3-70-g09d2 From 01aee875e626c695fe208addaaa6f58aad387dd6 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 10:02:08 -0400 Subject: Mostly keep context menu on screen --- src/client/views/ContextMenu.tsx | 74 +++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index 59a0de2a0..fd2f970da 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -6,6 +6,7 @@ import "./ContextMenu.scss"; import { library } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearch, faCircle } from '@fortawesome/free-solid-svg-icons'; +import Measure from "react-measure"; library.add(faSearch); library.add(faCircle); @@ -23,15 +24,12 @@ export class ContextMenu extends React.Component { @observable private _yRelativeToTop: boolean = true; @observable selectedIndex = -1; - private _searchRef = React.createRef(); - - private ref: React.RefObject; + @observable private _width: number = 0; + @observable private _height: number = 0; constructor(props: Readonly<{}>) { super(props); - this.ref = React.createRef(); - ContextMenu.Instance = this; } @@ -51,23 +49,42 @@ export class ContextMenu extends React.Component { return this._items; } + static readonly buffer = 20; + get pageX() { + const x = this._pageX; + if (x < 0) { + return 0; + } + const width = this._width; + if (x + width > window.innerWidth - ContextMenu.buffer) { + return window.innerWidth - ContextMenu.buffer - width; + } + return x; + } + + get pageY() { + const y = this._pageY; + if (y < 0) { + return 0; + } + const height = this._height; + if (y + height > window.innerHeight - ContextMenu.buffer) { + return window.innerHeight - ContextMenu.buffer - height; + } + return y; + } + @action displayMenu(x: number, y: number) { //maxX and maxY will change if the UI/font size changes, but will work for any amount //of items added to the menu - let maxX = window.innerWidth - 150; - let maxY = window.innerHeight - ((this._items.length + 1/*for search box*/) * 34 + 30); - this._pageX = x > maxX ? maxX : x; - this._pageY = y > maxY ? maxY : y; + this._pageX = x; + this._pageY = y; this._searchString = ""; this._display = true; - - if (this._searchRef.current) { - this._searchRef.current.focus(); - } } @action @@ -143,19 +160,28 @@ export class ContextMenu extends React.Component { if (!this._display) { return null; } - let style = this._yRelativeToTop ? { left: this._pageX, top: this._pageY } : - { left: this._pageX, bottom: this._pageY }; + let style = this._yRelativeToTop ? { left: this.pageX, top: this.pageY } : + { left: this.pageX, bottom: this.pageY }; + + console.log(this._pageX); + console.log(this.pageX); + console.log(); return ( -
      - - - - - - - {this.menuItems} -
      + { this._width = r.offset.width; this._height = r.offset.height; })}> + {({ measureRef }) => ( +
      + + + + + + + {this.menuItems} +
      + ) + } +
      ); } -- cgit v1.2.3-70-g09d2 From a5478b2d4cc3b66c6b58471cbb05c623d0109724 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 10:04:51 -0400 Subject: "Fixed" search --- src/server/Search.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/server/Search.ts b/src/server/Search.ts index fd6ef36a6..d776480c6 100644 --- a/src/server/Search.ts +++ b/src/server/Search.ts @@ -7,7 +7,6 @@ export class Search { private url = 'http://localhost:8983/solr/'; public async updateDocument(document: any) { - return; try { const res = await rp.post(this.url + "dash/update", { headers: { 'content-type': 'application/json' }, -- cgit v1.2.3-70-g09d2 From e9d62f4ca0dbeb57e46239047041a8a04da7b504 Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 20 Jun 2019 11:26:16 -0400 Subject: changed color picker. fixed delting selected docs. fixed scaling items in nested panels. --- src/client/util/SelectionManager.ts | 2 +- src/client/views/DocumentDecorations.tsx | 6 ++---- src/client/views/InkingControl.tsx | 11 +++++++++-- src/client/views/MainView.tsx | 6 +++--- 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 09bccb1a0..7dbb81e76 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -66,7 +66,7 @@ export namespace SelectionManager { export function GetIsDragging() { return manager.IsDragging; } export function SelectedDocuments(): Array { - return manager.SelectedDocuments; + return manager.SelectedDocuments.slice(); } export function ViewsSortedHorizontally(): DocumentView[] { let sorted = SelectionManager.SelectedDocuments().slice().sort((doc1, doc2) => { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index d8642d675..2c0e18bbb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -464,16 +464,14 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> runInAction(() => FormattedTextBox.InputBoxOverlay = undefined); SelectionManager.SelectedDocuments().forEach(element => { - const rect = element.ContentDiv ? element.ContentDiv.getBoundingClientRect() : new DOMRect(); - - if (rect.width !== 0 && (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0)) { + if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { let doc = PositionDocument(element.props.Document); let nwidth = doc.nativeWidth || 0; let nheight = doc.nativeHeight || 0; let zoomBasis = NumCast(doc.zoomBasis, 1); let width = (doc.width || 0) / zoomBasis; let height = (doc.height || (nheight / nwidth * width)) / zoomBasis; - let scale = width / rect.width; + let scale = element.props.ScreenToLocalTransform().Scale; let actualdW = Math.max(width + (dW * scale), 20); let actualdH = Math.max(height + (dH * scale), 20); doc.x = (doc.x || 0) + dX * (actualdW - width); diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index b98132c23..0837e07a9 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -28,11 +28,18 @@ export class InkingControl extends React.Component { switchTool = (tool: InkTool): void => { this._selectedTool = tool; } + decimalToHexString(number: number) { + if (number < 0) { + number = 0xFFFFFFFF + number + 1; + } + + return number.toString(16).toUpperCase(); + } @action switchColor = (color: ColorResult): void => { - this._selectedColor = color.hex; - SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = color.hex); + this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); + SelectionManager.SelectedDocuments().forEach(doc => Doc.GetProto(doc.props.Document).backgroundColor = this._selectedColor); } @action diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e3d4ff8b5..51630c29b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -3,7 +3,7 @@ import { faFilePdf, faFilm, faFont, faGlobeAsia, faImage, faMusic, faObjectGroup import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; -import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker } from 'react-color'; +import { CirclePicker, SliderPicker, BlockPicker, TwitterPicker, SketchPicker } from 'react-color'; import "normalize.css"; import * as React from 'react'; import Measure from 'react-measure'; @@ -272,8 +272,8 @@ export class MainView extends React.Component {
    • {btns.map(btn => -- cgit v1.2.3-70-g09d2 From af8e5cf1bfbfa2d57b4fd89c72306a71d8cabe1d Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 11:32:54 -0400 Subject: Fixed context menu search --- src/client/views/ContextMenu.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index fd2f970da..69692dbb8 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -167,17 +167,22 @@ export class ContextMenu extends React.Component { console.log(this.pageX); console.log(); + const contents = ( + <> + + + + + + + {this.menuItems} + + ); return ( { this._width = r.offset.width; this._height = r.offset.height; })}> {({ measureRef }) => (
      - - - - - - - {this.menuItems} + {contents}
      ) } -- cgit v1.2.3-70-g09d2 From 1f24c5010a1cf6365265ea1f02327bb81a98134a Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 14:54:55 -0400 Subject: Doc.GetProto change and swapped KVP syntax --- src/client/views/nodes/KeyValueBox.tsx | 2 +- src/client/views/nodes/KeyValuePair.tsx | 3 ++- src/new_fields/Doc.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 917be734d..3d626eef0 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -54,7 +54,7 @@ export class KeyValueBox extends React.Component { field = res.result; } if (Field.IsField(field, true)) { - let target = !eq ? doc : Doc.GetProto(doc); + let target = eq ? doc : Doc.GetProto(doc); target[key] = field; return true; } diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index dd1bca7f6..420a1ad94 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -61,10 +61,11 @@ export class KeyValuePair extends React.Component { { + const onDelegate = Object.keys(props.Document).includes(props.fieldKey); let field = FieldValue(props.Document[props.fieldKey]); if (Field.IsField(field)) { - return Field.toScriptString(field); + return (onDelegate ? "=" : "") + Field.toScriptString(field); } return ""; }} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 1b0ff812f..de4bc4d5d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -207,7 +207,7 @@ export namespace Doc { // gets the document's prototype or returns the document if it is a prototype export function GetProto(doc: Doc) { - return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : doc.proto!; + return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc); } export function allKeys(doc: Doc): string[] { -- cgit v1.2.3-70-g09d2 From 9b3e80def0be6c09c31b5176817a54323d217d81 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 15:06:41 -0400 Subject: Handled more events in editable view --- src/client/views/EditableView.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 0f6281b5c..97a2d19dd 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -72,10 +72,14 @@ export class EditableView extends React.Component { e.stopPropagation(); } + stopPropagation(e: React.SyntheticEvent) { + e.stopPropagation(); + } + render() { if (this._editing) { return this._editing = false)} + onBlur={action(() => this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} style={{ display: this.props.display }} />; } else { return ( -- cgit v1.2.3-70-g09d2 From a5dc0e04add05f2f5bf1e17f1ac0a5e0aba1ea41 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 16:27:44 -0400 Subject: Added hidden flag to documents --- src/client/views/nodes/DocumentView.tsx | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index acd5e4cf2..280804630 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -89,6 +89,7 @@ const schema = createSchema({ nativeWidth: "number", nativeHeight: "number", backgroundColor: "string", + hidden: "boolean" }); export const positionSchema = createSchema({ @@ -538,6 +539,9 @@ export class DocumentView extends DocComponent(Docu } render() { + if (this.Document.hidden) { + return null; + } var scaling = this.props.ContentScaling(); var nativeWidth = this.nativeWidth > 0 ? `${this.nativeWidth}px` : "100%"; var nativeHeight = BoolCast(this.props.Document.ignoreAspect) ? this.props.PanelHeight() / this.props.ContentScaling() : this.nativeHeight > 0 ? `${this.nativeHeight}px` : "100%"; -- cgit v1.2.3-70-g09d2 From a39b2854b848006c19460685d7bf4005a9f650ae Mon Sep 17 00:00:00 2001 From: bob Date: Thu, 20 Jun 2019 17:09:50 -0400 Subject: moved AddDocToList to Doc utils --- src/client/views/collections/CollectionTreeView.tsx | 18 ++++-------------- src/new_fields/Doc.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c51c16883..443c763bf 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -251,16 +251,6 @@ class TreeView extends React.Component { return false; } - public static AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean) { - let list = Cast(target[key], listSpec(Doc)); - if (list) { - let ind = relativeTo ? list.indexOf(relativeTo) : -1; - if (ind === -1) list.push(doc); - else list.splice(before ? ind : ind + 1, 0, doc); - } - return true; - } - docTransform = () => { let { scale, translateX, translateY } = Utils.GetScreenTransform(this._dref.current!); let outerXf = this.props.outerXf(); @@ -273,7 +263,7 @@ class TreeView extends React.Component { let contentElement: (JSX.Element | null) = null; let docList = Cast(this.props.document[this._chosenKey], listSpec(Doc)); let remDoc = (doc: Doc) => this.remove(doc, this._chosenKey); - let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); + let addDoc = (doc: Doc, addBefore?: Doc, before?: boolean) => Doc.AddDocToList(this.props.document, this._chosenKey, doc, addBefore, before); let doc = Cast(this.props.document[this._chosenKey], Doc); let docWidth = () => NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; if (!this._collapsed) { @@ -334,7 +324,7 @@ class TreeView extends React.Component { if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) { let fieldKeysub = StrCast(docList[i - 1].layout).split("fieldKey")[1]; let fieldKey = fieldKeysub.split("\"")[1]; - TreeView.AddDocToList(docList[i - 1], fieldKey, child); + Doc.AddDocToList(docList[i - 1], fieldKey, child); remove(child); } } @@ -403,7 +393,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { render() { let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; - let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); + let addDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before); let moveDoc = (d: Doc, target: Doc, addDoc: (doc: Doc) => boolean) => this.props.moveDocument(d, target, addDoc); return !this.childDocs ? (null) : ( @@ -423,7 +413,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { Doc.GetProto(this.props.Document).title = value; let doc = Docs.FreeformDocument([], { title: "", x: 0, y: 0, width: 100, height: 25, templates: new List([Templates.Title.Layout]) }); TreeView.loadId = doc[Id]; - TreeView.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); + Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, this.childDocs.length ? this.childDocs[0] : undefined, true); }} />
    • diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 1b0ff812f..7bae7a02d 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -222,6 +222,16 @@ export namespace Doc { return Array.from(results); } + export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean) { + let list = Cast(target[key], listSpec(Doc)); + if (list) { + let ind = relativeTo ? list.indexOf(relativeTo) : -1; + if (ind === -1) list.push(doc); + else list.splice(before ? ind : ind + 1, 0, doc); + } + return true; + } + export function MakeAlias(doc: Doc) { if (!GetT(doc, "isPrototype", "boolean", true)) { return Doc.MakeCopy(doc); -- cgit v1.2.3-70-g09d2 From d2dfc0f9d35f0084a7c0dea73215f5d21055f2f3 Mon Sep 17 00:00:00 2001 From: yipstanley Date: Thu, 20 Jun 2019 17:17:14 -0400 Subject: pdf page sizes loading error --- src/client/views/pdf/PDFViewer.tsx | 1 + src/client/views/pdf/Page.tsx | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 7000352e7..6adead626 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -203,6 +203,7 @@ class Viewer extends React.Component { this._isPage[page] = "page"; this._visibleElements[page] = ( ; name: string; numPages: number; @@ -36,8 +37,8 @@ interface IPageProps { @observer export default class Page extends React.Component { @observable private _state: string = "N/A"; - @observable private _width: number = 0; - @observable private _height: number = 0; + @observable private _width: number = this.props.size.width; + @observable private _height: number = this.props.size.height; @observable private _page: Opt; @observable private _currPage: number = this.props.page + 1; @observable private _marqueeX: number = 0; -- cgit v1.2.3-70-g09d2 From 1f172642d12c4669960b8526324e4bd034994be4 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 17:44:24 -0400 Subject: Added arrange documents in grid command --- .../collectionFreeForm/CollectionFreeFormView.tsx | 32 ++++++++++++++++++++-- src/client/views/nodes/DocumentView.tsx | 2 +- 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 84841e469..f756fe625 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,6 +1,6 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym } from "../../../../new_fields/Doc"; +import { Doc, HeightSym, WidthSym, DocListCastAsync } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; import { InkField, StrokeData } from "../../../../new_fields/InkField"; import { createSchema, makeInterface } from "../../../../new_fields/Schema"; @@ -26,6 +26,7 @@ import { MarqueeView } from "./MarqueeView"; import React = require("react"); import v5 = require("uuid/v5"); import PDFMenu from "../../pdf/PDFMenu"; +import { ContextMenu } from "../../ContextMenu"; export const panZoomSchema = createSchema({ panX: "number", @@ -339,6 +340,33 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); } + onContextMenu = () => { + ContextMenu.Instance.addItem({ + description: "Arrange contents in grid", + event: async () => { + const docs = await DocListCastAsync(this.Document[this.props.fieldKey]); + if (docs) { + let startX = this.Document.panX || 0; + let x = startX; + let y = this.Document.panY || 0; + let i = 0; + const width = Math.max(...docs.map(doc => NumCast(doc.width))); + const height = Math.max(...docs.map(doc => NumCast(doc.height))); + for (const doc of docs) { + doc.x = x; + doc.y = y; + x += width + 20; + if (++i === 6) { + i = 0; + x = startX; + y += height + 20; + } + } + } + } + }); + } + private childViews = () => [ , ...this.views @@ -349,7 +377,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return (
      + onPointerDown={this.onPointerDown} onPointerMove={this.onCursorMove} onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onContextMenu={this.onContextMenu}> diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 280804630..522c37989 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -237,7 +237,7 @@ export class DocumentView extends DocComponent(Docu static _undoBatch?: UndoManager.Batch = undefined; @action - public collapseTargetsToPoint = async (scrpt: number[], expandedDocs: Doc[] | undefined): Promise => { + public collapseTargetsToPoint = (scrpt: number[], expandedDocs: Doc[] | undefined): void => { SelectionManager.DeselectAll(); if (expandedDocs) { if (!DocumentView._undoBatch) { -- cgit v1.2.3-70-g09d2 From 0cab79a50719719e1dade40520a6967f7aa8f951 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Thu, 20 Jun 2019 18:35:45 -0400 Subject: Added debug and release modes to server and client --- .gitignore | 2 ++ package.json | 2 ++ src/client/util/ClientUtils.ts.temp | 3 ++ .../views/collections/CollectionStackingView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 16 +++++----- src/server/index.ts | 34 ++++++++++++++++++---- 6 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 src/client/util/ClientUtils.ts.temp (limited to 'src') diff --git a/.gitignore b/.gitignore index a499c39a3..5d3100dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ node_modules package-lock.json dist/ .DS_Store +.env +ClientUtils.ts \ No newline at end of file diff --git a/package.json b/package.json index 7fd6c4ba9..91d3a3853 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@types/cookie-parser": "^1.4.1", "@types/cookie-session": "^2.0.36", "@types/d3-format": "^1.3.1", + "@types/dotenv": "^6.1.1", "@types/express": "^4.16.1", "@types/express-flash": "0.0.0", "@types/express-session": "^1.15.12", @@ -110,6 +111,7 @@ "cookie-session": "^2.0.0-beta.3", "crypto-browserify": "^3.11.0", "d3-format": "^1.3.2", + "dotenv": "^8.0.0", "express": "^4.16.4", "express-flash": "0.0.2", "express-session": "^1.15.6", diff --git a/src/client/util/ClientUtils.ts.temp b/src/client/util/ClientUtils.ts.temp new file mode 100644 index 000000000..f9fad5ed9 --- /dev/null +++ b/src/client/util/ClientUtils.ts.temp @@ -0,0 +1,3 @@ +export namespace ClientUtils { + export const RELEASE = "mode"; +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 368e94a8c..521e8d616 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -101,7 +101,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let renderScale = this.columnWidth / NumCast(d.nativeWidth, this.columnWidth); let aspect = NumCast(d.nativeWidth) / NumCast(d.nativeHeight); let width = () => this.columnWidth; - let height = () => aspect ? width() / aspect : d[HeightSym]() + let height = () => aspect ? width() / aspect : d[HeightSym](); let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); let childFocus = (doc: Doc) => { doc.libraryBrush = true; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c51c16883..c7ed2eb38 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -72,7 +72,7 @@ class TreeView extends React.Component { @undoBatch delete = () => this.props.deleteDoc(this.props.document); @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); - onPointerDown = (e: React.PointerEvent) => e.stopPropagation() + onPointerDown = (e: React.PointerEvent) => e.stopPropagation(); onPointerEnter = (e: React.PointerEvent): void => { this.props.active() && (this.props.document.libraryBrush = true); if (e.buttons === 1 && SelectionManager.GetIsDragging()) { @@ -114,11 +114,11 @@ class TreeView extends React.Component { return this.props.document !== target && this.props.deleteDoc(doc) && addDoc(doc); } @action - indent = () => this.props.addDocument(this.props.document) && this.delete(); + indent = () => this.props.addDocument(this.props.document) && this.delete() renderBullet() { - let docList = Cast(this.props.document["data"], listSpec(Doc)); - let doc = Cast(this.props.document["data"], Doc); + let docList = Cast(this.props.document.data, listSpec(Doc)); + let doc = Cast(this.props.document.data, Doc); let isDoc = doc instanceof Doc || docList; return
      this._collapsed = !this._collapsed)}> {} @@ -298,7 +298,7 @@ class TreeView extends React.Component { addDocTab={this.props.addDocTab} setPreviewScript={emptyFunction}> -
      +
      ; } } return
      @@ -337,14 +337,14 @@ class TreeView extends React.Component { TreeView.AddDocToList(docList[i - 1], fieldKey, child); remove(child); } - } + }; let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => { return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false); - } + }; let rowHeight = () => { let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); return aspect ? Math.min(child[WidthSym](), rowWidth()) / aspect : child[HeightSym](); - } + }; return request.get(url).pipe(fs.createWriteStream(dest)); +const release = process.env.RELEASE === "true"; +if (process.env.RELEASE === "true") { + console.log("Running server in release mode"); +} else { + console.log("Running server in debug mode"); +} +console.log(process.env.PWD); +let clientUtils = fs.readFileSync("./src/client/util/ClientUtils.ts.temp", "utf8"); +clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(release))}`; +fs.writeFileSync("./src/client/util/ClientUtils.ts", clientUtils, "utf8"); + const mongoUrl = 'mongodb://localhost:27017/Dash'; mongoose.connect(mongoUrl); mongoose.connection.on('connected', () => console.log("connected")); @@ -406,11 +418,21 @@ app.post(RouteStore.reset, postReset); app.use(RouteStore.corsProxy, (req, res) => req.pipe(request(req.url.substring(1))).pipe(res)); -app.get(RouteStore.delete, (req, res) => - deleteFields().then(() => res.redirect(RouteStore.home))); +app.get(RouteStore.delete, (req, res) => { + if (release) { + res.send("no"); + return; + } + deleteFields().then(() => res.redirect(RouteStore.home)); +}); -app.get(RouteStore.deleteAll, (req, res) => - deleteAll().then(() => res.redirect(RouteStore.home))); +app.get(RouteStore.deleteAll, (req, res) => { + if (release) { + res.send("no"); + return; + } + deleteAll().then(() => res.redirect(RouteStore.home)); +}); app.use(wdm(compiler, { publicPath: config.output.publicPath })); @@ -435,7 +457,9 @@ server.on("connection", function (socket: Socket) { Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args)); Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField); Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields); - Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields); + if (!release) { + Utils.AddServerHandler(socket, MessageStore.DeleteAll, deleteFields); + } Utils.AddServerHandler(socket, MessageStore.CreateField, CreateField); Utils.AddServerHandler(socket, MessageStore.UpdateField, diff => UpdateField(socket, diff)); -- cgit v1.2.3-70-g09d2 From a40e7bb5e9d1256002083d7e3f3c4db60cd8e9df Mon Sep 17 00:00:00 2001 From: Sam Wilkins <35748010+samwilkins333@users.noreply.github.com> Date: Thu, 20 Jun 2019 19:41:39 -0400 Subject: Fixed missed pointer up event --- src/client/views/collections/CollectionDockingView.tsx | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 69b9e77eb..de5c66c0b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -259,6 +259,11 @@ export class CollectionDockingView extends React.Component { this._isPointerDown = true; + let onPointerUp = action(() => { + window.removeEventListener("pointerup", onPointerUp) + this._isPointerDown = false + }) + window.addEventListener("pointerup", onPointerUp); var className = (e.target as any).className; if (className === "messageCounter") { e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From ca5e29fdc7c238274eaf90682a8fa2ddc90e4e17 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 20 Jun 2019 22:22:57 -0400 Subject: fix to open on right, fix to image drag fro web, and layout fixes for stacking view multi-column --- .../views/collections/CollectionStackingView.scss | 13 +++++ .../views/collections/CollectionStackingView.tsx | 62 ++++++++-------------- src/client/views/collections/CollectionSubView.tsx | 7 ++- .../views/collections/CollectionTreeView.tsx | 2 +- 4 files changed, 42 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index af194aec9..485ecf1de 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -38,4 +38,17 @@ background: $dark-color; color: $light-color; } + + + .collectionStackingView-columnDoc, + .collectionStackingView-masonryDoc { + margin-left: auto; + margin-right: auto; + } + + .collectionStackingView-masonryDoc { + transform-origin: top left; + grid-column-end: span 1; + height: 100%; + } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 368e94a8c..c855cb43a 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,5 +1,5 @@ import React = require("react"); -import { action, computed, IReactionDisposer, reaction } from "mobx"; +import { action, computed, IReactionDisposer, reaction, trace } from "mobx"; import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; @@ -10,7 +10,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { CollectionSchemaPreview } from "./CollectionSchemaView"; import "./CollectionStackingView.scss"; import { CollectionSubView } from "./CollectionSubView"; -import { auto } from "async"; +import { Transform } from "../../util/Transform"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -67,18 +67,17 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let children = this.childDocs.filter(d => !d.isMinimized); return children.map((d, i) => { let dref = React.createRef(); - let script = undefined; - let colWidth = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth; - let rowHeight = () => this.singleColDocHeight(d); let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]()); - return
      d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth; + let height = () => this.singleColDocHeight(d); + return
      + style={{ width: width(), height: height() }} > doc) { whenActiveChanged={this.props.whenActiveChanged} addDocTab={this.props.addDocTab} setPreviewScript={emptyFunction} - previewScript={script}> + previewScript={undefined}>
      ; }); @@ -96,46 +95,31 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @computed get children() { return this.childDocs.filter(d => !d.isMinimized).map((d, i) => { + let aspect = d.nativeHeight ? NumCast(d.nativeWidth) / NumCast(d.nativeHeight) : undefined; let dref = React.createRef(); let dxf = () => this.getDocTransform(d, dref.current!).scale(this.columnWidth / d[WidthSym]()); - let renderScale = this.columnWidth / NumCast(d.nativeWidth, this.columnWidth); - let aspect = NumCast(d.nativeWidth) / NumCast(d.nativeHeight); - let width = () => this.columnWidth; + let width = () => d.nativeWidth ? Math.min(d[WidthSym](), this.columnWidth) : this.columnWidth; let height = () => aspect ? width() / aspect : d[HeightSym]() let rowSpan = Math.ceil((height() + this.gridGap) / (this._gridSize + this.gridGap)); - let childFocus = (doc: Doc) => { - doc.libraryBrush = true; - this.props.focus(this.props.Document); // just focus on this collection, not the underlying document because the API doesn't support adding an offset to focus on and we can't pan zoom our contents to be centered. - }; return (
      - + + setPreviewScript={emptyFunction} + previewScript={undefined}> +
      ); }); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index e55cd9e37..699bddc7c 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -176,8 +176,11 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { return; } if (html && !html.startsWith(" 1 && tags[1].startsWith("img") ? tags[1] : ""; + if (img) { + let split = img.split("src=\"")[1].split("\"")[0]; let doc = Docs.ImageDocument(split, { ...options, width: 300 }); this.props.addDocument(doc, false); return; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c51c16883..e7a3c4022 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -70,7 +70,7 @@ class TreeView extends React.Component { } @undoBatch delete = () => this.props.deleteDoc(this.props.document); - @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "openRight"); + @undoBatch openRight = async () => this.props.addDocTab(this.props.document, "onRight"); onPointerDown = (e: React.PointerEvent) => e.stopPropagation() onPointerEnter = (e: React.PointerEvent): void => { -- cgit v1.2.3-70-g09d2 From 6e5cd0e991e2e6d7ae8de1d73ff273ba0737355c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sun, 23 Jun 2019 17:23:33 -0400 Subject: Fixed shift dragging with no open panes --- src/client/views/collections/CollectionDockingView.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index de5c66c0b..5f8862c43 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -135,10 +135,11 @@ export class CollectionDockingView extends React.Component Date: Mon, 24 Jun 2019 10:26:57 -0400 Subject: Changed how zooming works --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f756fe625..4b4e7465a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -217,7 +217,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } else { // if (modes[e.deltaMode] === 'pixels') coefficient = 50; // else if (modes[e.deltaMode] === 'lines') coefficient = 1000; // This should correspond to line-height?? - let deltaScale = (1 - (e.deltaY / coefficient)); + let deltaScale = e.deltaY > 0 ? (1 / 1.1) : 1.1; if (deltaScale * this.zoomScaling() < 1 && this.isAnnotationOverlay) { deltaScale = 1 / this.zoomScaling(); } -- cgit v1.2.3-70-g09d2 From 52051829373bc4acfe9d705b64c30e3fddebf439 Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Mon, 24 Jun 2019 10:49:05 -0400 Subject: Fixed image size stuff --- package.json | 3 +- src/client/documents/Documents.ts | 2 +- src/client/util/request-image-size.js | 73 +++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/client/util/request-image-size.js (limited to 'src') diff --git a/package.json b/package.json index 91d3a3853..713c5d585 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "html-to-image": "^0.1.0", "i": "^0.3.6", "image-data-uri": "^2.0.0", + "image-size": "^0.7.4", "imagesloaded": "^4.1.4", "jsonwebtoken": "^8.5.0", "jsx-to-string": "^1.4.0", @@ -175,13 +176,13 @@ "react-split-pane": "^0.1.85", "react-table": "^6.9.2", "request": "^2.88.0", - "request-image-size": "^2.1.0", "request-promise": "^4.2.4", "serializr": "^1.5.1", "sharp": "^0.22.1", "socket.io": "^2.2.0", "socket.io-client": "^2.2.0", "solr-node": "^1.1.3", + "standard-http-error": "^2.0.1", "typescript-collections": "^1.3.2", "url-loader": "^1.1.2", "uuid": "^3.3.2", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index de6c5bc6a..b04fc401a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -34,7 +34,7 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; -var requestImageSize = require('request-image-size'); +var requestImageSize = require('../util/request-image-size'); var path = require('path'); export interface DocumentOptions { diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js new file mode 100644 index 000000000..0f9328872 --- /dev/null +++ b/src/client/util/request-image-size.js @@ -0,0 +1,73 @@ +/** + * request-image-size: Detect image dimensions via request. + * Licensed under the MIT license. + * + * https://github.com/FdezRomero/request-image-size + * © 2017 Rodrigo Fernández Romero + * + * Based on the work of Johannes J. Schmidt + * https://github.com/jo/http-image-size + */ + +const request = require('request'); +const imageSize = require('image-size'); +const HttpError = require('standard-http-error'); + +module.exports = function requestImageSize(options) { + let opts = { + encoding: null + }; + + if (options && typeof options === 'object') { + opts = Object.assign(options, opts); + } else if (options && typeof options === 'string') { + opts = Object.assign({ uri: options }, opts); + } else { + return Promise.reject(new Error('You should provide an URI string or a "request" options object.')); + } + + opts.encoding = null; + + return new Promise((resolve, reject) => { + const req = request(opts); + + req.on('response', res => { + if (res.statusCode >= 400) { + return reject(new HttpError(res.statusCode, res.statusMessage)); + } + + let buffer = new Buffer([]); + let size; + let imageSizeError; + + res.on('data', chunk => { + buffer = Buffer.concat([buffer, chunk]); + + try { + size = imageSize(buffer); + } catch (err) { + imageSizeError = err; + return; + } + + if (size) { + resolve(size); + return req.abort(); + } + }); + + res.on('error', err => reject(err)); + + res.on('end', () => { + if (!size) { + return reject(imageSizeError); + } + + size.downloaded = buffer.length; + return resolve(size); + }); + }); + + req.on('error', err => reject(err)); + }); +}; -- cgit v1.2.3-70-g09d2