diff options
| author | bob <bcz@cs.brown.edu> | 2019-04-30 13:34:30 -0400 |
|---|---|---|
| committer | bob <bcz@cs.brown.edu> | 2019-04-30 13:34:30 -0400 |
| commit | f59503c26b165b71cf3fa6fe4c1b7a14cef2d441 (patch) | |
| tree | e079e726759f5f0e8d08783829860bd05b5f6e99 /src/client/views/collections | |
| parent | e013b7b146f91b0ffbc26e3770f5f90f417da60b (diff) | |
| parent | 86e89178628a27a91665ad835046e536bdb89729 (diff) | |
Merge branch 'master' into newDocs
Diffstat (limited to 'src/client/views/collections')
10 files changed, 134 insertions, 37 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index ed761d3f3..76adfcdcd 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -8,6 +8,7 @@ import { Doc, FieldResult, Opt } from '../../../new_fields/Doc'; import { listSpec } from '../../../new_fields/Schema'; import { List } from '../../../new_fields/List'; import { Id } from '../../../new_fields/RefField'; +import { SelectionManager } from '../../util/SelectionManager'; export enum CollectionViewType { Invalid, @@ -104,8 +105,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { // set the ZoomBasis only if hasn't already been set -- bcz: maybe set/resetting the ZoomBasis should be a parameter to addDocument? if (this.collectionViewType === CollectionViewType.Freeform || this.collectionViewType === CollectionViewType.Invalid) { let zoom = NumCast(this.props.Document.scale, 1); - let screen = this.props.ScreenToLocalTransform().inverse().Scale / (this.props as any).ContentScaling() * zoom; - doc.zoomBasis = screen; + doc.zoomBasis = zoom; } } return true; @@ -145,6 +145,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { return true; } if (this.removeDocument(doc)) { + SelectionManager.DeselectAll(); return addDocument(doc); } return false; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 1574562c6..958335425 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -241,6 +241,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp this.props.Document.data = json; if (this.undohack && !this.hack) { this.undohack.end(); + this.undohack = undefined; } this.hack = false; } diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss index 0eca3f1cd..f6fb79582 100644 --- a/src/client/views/collections/CollectionPDFView.scss +++ b/src/client/views/collections/CollectionPDFView.scss @@ -1,20 +1,39 @@ .collectionPdfView-buttonTray { - top : 25px; + top : 15px; left : 20px; position: relative; transform-origin: left top; position: absolute; } +.collectionPdfView-thumb { + width:25px; + height:25px; + transform-origin: left top; + position: absolute; + background: darkgray; +} +.collectionPdfView-slider { + width:25px; + height:25px; + transform-origin: left top; + position: absolute; + background: lightgray; +} .collectionPdfView-cont{ width: 100%; height: 100%; position: absolute; top: 0; left:0; - +} +.collectionPdfView-cont-dragging { + span { + user-select: none; + } } .collectionPdfView-backward { color : white; + font-size: 24px; top :0px; left : 0px; position: absolute; @@ -22,8 +41,9 @@ } .collectionPdfView-forward { color : white; + font-size: 24px; top :0px; - left : 35px; + left : 45px; position: absolute; background-color: rgba(50, 50, 50, 0.2); }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 5a1af354a..b3762206a 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -1,4 +1,4 @@ -import { action } from "mobx"; +import { action, observable } from "mobx"; import { observer } from "mobx-react"; import { ContextMenu } from "../ContextMenu"; import "./CollectionPDFView.scss"; @@ -17,18 +17,44 @@ export class CollectionPDFView extends React.Component<FieldViewProps> { public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(CollectionPDFView, fieldKey); } + @observable _inThumb = false; + private set curPage(value: number) { this.props.Document.curPage = value; } private get curPage() { return NumCast(this.props.Document.curPage, -1); } private get numPages() { return NumCast(this.props.Document.numPages); } @action onPageBack = () => this.curPage > 1 ? (this.props.Document.curPage = this.curPage - 1) : -1; @action onPageForward = () => this.curPage < this.numPages ? (this.props.Document.curPage = this.curPage + 1) : -1; + @action + onThumbDown = (e: React.PointerEvent) => { + document.addEventListener("pointermove", this.onThumbMove, false); + document.addEventListener("pointerup", this.onThumbUp, false); + e.stopPropagation(); + this._inThumb = true; + } + @action + onThumbMove = (e: PointerEvent) => { + let pso = (e.clientY - (e as any).target.parentElement.getBoundingClientRect().top) / (e as any).target.parentElement.getBoundingClientRect().height; + this.curPage = Math.trunc(Math.min(this.numPages, pso * this.numPages + 1)); + e.stopPropagation(); + } + @action + onThumbUp = (e: PointerEvent) => { + this._inThumb = false; + document.removeEventListener("pointermove", this.onThumbMove); + document.removeEventListener("pointerup", this.onThumbUp); + } + nativeWidth = () => NumCast(this.props.Document.nativeWidth); + nativeHeight = () => NumCast(this.props.Document.nativeHeight); private get uIButtons() { - let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().Scale); + let ratio = (this.curPage - 1) / this.numPages * 100; return ( - <div className="collectionPdfView-buttonTray" key="tray" style={{ transform: `scale(${scaling}, ${scaling})` }}> + <div className="collectionPdfView-buttonTray" key="tray" style={{ height: "100%" }}> <button className="collectionPdfView-backward" onClick={this.onPageBack}>{"<"}</button> <button className="collectionPdfView-forward" onClick={this.onPageForward}>{">"}</button> + <div className="collectionPdfView-slider" onPointerDown={this.onThumbDown} style={{ top: 60, left: -20, width: 50, height: `calc(100% - 80px)` }} > + <div className="collectionPdfView-thumb" onPointerDown={this.onThumbDown} style={{ top: `${ratio}%`, width: 50, height: 50 }} /> + </div> </div> ); } @@ -51,7 +77,7 @@ export class CollectionPDFView extends React.Component<FieldViewProps> { render() { return ( - <CollectionBaseView {...this.props} className="collectionPdfView-cont" onContextMenu={this.onContextMenu}> + <CollectionBaseView {...this.props} className={`collectionPdfView-cont${this._inThumb ? "-dragging" : ""}`} onContextMenu={this.onContextMenu}> {this.subView} </CollectionBaseView> ); diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 58d20819b..6add6ada8 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faCog, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked } from "mobx"; +import { action, computed, observable, untracked, runInAction } from "mobx"; import { observer } from "mobx-react"; import ReactTable, { CellInfo, ComponentPropsGetterR, ReactTableDefaults } from "react-table"; import { MAX_ROW_HEIGHT } from '../../views/globalCssVariables.scss'; @@ -58,7 +58,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @observable _newKeyName: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage); } - @computed get columns() { return Cast(this.props.Document.columns, listSpec("string"), []); } + @computed get columns() { return Cast(this.props.Document.schemaColumns, listSpec("string"), []); } @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } renderCell = (rowProps: CellInfo) => { @@ -156,9 +156,9 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action toggleKey = (key: string) => { - let list = Cast(this.props.Document.columns, listSpec("string")); + let list = Cast(this.props.Document.schemaColumns, listSpec("string")); if (list === undefined) { - this.props.Document.columns = list = new List<string>([key]); + this.props.Document.schemaColumns = list = new List<string>([key]); } else { const index = list.indexOf(key); if (index === -1) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss index 3b2f79be1..3e8a8a442 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.scss @@ -3,4 +3,10 @@ stroke-width: 3; transform: translate(10000px,10000px); pointer-events: all; +} +.collectionfreeformlinkview-linkCircle { + stroke: black; + stroke-width: 3; + transform: translate(10000px,10000px); + pointer-events: all; }
\ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index d4987fc18..3b700b053 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -4,20 +4,37 @@ import "./CollectionFreeFormLinkView.scss"; import React = require("react"); import v5 = require("uuid/v5"); import { StrCast, NumCast, BoolCast } from "../../../../new_fields/Types"; -import { Doc } from "../../../../new_fields/Doc"; +import { Doc, WidthSym, HeightSym } from "../../../../new_fields/Doc"; +import { InkingControl } from "../../InkingControl"; export interface CollectionFreeFormLinkViewProps { A: Doc; B: Doc; LinkDocs: Doc[]; + addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; + removeDocument: (document: Doc) => boolean; } @observer export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> { onPointerDown = (e: React.PointerEvent) => { - this.props.LinkDocs.map(l => - console.log("Link:" + StrCast(l.title))); + if (e.button === 0 && !InkingControl.Instance.selectedTool) { + let a = this.props.A; + let b = this.props.B; + let x1 = NumCast(a.x) + (BoolCast(a.isMinimized, false) ? 5 : a[WidthSym]() / 2); + let y1 = NumCast(a.y) + (BoolCast(a.isMinimized, false) ? 5 : a[HeightSym]() / 2); + let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : b[WidthSym]() / 2); + let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : b[HeightSym]() / 2); + this.props.LinkDocs.map(l => { + let width = l[WidthSym](); + l.x = (x1 + x2) / 2 - width / 2; + l.y = (y1 + y2) / 2 + 10; + if (!this.props.removeDocument(l)) this.props.addDocument(l, false); + }); + e.stopPropagation(); + e.preventDefault(); + } } render() { let l = this.props.LinkDocs; @@ -28,10 +45,14 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo let x2 = NumCast(b.x) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.width) / 2); let y2 = NumCast(b.y) + (BoolCast(b.isMinimized, false) ? 5 : NumCast(b.height) / 2); return ( - <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" onPointerDown={this.onPointerDown} - style={{ strokeWidth: `${l.length * 5}` }} - x1={`${x1}`} y1={`${y1}`} - x2={`${x2}`} y2={`${y2}`} /> + <> + <line key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" + style={{ strokeWidth: `${l.length * 5}` }} + x1={`${x1}`} y1={`${y1}`} + x2={`${x2}`} y2={`${y2}`} /> + <circle key={Utils.GenerateGuid()} className="collectionfreeformlinkview-linkLine" + cx={(x1 + x2) / 2} cy={(y1 + y2) / 2} r={10} onPointerDown={this.onPointerDown} /> + </> ); } }
\ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx index f693d55e8..e5dcf8c29 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx @@ -84,6 +84,14 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP if (containerDoc) { equalViews = DocumentManager.Instance.getDocumentViews(containerDoc.proto!); } + if (view.props.ContainingCollectionView) { + let collid = view.props.ContainingCollectionView.props.Document.Id; + Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []). + filter(child => + child.Id === collid).map(view => + DocumentManager.Instance.getDocumentViews(view).map(view => + equalViews.push(view))); + } return equalViews.filter(sv => sv.props.ContainingCollectionView && sv.props.ContainingCollectionView.props.Document === this.props.Document); } @@ -107,7 +115,8 @@ export class CollectionFreeFormLinksView extends React.Component<CollectionViewP ); return drawnPairs; }, [] as { a: Doc, b: Doc, l: Doc[] }[]); - return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} />); + return connections.map(c => <CollectionFreeFormLinkView key={Utils.GenerateGuid()} A={c.a} B={c.b} LinkDocs={c.l} + removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />); } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2689f48cd..f99800e21 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -24,6 +24,7 @@ import { FieldValue, Cast, NumCast } from "../../../../new_fields/Types"; import { pageSchema } from "../../nodes/ImageBox"; import { List } from "../../../../new_fields/List"; import { Id } from "../../../../new_fields/RefField"; +import { KeyStore } from "../../../../fields/KeyStore"; export const panZoomSchema = createSchema({ panX: "number", @@ -295,7 +296,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); } - private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.getDocumentViewProps(this.props.Document)} />]; + private childViews = () => [...this.views, <CollectionFreeFormBackgroundView key="backgroundView" {...this.props} {...this.getDocumentViewProps(this.props.Document)} />]; render() { trace(); const containerName = `collectionfreeformview${this.isAnnotationOverlay ? "-overlay" : "-container"}`; @@ -308,7 +309,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { {this.props.Document.Title} </text> </svg> */} - <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} + <MarqueeView container={this} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} isSelected={this.props.isSelected} addDocument={this.addDocument} removeDocument={this.props.removeDocument} addLiveTextDocument={this.addLiveTextBox} getContainerTransform={this.getContainerTransform} getTransform={this.getTransform}> <CollectionFreeFormViewPannableContents centeringShiftX={this.centeringShiftX} centeringShiftY={this.centeringShiftY} @@ -320,7 +321,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { </CollectionFreeFormLinksView> {/* <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" /> */} </CollectionFreeFormViewPannableContents> - <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} /> + <CollectionFreeFormOverlayView {...this.getDocumentViewProps(this.props.Document)} {...this.props} /> </MarqueeView> </div> ); @@ -332,7 +333,7 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> { @computed get overlayView() { let overlayLayout = Cast(this.props.Document.overlayLayout, "string", ""); return !overlayLayout ? (null) : - (<DocumentContentsView {...this.props} layoutKey={"overlayLayout"} + (<DocumentContentsView {...this.props} layoutKey={KeyStore.OverlayLayout} isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); } render() { @@ -341,12 +342,12 @@ class CollectionFreeFormOverlayView extends React.Component<DocumentViewProps> { } @observer -class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps> { +class CollectionFreeFormBackgroundView extends React.Component<DocumentViewProps & { isSelected: () => boolean }> { @computed get backgroundView() { let backgroundLayout = Cast(this.props.Document.backgroundLayout, "string", ""); return !backgroundLayout ? (null) : - (<DocumentContentsView {...this.props} layoutKey={"backgroundLayout"} - isTopMost={this.props.isTopMost} isSelected={returnFalse} select={emptyFunction} />); + (<DocumentContentsView {...this.props} layoutKey={KeyStore.BackgroundLayout} + isTopMost={this.props.isTopMost} isSelected={this.props.isSelected} select={emptyFunction} />); } render() { return this.backgroundView; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index df1e7a90e..1d3850a72 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -23,6 +23,7 @@ interface MarqueeViewProps { selectDocuments: (docs: Doc[]) => void; removeDocument: (doc: Doc) => boolean; addLiveTextDocument: (doc: Doc) => void; + isSelected: () => boolean; } @observer @@ -33,12 +34,13 @@ export class MarqueeView extends React.Component<MarqueeViewProps> @observable _downX: number = 0; @observable _downY: number = 0; @observable _visible: boolean = false; + _commandExecuted = false; @action cleanupInteractions = (all: boolean = false) => { if (all) { - document.removeEventListener("pointermove", this.onPointerMove, true); document.removeEventListener("pointerup", this.onPointerUp, true); + document.removeEventListener("pointermove", this.onPointerMove, true); } document.removeEventListener("keydown", this.marqueeCommand, true); this._visible = false; @@ -56,6 +58,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps> onPointerDown = (e: React.PointerEvent): void => { this._downX = this._lastX = e.pageX; this._downY = this._lastY = e.pageY; + this._commandExecuted = false; PreviewCursor.Visible = false; if ((CollectionFreeFormView.RIGHT_BTN_DRAG && e.button === 0 && !e.altKey && !e.metaKey && this.props.container.props.active()) || (!CollectionFreeFormView.RIGHT_BTN_DRAG && (e.button === 2 || (e.button === 0 && e.altKey)) && this.props.container.props.active())) { @@ -75,7 +78,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps> if (!e.cancelBubble) { if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) { - this._visible = true; + if (!this._commandExecuted) { + this._visible = true; + } e.stopPropagation(); e.preventDefault(); } @@ -104,7 +109,9 @@ export class MarqueeView extends React.Component<MarqueeViewProps> onClick = (e: React.MouseEvent): void => { if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress); + if (this.props.isSelected()) { + PreviewCursor.Show(e.clientX, e.clientY, this.onKeyPress); + } // let the DocumentView stopPropagation of this event when it selects this document } else { // why do we get a click event when the cursor have moved a big distance? // let's cut it off here so no one else has to deal with it. @@ -129,16 +136,21 @@ export class MarqueeView extends React.Component<MarqueeViewProps> @undoBatch @action marqueeCommand = (e: KeyboardEvent) => { - if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") { + if (this._commandExecuted) { + return; + } + if (e.key === "Backspace" || e.key === "Delete" || e.key == "d") { + this._commandExecuted = true; this.marqueeSelect().map(d => this.props.removeDocument(d)); let ink = Cast(this.props.container.props.Document.ink, InkField); if (ink) { this.marqueeInkDelete(ink.inkData); } - this.cleanupInteractions(true); + this.cleanupInteractions(false); e.stopPropagation(); } if (e.key === "c" || e.key === "r" || e.key === "e") { + this._commandExecuted = true; e.stopPropagation(); let bounds = this.Bounds; let selected = this.marqueeSelect().map(d => { @@ -157,7 +169,6 @@ export class MarqueeView extends React.Component<MarqueeViewProps> panX: 0, panY: 0, borderRounding: e.key === "e" ? -1 : undefined, - backgroundColor: selected.length ? "white" : "", scale: zoomBasis, width: bounds.width * zoomBasis, height: bounds.height * zoomBasis, @@ -166,7 +177,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps> }); this.marqueeInkDelete(inkData); - SelectionManager.DeselectAll(); + // SelectionManager.DeselectAll(); if (e.key === "r") { let summary = Docs.TextDocument({ x: bounds.left, y: bounds.top, width: 300, height: 100, backgroundColor: "yellow", title: "-summary-" }); Doc.MakeLink(summary.proto!, newCollection.proto!); @@ -176,9 +187,10 @@ export class MarqueeView extends React.Component<MarqueeViewProps> else { this.props.addDocument(newCollection, false); } - this.cleanupInteractions(true); + this.cleanupInteractions(false); } if (e.key === "s") { + this._commandExecuted = true; e.stopPropagation(); e.preventDefault(); let bounds = this.Bounds; @@ -188,7 +200,7 @@ export class MarqueeView extends React.Component<MarqueeViewProps> this.props.addLiveTextDocument(summary); selected.forEach(select => Doc.MakeLink(summary.proto!, select.proto!)); - this.cleanupInteractions(true); + this.cleanupInteractions(false); } } @action |
