From 4f1f20a52a5c01b3046a56f37b6c7c029d3e10f0 Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 13 Mar 2019 15:17:53 -0400 Subject: fixed a variety of things related to selection, dragging, marqueeing --- .../views/collections/CollectionVideoView.tsx | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/client/views/collections/CollectionVideoView.tsx (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx new file mode 100644 index 000000000..a6471f53c --- /dev/null +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -0,0 +1,50 @@ +import { action, computed } from "mobx"; +import { observer } from "mobx-react"; +import { Document } from "../../../fields/Document"; +import { KeyStore } from "../../../fields/KeyStore"; +import { ContextMenu } from "../ContextMenu"; +import { CollectionView, CollectionViewType } from "./CollectionView"; +import { CollectionViewProps } from "./CollectionViewBase"; +import React = require("react"); +import { FieldId } from "../../../fields/Field"; + + +@observer +export class CollectionVideoView extends React.Component { + + public static LayoutString(fieldKey: string = "DataKey") { + return `<${CollectionVideoView.name} Document={Document} + ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings} + isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`; + } + + public SelectedDocs: FieldId[] = [] + @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0; + @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0; + + @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); } + @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); } + // "inherited" CollectionView API starts here... + + public active: () => boolean = () => CollectionView.Active(this); + + addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); } + removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } + + specificContextMenu = (e: React.MouseEvent): void => { + if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } }); + } + } + + get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; } + get subView(): any { return CollectionView.SubView(this); } + + render() { + return (
+ {/* */} +
) + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 41f458fb4f383bfbf8d5b651189c2f4731262ff8 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 13 Mar 2019 20:33:51 -0400 Subject: added annotations for videos. added custom play controls for videos. --- src/client/documents/Documents.ts | 5 +- src/client/views/InkingCanvas.tsx | 6 +- .../views/collections/CollectionFreeFormView.tsx | 8 +-- src/client/views/collections/CollectionPDFView.tsx | 13 ++-- .../views/collections/CollectionVideoView.scss | 35 +++++++++ .../views/collections/CollectionVideoView.tsx | 83 +++++++++++++++++++--- src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- src/fields/KeyStore.ts | 1 - 10 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 src/client/views/collections/CollectionVideoView.scss (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f35d553b1..15e16a491 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -23,6 +23,7 @@ import { PDFField } from "../../fields/PDFField"; import { PDFBox } from "../views/nodes/PDFBox"; import { CollectionPDFView } from "../views/collections/CollectionPDFView"; import { RichTextField } from "../../fields/RichTextField"; +import { CollectionVideoView } from "../views/collections/CollectionVideoView"; export interface DocumentOptions { x?: number; @@ -138,9 +139,9 @@ export namespace Documents { } function GetVideoPrototype(): Document { if (!videoProto) { - videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionView.LayoutString("AnnotationsKey"), + videoProto = setupPrototypeOptions(videoProtoId, "VIDEO_PROTO", CollectionVideoView.LayoutString("AnnotationsKey"), { x: 0, y: 0, nativeWidth: 600, width: 300, layoutKeys: [KeyStore.Data, KeyStore.Annotations] }); - videoProto.SetNumber(KeyStore.CurFrame, 1); + videoProto.SetNumber(KeyStore.CurPage, 0); videoProto.SetText(KeyStore.BackgroundLayout, VideoBox.LayoutString()); } return videoProto; diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 0d87c1239..064d1cba2 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -74,7 +74,7 @@ export class InkingCanvas extends React.Component { color: InkingControl.Instance.selectedColor, width: InkingControl.Instance.selectedWidth, tool: InkingControl.Instance.selectedTool, - page: this.props.Document.GetNumber(KeyStore.CurPage, 0) + page: this.props.Document.GetNumber(KeyStore.CurPage, -1) }); this.inkData = data; this._isDrawing = true; @@ -145,11 +145,11 @@ export class InkingCanvas extends React.Component { // parse data from server let paths: Array = [] - let curPage = this.props.Document.GetNumber(KeyStore.CurPage, 0) + let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1) Array.from(lines).map(item => { let id = item[0]; let strokeData = item[1]; - if (strokeData.page == 0 || strokeData.page == curPage) + if (strokeData.page == -1 || strokeData.page == curPage) paths.push( { - var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1); + var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); const lvalue = this.props.Document.GetT>(this.props.fieldKey, ListField); let active: Document[] = []; if (lvalue && lvalue != FieldWaiting) { lvalue.Data.map(doc => { - var page = doc.GetNumber(KeyStore.Page, 0); - if (page == curPage || page == 0) { + var page = doc.GetNumber(KeyStore.Page, -1); + if (page == curPage || page == -1) { active.push(doc); } }) @@ -257,7 +257,7 @@ export class CollectionFreeFormView extends CollectionViewBase { @computed get views() { - var curPage = this.props.Document.GetNumber(KeyStore.CurPage, 1); + var curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1); const lvalue = this.props.Document.GetT>(this.props.fieldKey, ListField); if (lvalue && lvalue != FieldWaiting) { return lvalue.Data.map(doc => { diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index f22c07060..91ffc9500 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -18,13 +18,12 @@ export class CollectionPDFView extends React.Component { isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`; } - public SelectedDocs: FieldId[] = [] - @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0; - @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0; + private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, -1); } + private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); } + @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : -1; + @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1; - @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); } - @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); } - @computed private get uIButtons() { + private get uIButtons() { return (
@@ -33,7 +32,7 @@ export class CollectionPDFView extends React.Component { } // "inherited" CollectionView API starts here... - + public SelectedDocs: FieldId[] = [] public active: () => boolean = () => CollectionView.Active(this); addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); } diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss new file mode 100644 index 000000000..6c2f5a62a --- /dev/null +++ b/src/client/views/collections/CollectionVideoView.scss @@ -0,0 +1,35 @@ +.collectionVideoView-controls{ + width: 100%; + height: 100%; + position: absolute; +} +.collectionVideoView-time{ + color : white; + top :25px; + left : 25px; + position: absolute; + background-color: rgba(50, 50, 50, 0.2); +} +.collectionVideoView-play { + width: 25px; + height: 20px; + bottom: 25px; + left : 25px; + position: absolute; + color : white; + background-color: rgba(50, 50, 50, 0.2); + border-radius: 4px; + text-align: center; +} +.collectionVideoView-full { + width: 25px; + height: 20px; + bottom: 25px; + right : 25px; + position: absolute; + color : white; + background-color: rgba(50, 50, 50, 0.2); + border-radius: 4px; + text-align: center; + +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index a6471f53c..058325b21 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -1,4 +1,4 @@ -import { action, computed } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; @@ -7,6 +7,8 @@ import { CollectionView, CollectionViewType } from "./CollectionView"; import { CollectionViewProps } from "./CollectionViewBase"; import React = require("react"); import { FieldId } from "../../../fields/Field"; +import { ReplaceAroundStep } from "prosemirror-transform"; +import "./CollectionVideoView.scss" @observer @@ -18,14 +20,10 @@ export class CollectionVideoView extends React.Component { isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`; } - public SelectedDocs: FieldId[] = [] - @action onPageBack = () => this.curPage > 1 ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage - 1) : 0; - @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : 0; - - @computed private get curPage() { return this.props.Document.GetNumber(KeyStore.CurPage, 0); } - @computed private get numPages() { return this.props.Document.GetNumber(KeyStore.NumPages, 0); } + private _mainCont = React.createRef(); // "inherited" CollectionView API starts here... + public SelectedDocs: FieldId[] = [] public active: () => boolean = () => CollectionView.Active(this); addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); } @@ -40,11 +38,74 @@ export class CollectionVideoView extends React.Component { get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; } get subView(): any { return CollectionView.SubView(this); } + componentDidMount() { + this.repete(); + } + + player = (): HTMLVideoElement => { + return this._mainCont.current!.getElementsByTagName("video")[0]; + } + + @action + repete = () => { + if (this.player()) { + this.ctime = this.player().currentTime; + this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime)); + } + setTimeout(() => this.repete(), 100) + } + + + @observable + ctime: number = 0 + @observable + playing: boolean = false; + + @action + onPlayDown = () => { + if (this.player()) { + if (this.player().paused) { + this.player().play(); + this.playing = true; + } else { + this.player().pause(); + this.playing = false; + } + } + } + @action + onFullDown = (e: React.PointerEvent) => { + if (this.player()) { + this.player().requestFullscreen(); + e.stopPropagation(); + e.preventDefault(); + } + } + + @action + onResetDown = () => { + if (this.player()) { + this.player().pause(); + this.player().currentTime = 0; + } + + } + render() { - return (
- {/* */} + return (
+
+ {this.subView} +
+ {"" + Math.round(this.ctime)} + {" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)} +
+
+ {this.playing ? "\"" : ">"} +
+
+ F +
+
) } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 17a0fbd23..bbb4a5d83 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -50,7 +50,7 @@ export class CollectionView extends React.Component { @action public static AddDocument(props: CollectionViewProps, doc: Document) { - doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, 0)); + doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1)); if (props.Document.Get(props.fieldKey) instanceof Field) { //TODO This won't create the field if it doesn't already exist const value = props.Document.GetData(props.fieldKey, ListField, new Array()) diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 544af41a2..b0b5a63a4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -86,7 +86,7 @@ export class PDFBox extends React.Component { @observable private _interactive: boolean = false; @observable private _loaded: boolean = false; - @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, 0); } + @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); } componentDidMount() { this._reactionDisposer = reaction( diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 9c60d4348..053d9dd56 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -51,7 +51,7 @@ export class VideoBox extends React.Component {
{({ measureRef }) => -
diff --git a/src/client/views/collections/CollectionPDFView.scss b/src/client/views/collections/CollectionPDFView.scss new file mode 100644 index 000000000..28878c6fa --- /dev/null +++ b/src/client/views/collections/CollectionPDFView.scss @@ -0,0 +1,28 @@ +.collectionPdfView-buttonTray { + width: 100%; + height: 100%; + top : 25px; + left : 20px; + position: relative; + transform-origin: left top; + position: absolute; +} +.collectionPdfView-cont{ + width: 100%; + height: 100%; + position: absolute; +} +.collectionPdfView-backward { + color : white; + top :0px; + left : 0px; + position: absolute; + background-color: rgba(50, 50, 50, 0.2); +} +.collectionPdfView-forward { + color : white; + top :0px; + left : 25px; + 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 be3a281f2..124d82c8b 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -5,6 +5,7 @@ import { KeyStore } from "../../../fields/KeyStore"; import { ContextMenu } from "../ContextMenu"; import { CollectionView, CollectionViewType } from "./CollectionView"; import { CollectionViewProps } from "./CollectionViewBase"; +import "./CollectionPDFView.scss" import React = require("react"); import { FieldId } from "../../../fields/Field"; @@ -24,10 +25,11 @@ export class CollectionPDFView extends React.Component { @action onPageForward = () => this.curPage < this.numPages ? this.props.Document.SetNumber(KeyStore.CurPage, this.curPage + 1) : -1; private get uIButtons() { + let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); return ( -
- - +
+ +
); } @@ -49,7 +51,7 @@ export class CollectionPDFView extends React.Component { get subView(): any { return CollectionView.SubView(this); } render() { - return (
+ return (
{this.subView} {this.props.isSelected() ? this.uIButtons : (null)}
) diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss index 6c2f5a62a..ec3c0327d 100644 --- a/src/client/views/collections/CollectionVideoView.scss +++ b/src/client/views/collections/CollectionVideoView.scss @@ -1,4 +1,9 @@ -.collectionVideoView-controls{ +.collectionVideoView-buttonTray{ + width: 100%; + height: 100%; + position: absolute; +} +.collectionVideoView-cont{ width: 100%; height: 100%; position: absolute; @@ -9,6 +14,7 @@ left : 25px; position: absolute; background-color: rgba(50, 50, 50, 0.2); + transform-origin: left top; } .collectionVideoView-play { width: 25px; @@ -20,6 +26,7 @@ background-color: rgba(50, 50, 50, 0.2); border-radius: 4px; text-align: center; + transform-origin: left bottom; } .collectionVideoView-full { width: 25px; @@ -31,5 +38,6 @@ background-color: rgba(50, 50, 50, 0.2); border-radius: 4px; text-align: center; + transform-origin: right bottom; } \ No newline at end of file diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 9856a1284..9b01c23c9 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -7,7 +7,6 @@ import { CollectionView, CollectionViewType } from "./CollectionView"; import { CollectionViewProps } from "./CollectionViewBase"; import React = require("react"); import { FieldId } from "../../../fields/Field"; -import { ReplaceAroundStep } from "prosemirror-transform"; import "./CollectionVideoView.scss" @@ -21,6 +20,25 @@ export class CollectionVideoView extends React.Component { } private _mainCont = React.createRef(); + + private get uIButtons() { + let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); + return ( +
+
+ {"" + Math.round(this.ctime)} + {" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)} +
+
+ {this.playing ? "\"" : ">"} +
+
+ F +
+
); + } + + // "inherited" CollectionView API starts here... @observable @@ -40,20 +58,20 @@ export class CollectionVideoView extends React.Component { get subView(): any { return CollectionView.SubView(this); } componentDidMount() { - this.repete(); + this.updateTimecode(); } - player = (): HTMLVideoElement => { - return this._mainCont.current!.getElementsByTagName("video")[0]; + get player(): HTMLVideoElement | undefined { + return this._mainCont.current ? this._mainCont.current.getElementsByTagName("video")[0] : undefined; } @action - repete = () => { - if (this.player()) { - this.ctime = this.player().currentTime; + updateTimecode = () => { + if (this.player) { + this.ctime = this.player.currentTime; this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime)); } - setTimeout(() => this.repete(), 100) + setTimeout(() => this.updateTimecode(), 100) } @@ -64,20 +82,20 @@ export class CollectionVideoView extends React.Component { @action onPlayDown = () => { - if (this.player()) { - if (this.player().paused) { - this.player().play(); + if (this.player) { + if (this.player.paused) { + this.player.play(); this.playing = true; } else { - this.player().pause(); + this.player.pause(); this.playing = false; } } } @action onFullDown = (e: React.PointerEvent) => { - if (this.player()) { - this.player().requestFullscreen(); + if (this.player) { + this.player.requestFullscreen(); e.stopPropagation(); e.preventDefault(); } @@ -85,28 +103,17 @@ export class CollectionVideoView extends React.Component { @action onResetDown = () => { - if (this.player()) { - this.player().pause(); - this.player().currentTime = 0; + if (this.player) { + this.player.pause(); + this.player.currentTime = 0; } } render() { return (
-
- {this.subView} -
- {"" + Math.round(this.ctime)} - {" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)} -
-
- {this.playing ? "\"" : ">"} -
-
- F -
-
+ {this.subView} + {this.uIButtons}
) } } \ No newline at end of file diff --git a/src/client/views/collections/PreviewCursor.tsx b/src/client/views/collections/PreviewCursor.tsx index ab68fbc46..a1411250a 100644 --- a/src/client/views/collections/PreviewCursor.tsx +++ b/src/client/views/collections/PreviewCursor.tsx @@ -16,7 +16,7 @@ import { CollectionFreeFormView } from "./CollectionFreeFormView"; export interface PreviewCursorProps { getTransform: () => Transform; container: CollectionFreeFormView; - addLiveTextDocuemnt: (doc: Document) => void; + addLiveTextDocument: (doc: Document) => void; } @observer @@ -57,11 +57,13 @@ export class PreviewCursor extends React.Component { @action onKeyPress = (e: KeyboardEvent) => { //if not these keys, make a textbox if preview cursor is active! - if (!e.ctrlKey && !e.altKey) { + if (!e.ctrlKey && !e.altKey && !e.defaultPrevented) { //make textbox and add it to this collection let [x, y] = this.props.getTransform().transformPoint(this._lastX, this._lastY); let newBox = Documents.TextDocument({ width: 200, height: 100, x: x, y: y, title: "new" }); - this.props.addLiveTextDocuemnt(newBox); + this.props.addLiveTextDocument(newBox); + e.stopPropagation(); + e.preventDefault(); } } diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 9f92410d4..ad947afd5 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -11,5 +11,5 @@ } .pdfBox-contentContainer { position: absolute; - transform-origin: "left top"; + transform-origin: left top; } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 05b23c6b94b47393ba4d34a393e5080f20aff099 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 14 Mar 2019 19:14:07 -0400 Subject: fixed scrollzooming on videos. --- .../views/collections/CollectionVideoView.scss | 6 +----- .../views/collections/CollectionVideoView.tsx | 23 +++++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/views/collections/CollectionVideoView.scss b/src/client/views/collections/CollectionVideoView.scss index ec3c0327d..ddcd74616 100644 --- a/src/client/views/collections/CollectionVideoView.scss +++ b/src/client/views/collections/CollectionVideoView.scss @@ -1,8 +1,4 @@ -.collectionVideoView-buttonTray{ - width: 100%; - height: 100%; - position: absolute; -} + .collectionVideoView-cont{ width: 100%; height: 100%; diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 9b01c23c9..0e2d250aa 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -23,19 +23,18 @@ export class CollectionVideoView extends React.Component { private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); - return ( -
-
- {"" + Math.round(this.ctime)} - {" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)} + return ([ +
+ {"" + Math.round(this.ctime)} + {" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)} +
, +
+ {this.playing ? "\"" : ">"} +
, +
+ F
-
- {this.playing ? "\"" : ">"} -
-
- F -
-
); + ]); } -- cgit v1.2.3-70-g09d2 From f089e10dfd42edc395729a6b71e42385745f364e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 14 Mar 2019 22:21:16 -0400 Subject: marquees can now delete ink or move it to a new collection. --- src/client/documents/Documents.ts | 3 ++ src/client/views/InkingCanvas.scss | 3 +- src/client/views/InkingCanvas.tsx | 22 +++++----- src/client/views/InkingStroke.tsx | 2 - src/client/views/Main.tsx | 2 +- .../views/collections/CollectionFreeFormView.tsx | 4 +- .../views/collections/CollectionVideoView.tsx | 6 +-- src/client/views/collections/MarqueeView.tsx | 47 +++++++++++++++++----- 8 files changed, 63 insertions(+), 26 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4e0eefcc6..8402118b8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -24,10 +24,12 @@ import { PDFBox } from "../views/nodes/PDFBox"; import { CollectionPDFView } from "../views/collections/CollectionPDFView"; import { RichTextField } from "../../fields/RichTextField"; import { CollectionVideoView } from "../views/collections/CollectionVideoView"; +import { StrokeData, InkField } from "../../fields/InkField"; export interface DocumentOptions { x?: number; y?: number; + ink?: Map; width?: number; height?: number; nativeWidth?: number; @@ -87,6 +89,7 @@ export namespace Documents { if (options.backgroundColor !== undefined) { doc.SetText(KeyStore.BackgroundColor, options.backgroundColor); } if (options.layout !== undefined) { doc.SetText(KeyStore.Layout, options.layout); } if (options.layoutKeys !== undefined) { doc.Set(KeyStore.LayoutKeys, new ListField(options.layoutKeys)); } + if (options.ink !== undefined) { doc.Set(KeyStore.Ink, new InkField(options.ink)); } return doc; } function setupPrototypeOptions(protoId: string, title: string, layout: string, options: DocumentOptions): Document { diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index f654b194b..37fa3143d 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -1,8 +1,9 @@ .inking-canvas { - position: fixed; + position: absolute; top: -50000px; left: -50000px; // z-index: 99; //overlays ink on top of everything svg { + position:absolute; width: 100000px; height: 100000px; .highlight { diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index 064d1cba2..84c47f616 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -1,4 +1,5 @@ import { observer } from "mobx-react"; +import { observable } from "mobx"; import { action, computed } from "mobx"; import { InkingControl } from "./InkingControl"; import React = require("react"); @@ -6,14 +7,10 @@ import { Transform } from "../util/Transform"; import { Document } from "../../fields/Document"; import { KeyStore } from "../../fields/KeyStore"; import { InkField, InkTool, StrokeData, StrokeMap } from "../../fields/InkField"; -import { JsxArgs } from "./nodes/DocumentView"; import { InkingStroke } from "./InkingStroke"; import "./InkingCanvas.scss" -import { CollectionDockingView } from "./collections/CollectionDockingView"; import { Utils } from "../../Utils"; import { FieldWaiting } from "../../fields/Field"; -import { getMapLikeKeys } from "mobx/lib/internal"; - interface InkCanvasProps { getScreenTransform: () => Transform; @@ -22,7 +19,16 @@ interface InkCanvasProps { @observer export class InkingCanvas extends React.Component { - + static InkOffset: number = 50000; + public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean { + let inside = false; + stroke.pathData.map(val => { + if (selRect.left < val.x - InkingCanvas.InkOffset && selRect.left + selRect.width > val.x - InkingCanvas.InkOffset && + selRect.top < val.y - InkingCanvas.InkOffset && selRect.top + selRect.height > val.y - InkingCanvas.InkOffset) + inside = true; + }); + return inside + } private _isDrawing: boolean = false; private _idGenerator: string = ""; @@ -51,7 +57,6 @@ export class InkingCanvas extends React.Component { document.removeEventListener("mouseup", this.handleMouseUp); } - @action handleMouseDown = (e: React.PointerEvent): void => { if (e.button != 0 || @@ -62,7 +67,6 @@ export class InkingCanvas extends React.Component { if (InkingControl.Instance.selectedTool === InkTool.Eraser) { return } - e.stopPropagation() const point = this.relativeCoordinatesForEvent(e); // start the new line, saves a uuid to represent the field of the stroke @@ -110,8 +114,8 @@ export class InkingCanvas extends React.Component { relativeCoordinatesForEvent = (e: React.MouseEvent): { x: number, y: number } => { let [x, y] = this.props.getScreenTransform().transformPoint(e.clientX, e.clientY); - x += 50000 - y += 50000 + x += InkingCanvas.InkOffset; + y += InkingCanvas.InkOffset; return { x, y }; } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index d724421d3..87b5c43d8 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -21,8 +21,6 @@ export class InkingStroke extends React.Component { @observable private _strokeColor: string = this.props.color; @observable private _strokeWidth: string = this.props.width; - private _canvasColor: string = "#cdcdcd"; - deleteStroke = (e: React.MouseEvent): void => { if (InkingControl.Instance.selectedTool === InkTool.Eraser && e.buttons === 1) { this.props.deleteCallback(this.props.id); diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 8ee028076..d73e9b8fe 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -58,7 +58,7 @@ Documents.initProtos(mainDocId, (res?: Document) => { let videourl = "http://techslides.com/demos/sample-videos/small.mp4"; let clearDatabase = action(() => Utils.Emit(Server.Socket, MessageStore.DeleteAll, {})) let addTextNode = action(() => Documents.TextDocument({ width: 200, height: 200, title: "a text note" })) - let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, backgroundColor: "Transparent", title: "a freeform collection" })); + let addColNode = action(() => Documents.FreeformDocument([], { width: 200, height: 200, title: "a freeform collection" })); let addSchemaNode = action(() => Documents.SchemaDocument([Documents.TextDocument()], { width: 200, height: 200, title: "a schema collection" })); let addVideoNode = action(() => Documents.VideoDocument(videourl, { width: 200, title: "video node" })); let addPDFNode = action(() => Documents.PdfDocument(pdfurl, { width: 200, title: "a schema collection" })); diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 98fb23fa4..c8b745254 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -338,7 +338,9 @@ export class CollectionFreeFormView extends CollectionViewBase { {this.views}
- + {this.overlayView}
); diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 0e2d250aa..b64ef3c07 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -24,14 +24,14 @@ export class CollectionVideoView extends React.Component { private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); return ([ -
+
{"" + Math.round(this.ctime)} {" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)}
, -
+
{this.playing ? "\"" : ">"}
, -
+
F
]); diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx index d9e4df1e9..67716f99a 100644 --- a/src/client/views/collections/MarqueeView.tsx +++ b/src/client/views/collections/MarqueeView.tsx @@ -3,21 +3,22 @@ import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { FieldWaiting, Opt } from "../../../fields/Field"; import { KeyStore } from "../../../fields/KeyStore"; -import { ListField } from "../../../fields/ListField"; import { Documents } from "../../documents/Documents"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; import "./MarqueeView.scss"; import React = require("react"); - +import { InkField, StrokeData } from "../../../fields/InkField"; +import { Utils } from "../../../Utils"; +import { InkingCanvas } from "../InkingCanvas"; interface MarqueeViewProps { getMarqueeTransform: () => Transform; getTransform: () => Transform; container: CollectionFreeFormView; addDocument: (doc: Document) => void; - activeDocuemnts: () => Document[]; + activeDocuments: () => Document[]; selectDocuments: (docs: Document[]) => void; removeDocument: (doc: Document) => boolean; } @@ -94,29 +95,57 @@ export class MarqueeView extends React.Component marqueeCommand = (e: KeyboardEvent) => { if (e.key == "Backspace") { this.marqueeSelect().map(d => this.props.removeDocument(d)); + this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField); this.cleanupInteractions(); } if (e.key == "c") { let bounds = this.Bounds; - let selected = this.marqueeSelect().map(m => m); - this.marqueeSelect().map(d => this.props.removeDocument(d)); - //setTimeout(() => { - this.props.addDocument(Documents.FreeformDocument(selected.map(d => { + let selected = this.marqueeSelect().map(d => { + this.props.removeDocument(d); d.SetNumber(KeyStore.X, d.GetNumber(KeyStore.X, 0) - bounds.left - bounds.width / 2); d.SetNumber(KeyStore.Y, d.GetNumber(KeyStore.Y, 0) - bounds.top - bounds.height / 2); d.SetNumber(KeyStore.Page, 0); d.SetText(KeyStore.Title, "" + d.GetNumber(KeyStore.Width, 0) + " " + d.GetNumber(KeyStore.Height, 0)); return d; - }), { x: bounds.left, y: bounds.top, panx: 0, pany: 0, width: bounds.width, height: bounds.height, title: "a nested collection" })); + }); + let liftedInk = this.marqueeInkSelect(true); + this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField); + //setTimeout(() => { + this.props.addDocument(Documents.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panx: 0, pany: 0, width: bounds.width, backgroundColor: "Transparent", height: bounds.height, ink: liftedInk, title: "a nested collection" })); // }, 100); this.cleanupInteractions(); } } + marqueeInkSelect(select: boolean) { + let selRect = this.Bounds; + let centerShiftX = 0 - (selRect.left + selRect.width / 2); // moves each point by the offset that shifts the selection's center to the origin. + let centerShiftY = 0 - (selRect.top + selRect.height / 2); + let ink = this.props.container.props.Document.GetT(KeyStore.Ink, InkField); + if (ink && ink != FieldWaiting) { + let idata = new Map(); + ink.Data.forEach((value: StrokeData, key: string, map: any) => { + let inside = InkingCanvas.IntersectStrokeRect(value, selRect); + if (inside && select) { + idata.set(key, + { + pathData: value.pathData.map(val => { return { x: val.x + centerShiftX, y: val.y + centerShiftY } }), + color: value.color, + width: value.width, + tool: value.tool, + page: -1 + }); + } else if (!inside && !select) { + idata.set(key, value); + } + }) + return idata; + } + } marqueeSelect() { let selRect = this.Bounds; let selection: Document[] = []; - this.props.activeDocuemnts().map(doc => { + this.props.activeDocuments().map(doc => { var x = doc.GetNumber(KeyStore.X, 0); var y = doc.GetNumber(KeyStore.Y, 0); var w = doc.GetNumber(KeyStore.Width, 0); -- cgit v1.2.3-70-g09d2 From 0b6aa6e7fb31ce0f8085bfee77130f4a276cda80 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 18 Mar 2019 09:35:54 -0400 Subject: tweaks --- src/client/views/collections/CollectionFreeFormView.scss | 4 ++-- src/client/views/collections/CollectionFreeFormView.tsx | 2 +- src/client/views/collections/CollectionVideoView.tsx | 2 +- src/fields/ListField.ts | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/views/collections/CollectionFreeFormView.scss b/src/client/views/collections/CollectionFreeFormView.scss index 9c7a03b52..79ca5fccd 100644 --- a/src/client/views/collections/CollectionFreeFormView.scss +++ b/src/client/views/collections/CollectionFreeFormView.scss @@ -8,11 +8,11 @@ } //nested freeform views - .collectionfreeformview-container { + //.collectionfreeformview-container { // background-image: linear-gradient(to right, $light-color-secondary 1px, transparent 1px), // linear-gradient(to bottom, $light-color-secondary 1px, transparent 1px); // background-size: 30px 30px; - } + //} border: 0px solid $light-color-secondary; border-radius: $border-radius; diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 808a22a5d..728076ef8 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -304,7 +304,7 @@ export class CollectionFreeFormView extends CollectionViewBase { onDrop={this.onDrop.bind(this)} onDragOver={this.onDragOver} onBlur={this.onBlur} - style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px`, }} + style={{ borderWidth: `${COLLECTION_BORDER_WIDTH}px` }}// , zIndex: !this.props.isTopMost ? -1 : undefined }} tabIndex={0} ref={this.createDropTarget}>
{ render() { return (
{this.subView} - {this.uIButtons} + {this.props.isSelected() ? this.uIButtons : (null)}
) } } \ No newline at end of file diff --git a/src/fields/ListField.ts b/src/fields/ListField.ts index 77c1d6e14..4527ee548 100644 --- a/src/fields/ListField.ts +++ b/src/fields/ListField.ts @@ -20,6 +20,9 @@ export class ListField extends BasicField { private observeDisposer: Lambda | undefined; private observeList(): void { + if (this.observeDisposer) { + this.observeDisposer() + } this.observeDisposer = observe(this.Data as IObservableArray, (change: IArrayChange | IArraySplice) => { this.updateProxies() if (change.type == "splice") { @@ -39,9 +42,6 @@ export class ListField extends BasicField { } protected setData(value: T[]) { - if (this.observeDisposer) { - this.observeDisposer() - } this.data = observable(value); this.updateProxies(); this.observeList(); -- cgit v1.2.3-70-g09d2 From 8418b842b16da68150243e1750aa8b40eb8a8792 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 18 Mar 2019 11:48:24 -0400 Subject: fixed removing from schemas. fixed dragging link button to move quickly. cleaned up drag/drop code a little --- src/client/util/DragManager.ts | 12 +++----- src/client/views/DocumentDecorations.tsx | 14 ++++----- src/client/views/InkingControl.tsx | 3 +- .../views/collections/CollectionFreeFormView.tsx | 4 +-- src/client/views/collections/CollectionPDFView.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 4 +-- .../views/collections/CollectionVideoView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 7 +++-- .../views/collections/CollectionViewBase.tsx | 36 ++++++++-------------- src/client/views/collections/MarqueeView.tsx | 15 +++++++-- src/client/views/nodes/DocumentView.tsx | 16 +++++----- 11 files changed, 57 insertions(+), 58 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index c0abec407..54784f2d2 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -2,18 +2,18 @@ import { DocumentDecorations } from "../views/DocumentDecorations"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { Document } from "../../fields/Document" import { action } from "mobx"; -import { DocumentView } from "../views/nodes/DocumentView"; import { ImageField } from "../../fields/ImageField"; import { KeyStore } from "../../fields/KeyStore"; +import { CollectionView } from "../views/collections/CollectionView"; -export function setupDrag(_reference: React.RefObject, docFunc: () => Document) { +export function setupDrag(_reference: React.RefObject, docFunc: () => Document, removeFunc: (containingCollection: CollectionView) => void = () => { }) { let onRowMove = action((e: PointerEvent): void => { e.stopPropagation(); e.preventDefault(); document.removeEventListener("pointermove", onRowMove); document.removeEventListener('pointerup', onRowUp); - DragManager.StartDrag(_reference.current!, { document: docFunc() }); + DragManager.StartDrag(_reference.current!, { draggedDocument: docFunc(), removeDocument: removeFunc }); }); let onRowUp = action((e: PointerEvent): void => { document.removeEventListener("pointermove", onRowMove); @@ -122,9 +122,7 @@ export namespace DragManager { // bcz: PDFs don't show up if you clone them because they contain a canvas. // however, PDF's have a thumbnail field that contains an image of their canvas. // So we replace the pdf's canvas with the image thumbnail - const docView: DocumentView = dragData["documentView"]; - const doc: Document = dragData["document"]; - + const doc: Document = dragData["draggedDocument"]; if (doc) { var pdfBox = dragElement.getElementsByClassName("pdfBox-cont")[0] as HTMLElement; let thumbnail = doc.GetT(KeyStore.Thumbnail, ImageField); @@ -138,7 +136,6 @@ export namespace DragManager { } } - dragDiv.appendChild(dragElement); let hideSource = false; @@ -191,6 +188,7 @@ export namespace DragManager { if (!target) { return; } + dragData["droppedDocument"] = dragData["aliasOnDrop"] ? (dragData["draggedDocument"] as Document).CreateAlias() : dragData["draggedDocument"]; target.dispatchEvent(new CustomEvent("dashOnDrop", { bubbles: true, detail: { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3bdb7d5b3..1145fbe7f 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -77,7 +77,6 @@ export class DocumentDecorations extends React.Component { } onLinkButtonUp = (e: PointerEvent): void => { - console.log("up"); document.removeEventListener("pointermove", this.onLinkButtonMoved) document.removeEventListener("pointerup", this.onLinkButtonUp) e.stopPropagation(); @@ -85,10 +84,11 @@ export class DocumentDecorations extends React.Component { onLinkButtonMoved = (e: PointerEvent): void => { - console.log("moved"); - let dragData: { [id: string]: any } = {}; - dragData["linkSourceDoc"] = SelectionManager.SelectedDocuments()[0]; if (this._linkButton.current != null) { + document.removeEventListener("pointermove", this.onLinkButtonMoved) + document.removeEventListener("pointerup", this.onLinkButtonUp) + let dragData: { [id: string]: any } = {}; + dragData["linkSourceDoc"] = SelectionManager.SelectedDocuments()[0]; DragManager.StartDrag(this._linkButton.current, dragData, { handlers: { dragComplete: action(() => { }), @@ -96,8 +96,6 @@ export class DocumentDecorations extends React.Component { hideSource: false }) } - document.removeEventListener("pointermove", this.onLinkButtonMoved) - document.removeEventListener("pointerup", this.onLinkButtonUp) e.stopPropagation(); } @@ -217,7 +215,7 @@ export class DocumentDecorations extends React.Component { }> -
{linkCount}
+
{linkCount}
); } return ( @@ -237,7 +235,7 @@ export class DocumentDecorations extends React.Component {
e.preventDefault()}>
e.preventDefault()}>
-
{linkButton}
+
{linkButton}
) diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 6616f68d8..ad6bbd476 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -11,6 +11,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPen, faHighlighter, faEraser, faBan } from '@fortawesome/free-solid-svg-icons'; import { SelectionManager } from "../util/SelectionManager"; import { KeyStore } from "../../fields/KeyStore"; +import { TextField } from "../../fields/TextField"; library.add(faPen, faHighlighter, faEraser, faBan); @@ -39,7 +40,7 @@ export class InkingControl extends React.Component { if (SelectionManager.SelectedDocuments().length == 1) { var sdoc = SelectionManager.SelectedDocuments()[0]; if (sdoc.props.ContainingCollectionView && sdoc.props.ContainingCollectionView) { - sdoc.props.Document.SetText(KeyStore.BackgroundColor, color.hex); + sdoc.props.Document.SetOnPrototype(KeyStore.BackgroundColor, new TextField(color.hex)); } } } diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index 728076ef8..85ea2d121 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -28,7 +28,7 @@ export class CollectionFreeFormView extends CollectionViewBase { // mark this collection so that when the text box is created we can send it the SelectOnLoad prop to focus itself this._selectOnLoaded = newBox.Id; //set text to be the typed key and get focus on text box - this.props.addDocument(newBox); + this.props.addDocument(newBox, false); //remove cursor from screen this.PreviewCursorVisible = false; } @@ -79,7 +79,7 @@ export class CollectionFreeFormView extends CollectionViewBase { let screenX = de.x - (de.data["xOffset"] as number || 0); let screenY = de.y - (de.data["yOffset"] as number || 0); const [x, y] = this.getTransform().transformPoint(screenX, screenY); - let doc: Document = de.data["document"]; + let doc: Document = de.data["droppedDocument"]; if (doc) { doc.SetNumber(KeyStore.X, x); doc.SetNumber(KeyStore.Y, y); diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index 124d82c8b..e64b4c945 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -38,7 +38,7 @@ export class CollectionPDFView extends React.Component { public SelectedDocs: FieldId[] = [] public active: () => boolean = () => CollectionView.Active(this); - addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); } + addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); } removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } specificContextMenu = (e: React.MouseEvent): void => { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index f88a62a0f..ced08a14e 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -14,7 +14,7 @@ import { EditableView } from "../EditableView"; import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from "../nodes/FieldView"; import "./CollectionSchemaView.scss"; -import { COLLECTION_BORDER_WIDTH } from "./CollectionView"; +import { COLLECTION_BORDER_WIDTH, CollectionView } from "./CollectionView"; import { CollectionViewBase } from "./CollectionViewBase"; import { setupDrag } from "../../util/DragManager"; @@ -47,7 +47,7 @@ export class CollectionSchemaView extends CollectionViewBase { ) let reference = React.createRef(); - let onItemDown = setupDrag(reference, () => props.doc); + let onItemDown = setupDrag(reference, () => props.doc, (containingCollection: CollectionView) => this.props.removeDocument(props.doc)); return (
{ public SelectedDocs: FieldId[] = [] public active: () => boolean = () => CollectionView.Active(this); - addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); } + addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); } removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } specificContextMenu = (e: React.MouseEvent): void => { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 40acf466e..303099c16 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -36,7 +36,7 @@ export class CollectionView extends React.Component { @observable public SelectedDocs: FieldId[] = []; public active: () => boolean = () => CollectionView.Active(this); - addDocument = (doc: Document): void => { CollectionView.AddDocument(this.props, doc); } + addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); } removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } get subView() { return CollectionView.SubView(this); } @@ -48,12 +48,13 @@ export class CollectionView extends React.Component { } @action - public static AddDocument(props: CollectionViewProps, doc: Document) { + public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean) { doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1)); if (props.Document.Get(props.fieldKey) instanceof Field) { //TODO This won't create the field if it doesn't already exist const value = props.Document.GetData(props.fieldKey, ListField, new Array()) - value.push(doc); + if (!value.some(v => v.Id == doc.Id) || allowDuplicates) + value.push(doc); } else { props.Document.SetOnPrototype(props.fieldKey, new ListField([doc])); } diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index 3d1a76a07..0358b5907 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -27,7 +27,7 @@ export interface CollectionViewProps { } export interface SubCollectionViewProps extends CollectionViewProps { active: () => boolean; - addDocument: (doc: Document) => void; + addDocument: (doc: Document, allowDuplicates: boolean) => void; removeDocument: (doc: Document) => boolean; CollectionView: CollectionView; } @@ -46,28 +46,16 @@ export class CollectionViewBase extends React.Component @undoBatch @action protected drop(e: Event, de: DragManager.DropEvent) { - let docToAlias = de.data["documentToAlias"]; - let docView = de.data["documentView"]; - let doc = docToAlias ? docToAlias.CreateAlias() : de.data["document"]; - if (docToAlias) { - [KeyStore.Width, KeyStore.Height].map(key => - docToAlias.GetTAsync(key, NumberField, (f: Opt) => { - if (f) { - doc.SetNumber(key, f.Data) - } - }) - ); - de.data["document"] = doc; - this.props.addDocument(doc); - } else if (docView) { - if (doc && docView.props.RemoveDocument && docView.props.ContainingCollectionView !== this.props.CollectionView) { - docView.props.RemoveDocument(doc); - this.props.removeDocument(doc); // bcz: not good -- want to check if it's there and then add if it isn't - this.props.addDocument(doc); + let dropped = de.data["droppedDocument"]; + if (dropped) { + if (de.data["aliasOnDrop"]) { + let dragged = de.data["draggedDocument"]; + [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key => + dragged.GetTAsync(key, NumberField, (f: Opt) => f ? dropped.SetNumber(key, f.Data) : null)); + } else if (de.data["removeDocument"]) { + de.data["removeDocument"](this.props.CollectionView); } - } else if (doc) { - this.props.removeDocument(doc); // bcz: not good -- want to check if it's there and then add if it isn't - this.props.addDocument(doc); + this.props.addDocument(dropped, false); } e.stopPropagation(); } @@ -89,7 +77,7 @@ export class CollectionViewBase extends React.Component console.log("not good"); let htmlDoc = Documents.HtmlDocument(html, { ...options, width: 300, height: 300 }); htmlDoc.SetText(KeyStore.DocumentText, text); - this.props.addDocument(htmlDoc); + this.props.addDocument(htmlDoc, false); return; } @@ -99,7 +87,7 @@ export class CollectionViewBase extends React.Component const upload = window.location.origin + "/upload"; let item = e.dataTransfer.items[i]; if (item.kind === "string" && item.type.indexOf("uri") != -1) { - e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options)))) + e.dataTransfer.items[i].getAsString(action((s: string) => this.props.addDocument(Documents.WebDocument(s, options), false))) } let type = item.type console.log(type) diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx index f5c83a934..8c2f3443c 100644 --- a/src/client/views/collections/MarqueeView.tsx +++ b/src/client/views/collections/MarqueeView.tsx @@ -17,7 +17,7 @@ interface MarqueeViewProps { getMarqueeTransform: () => Transform; getTransform: () => Transform; container: CollectionFreeFormView; - addDocument: (doc: Document) => void; + addDocument: (doc: Document, allowDuplicates: false) => void; activeDocuments: () => Document[]; selectDocuments: (docs: Document[]) => void; removeDocument: (doc: Document) => boolean; @@ -111,7 +111,18 @@ export class MarqueeView extends React.Component let liftedInk = this.marqueeInkSelect(true); this.props.container.props.Document.SetData(KeyStore.Ink, this.marqueeInkSelect(false), InkField); //setTimeout(() => { - this.props.addDocument(Documents.FreeformDocument(selected, { x: bounds.left, y: bounds.top, panx: 0, pany: 0, width: bounds.width, backgroundColor: "Transparent", height: bounds.height, ink: liftedInk, title: "a nested collection" })); + let newCollection = Documents.FreeformDocument(selected, { + x: bounds.left, + y: bounds.top, + panx: 0, + pany: 0, + width: bounds.width, + height: bounds.height, + backgroundColor: "Transparent", + ink: liftedInk, + title: "a nested collection" + }); + this.props.addDocument(newCollection, false); // }, 100); this.cleanupInteractions(); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index fbd58ef7e..e98815fa6 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -23,7 +23,7 @@ import React = require("react"); export interface DocumentViewProps { ContainingCollectionView: Opt; Document: Document; - AddDocument?: (doc: Document) => void; + AddDocument?: (doc: Document, allowDuplicates: boolean) => void; RemoveDocument?: (doc: Document) => boolean; ScreenToLocalTransform: () => Transform; isTopMost: boolean; @@ -164,12 +164,15 @@ export class DocumentView extends React.Component { if (this._mainCont.current) { const [left, top] = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); let dragData: { [id: string]: any } = {}; - dragData["documentView"] = this; - if (dropAliasOfDraggedDoc) - dragData["documentToAlias"] = this.props.Document; - dragData["document"] = this.props.Document; + dragData["aliasOnDrop"] = dropAliasOfDraggedDoc; + dragData["draggedDocument"] = this.props.Document; dragData["xOffset"] = x - left; dragData["yOffset"] = y - top; + dragData["removeDocument"] = (dropCollectionView: CollectionView) => { + if (this.props.RemoveDocument && this.props.ContainingCollectionView !== dropCollectionView) { + this.props.RemoveDocument(this.props.Document); + } + } DragManager.StartDrag(this._mainCont.current, dragData, { handlers: { dragComplete: action(() => { }), @@ -213,7 +216,7 @@ export class DocumentView extends React.Component { fieldsClicked = (e: React.MouseEvent): void => { if (this.props.AddDocument) { - this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 })); + this.props.AddDocument(Documents.KVPDocument(this.props.Document, { width: 300, height: 300 }), false); } } fullScreenClicked = (e: React.MouseEvent): void => { @@ -232,7 +235,6 @@ export class DocumentView extends React.Component { @action drop = (e: Event, de: DragManager.DropEvent) => { - console.log("drop"); const sourceDocView: DocumentView = de.data["linkSourceDoc"]; if (!sourceDocView) { return; -- cgit v1.2.3-70-g09d2 From bc59ea805f32568f0835bd55d39575236c24a066 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 21 Mar 2019 22:22:25 -0400 Subject: added very basic cycle detection when adding to collections --- src/client/util/DragManager.ts | 4 +--- .../views/collections/CollectionDockingView.tsx | 2 +- .../views/collections/CollectionFreeFormView.tsx | 21 ++++++++++++--------- src/client/views/collections/CollectionPDFView.tsx | 2 +- .../views/collections/CollectionVideoView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 22 ++++++++++++++++++---- .../views/collections/CollectionViewBase.tsx | 12 ++++++++---- src/client/views/collections/MarqueeView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- 9 files changed, 44 insertions(+), 25 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 753115f76..661fa4dc8 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -197,9 +197,7 @@ export namespace DragManager { document.removeEventListener("pointermove", moveHandler, true); document.removeEventListener("pointerup", upHandler); dragDiv.removeChild(dragElement); - if (hideSource && !wasHidden) { - ele.hidden = false; - } + ele.hidden = false; } const upHandler = (e: PointerEvent) => { abortDrag(); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 950df7261..39b284d8e 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -202,7 +202,7 @@ export class CollectionDockingView extends React.Component { }), }, - hideSource: true + hideSource: false })) ); } diff --git a/src/client/views/collections/CollectionFreeFormView.tsx b/src/client/views/collections/CollectionFreeFormView.tsx index da9f7b392..8b9d178c9 100644 --- a/src/client/views/collections/CollectionFreeFormView.tsx +++ b/src/client/views/collections/CollectionFreeFormView.tsx @@ -75,16 +75,19 @@ export class CollectionFreeFormView extends CollectionViewBase { @undoBatch @action - drop = (e: Event, de: DragManager.DropEvent) => { - super.drop(e, de); - if (de.data instanceof DragManager.DocumentDragData) { - let screenX = de.x - (de.data.xOffset as number || 0); - let screenY = de.y - (de.data.yOffset as number || 0); - const [x, y] = this.getTransform().transformPoint(screenX, screenY); - de.data.droppedDocument.SetNumber(KeyStore.X, x); - de.data.droppedDocument.SetNumber(KeyStore.Y, y); - this.bringToFront(de.data.droppedDocument); + drop = (e: Event, de: DragManager.DropEvent): boolean => { + if (super.drop(e, de)) { + if (de.data instanceof DragManager.DocumentDragData) { + let screenX = de.x - (de.data.xOffset as number || 0); + let screenY = de.y - (de.data.yOffset as number || 0); + const [x, y] = this.getTransform().transformPoint(screenX, screenY); + de.data.droppedDocument.SetNumber(KeyStore.X, x); + de.data.droppedDocument.SetNumber(KeyStore.Y, y); + this.bringToFront(de.data.droppedDocument); + } + return true; } + return false; } diff --git a/src/client/views/collections/CollectionPDFView.tsx b/src/client/views/collections/CollectionPDFView.tsx index e64b4c945..4d2daf149 100644 --- a/src/client/views/collections/CollectionPDFView.tsx +++ b/src/client/views/collections/CollectionPDFView.tsx @@ -38,7 +38,7 @@ export class CollectionPDFView extends React.Component { public SelectedDocs: FieldId[] = [] public active: () => boolean = () => CollectionView.Active(this); - addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); } + addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } specificContextMenu = (e: React.MouseEvent): void => { diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index 05f759967..a3921696f 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -44,7 +44,7 @@ export class CollectionVideoView extends React.Component { public SelectedDocs: FieldId[] = [] public active: () => boolean = () => CollectionView.Active(this); - addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); } + addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } specificContextMenu = (e: React.MouseEvent): void => { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7e1d31018..2e93e6bb8 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -37,7 +37,7 @@ export class CollectionView extends React.Component { @observable public SelectedDocs: FieldId[] = []; public active: () => boolean = () => CollectionView.Active(this); - addDocument = (doc: Document, allowDuplicates: boolean): void => { CollectionView.AddDocument(this.props, doc, allowDuplicates); } + addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } get subView() { return CollectionView.SubView(this); } @@ -48,17 +48,31 @@ export class CollectionView extends React.Component { return isSelected || childSelected || topMost; } + static createsCycle(doc: Document, props: CollectionViewProps): boolean { + let ldata = doc.GetList(KeyStore.Data, []); + for (let i = 0; i < ldata.length; i++) { + if (CollectionView.createsCycle(ldata[i], props)) + return true; + } + return doc.Id == props.Document.Id; + } + @action - public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean) { + public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean): boolean { doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1)); if (props.Document.Get(props.fieldKey) instanceof Field) { //TODO This won't create the field if it doesn't already exist const value = props.Document.GetData(props.fieldKey, ListField, new Array()) - if (!value.some(v => v.Id == doc.Id) || allowDuplicates) - value.push(doc); + if (!CollectionView.createsCycle(doc, props)) { + if (!value.some(v => v.Id == doc.Id) || allowDuplicates) + value.push(doc); + } + else + return false; } else { props.Document.SetOnPrototype(props.fieldKey, new ListField([doc])); } + return true; } @action diff --git a/src/client/views/collections/CollectionViewBase.tsx b/src/client/views/collections/CollectionViewBase.tsx index f33007196..535a8a8d2 100644 --- a/src/client/views/collections/CollectionViewBase.tsx +++ b/src/client/views/collections/CollectionViewBase.tsx @@ -17,6 +17,7 @@ import { NumberField } from "../../../fields/NumberField"; import { DocumentManager } from "../../util/DocumentManager"; import request = require("request"); import { ServerUtils } from "../../../server/ServerUtil"; +import { addHiddenFinalProp } from "mobx/lib/internal"; export interface CollectionViewProps { fieldKey: Key; @@ -33,7 +34,7 @@ export interface CollectionViewProps { export interface SubCollectionViewProps extends CollectionViewProps { active: () => boolean; - addDocument: (doc: Document, allowDuplicates: boolean) => void; + addDocument: (doc: Document, allowDuplicates: boolean) => boolean; removeDocument: (doc: Document) => boolean; CollectionView: CollectionView; } @@ -83,17 +84,20 @@ export class CollectionViewBase extends React.Component @undoBatch @action - protected drop(e: Event, de: DragManager.DropEvent) { + protected drop(e: Event, de: DragManager.DropEvent): boolean { if (de.data instanceof DragManager.DocumentDragData) { if (de.data.aliasOnDrop) { [KeyStore.Width, KeyStore.Height, KeyStore.CurPage].map(key => de.data.draggedDocument.GetTAsync(key, NumberField, (f: Opt) => f ? de.data.droppedDocument.SetNumber(key, f.Data) : null)); - } else if (de.data.removeDocument) { + } + let added = this.props.addDocument(de.data.droppedDocument, false); + if (added && de.data.removeDocument && !de.data.aliasOnDrop) { de.data.removeDocument(this.props.CollectionView); } - this.props.addDocument(de.data.droppedDocument, false); e.stopPropagation(); + return added; } + return false; } protected getDocumentFromType(type: string, path: string, options: DocumentOptions): Opt { diff --git a/src/client/views/collections/MarqueeView.tsx b/src/client/views/collections/MarqueeView.tsx index 8c2f3443c..b48ad9c64 100644 --- a/src/client/views/collections/MarqueeView.tsx +++ b/src/client/views/collections/MarqueeView.tsx @@ -17,7 +17,7 @@ interface MarqueeViewProps { getMarqueeTransform: () => Transform; getTransform: () => Transform; container: CollectionFreeFormView; - addDocument: (doc: Document, allowDuplicates: false) => void; + addDocument: (doc: Document, allowDuplicates: false) => boolean; activeDocuments: () => Document[]; selectDocuments: (docs: Document[]) => void; removeDocument: (doc: Document) => boolean; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index fec451b09..383341e02 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -23,7 +23,7 @@ import React = require("react"); export interface DocumentViewProps { ContainingCollectionView: Opt; Document: Document; - AddDocument?: (doc: Document, allowDuplicates: boolean) => void; + AddDocument?: (doc: Document, allowDuplicates: boolean) => boolean; RemoveDocument?: (doc: Document) => boolean; ScreenToLocalTransform: () => Transform; isTopMost: boolean; -- cgit v1.2.3-70-g09d2 From 2264fb874a09ee01540141986325c76650f32c21 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 25 Mar 2019 23:20:22 -0400 Subject: added 1-level navigation to region annotations --- .../views/collections/CollectionDockingView.tsx | 2 +- .../views/collections/CollectionVideoView.tsx | 108 ++++++++++++--------- src/client/views/collections/CollectionView.tsx | 6 +- src/client/views/nodes/LinkBox.tsx | 21 +++- src/client/views/nodes/PDFBox.tsx | 21 ++-- src/client/views/nodes/VideoBox.tsx | 29 ++++-- src/fields/KeyStore.ts | 2 + 7 files changed, 120 insertions(+), 69 deletions(-) (limited to 'src/client/views/collections/CollectionVideoView.tsx') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 39b284d8e..d4f510f5d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -191,7 +191,7 @@ export class CollectionDockingView extends React.Component { var className = (e.target as any).className; - if ((className == "lm_title" || className == "lm_tab lm_active") && e.ctrlKey) { + if ((className == "lm_title" || className == "lm_tab lm_active") && (e.ctrlKey || e.altKey)) { e.stopPropagation(); e.preventDefault(); let docid = (e.target as any).DashDocId; diff --git a/src/client/views/collections/CollectionVideoView.tsx b/src/client/views/collections/CollectionVideoView.tsx index a3921696f..470a853e3 100644 --- a/src/client/views/collections/CollectionVideoView.tsx +++ b/src/client/views/collections/CollectionVideoView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable } from "mobx"; +import { action, computed, observable, trace } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../../fields/Document"; import { KeyStore } from "../../../fields/KeyStore"; @@ -12,24 +12,26 @@ import "./CollectionVideoView.scss" @observer export class CollectionVideoView extends React.Component { + private _intervalTimer: any = undefined; + private _player: HTMLVideoElement | undefined = undefined; + + @observable _currentTimecode: number = 0; + @observable _isPlaying: boolean = false; public static LayoutString(fieldKey: string = "DataKey") { return `<${CollectionVideoView.name} Document={Document} ScreenToLocalTransform={ScreenToLocalTransform} fieldKey={${fieldKey}} panelWidth={PanelWidth} panelHeight={PanelHeight} isSelected={isSelected} select={select} bindings={bindings} isTopMost={isTopMost} SelectOnLoad={selectOnLoad} BackgroundView={BackgroundView} focus={focus}/>`; } - - private _mainCont = React.createRef(); - private get uIButtons() { let scaling = Math.min(1.8, this.props.ScreenToLocalTransform().transformDirection(1, 1)[0]); return ([
- {"" + Math.round(this.ctime)} - {" " + Math.round((this.ctime - Math.trunc(this.ctime)) * 100)} + {"" + Math.round(this._currentTimecode)} + {" " + Math.round((this._currentTimecode - Math.trunc(this._currentTimecode)) * 100)}
,
- {this.playing ? "\"" : ">"} + {this._isPlaying ? "\"" : ">"}
,
F @@ -37,64 +39,54 @@ export class CollectionVideoView extends React.Component { ]); } - - // "inherited" CollectionView API starts here... - - @observable - public SelectedDocs: FieldId[] = [] - public active: () => boolean = () => CollectionView.Active(this); - - addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } - removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } - - specificContextMenu = (e: React.MouseEvent): void => { - if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 - ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } }); + @action + mainCont = (ele: HTMLDivElement | null) => { + if (ele) { + this._player = ele!.getElementsByTagName("video")[0]; + if (this.props.Document.GetNumber(KeyStore.CurPage, -1) >= 0) { + this._currentTimecode = this.props.Document.GetNumber(KeyStore.CurPage, -1); + } } } - get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; } - get subView(): any { return CollectionView.SubView(this); } - componentDidMount() { - this.updateTimecode(); + this._intervalTimer = setInterval(this.updateTimecode, 1000); } - get player(): HTMLVideoElement | undefined { - return this._mainCont.current ? this._mainCont.current.getElementsByTagName("video")[0] : undefined; + componentWillUnmount() { + clearInterval(this._intervalTimer); } @action updateTimecode = () => { - if (this.player) { - this.ctime = this.player.currentTime; - this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this.ctime)); + if (this._player) { + if ((this._player as any).AHackBecauseSomethingResetsTheVideoToZero != -1) { + this._player.currentTime = (this._player as any).AHackBecauseSomethingResetsTheVideoToZero; + (this._player as any).AHackBecauseSomethingResetsTheVideoToZero = -1; + } else { + this._currentTimecode = this._player.currentTime; + this.props.Document.SetNumber(KeyStore.CurPage, Math.round(this._currentTimecode)); + } } - setTimeout(() => this.updateTimecode(), 100) } - - @observable - ctime: number = 0 - @observable - playing: boolean = false; - @action onPlayDown = () => { - if (this.player) { - if (this.player.paused) { - this.player.play(); - this.playing = true; + if (this._player) { + if (this._player.paused) { + this._player.play(); + this._isPlaying = true; } else { - this.player.pause(); - this.playing = false; + this._player.pause(); + this._isPlaying = false; } } } + @action onFullDown = (e: React.PointerEvent) => { - if (this.player) { - this.player.requestFullscreen(); + if (this._player) { + this._player.requestFullscreen(); e.stopPropagation(); e.preventDefault(); } @@ -102,15 +94,35 @@ export class CollectionVideoView extends React.Component { @action onResetDown = () => { - if (this.player) { - this.player.pause(); - this.player.currentTime = 0; + if (this._player) { + this._player.pause(); + this._player.currentTime = 0; } } + // "inherited" CollectionView API starts here... + + @observable + public SelectedDocs: FieldId[] = [] + public active: () => boolean = () => CollectionView.Active(this); + + addDocument = (doc: Document, allowDuplicates: boolean): boolean => { return CollectionView.AddDocument(this.props, doc, allowDuplicates); } + removeDocument = (doc: Document): boolean => { return CollectionView.RemoveDocument(this.props, doc); } + + specificContextMenu = (e: React.MouseEvent): void => { + if (!e.isPropagationStopped() && this.props.Document.Id != "mainDoc") { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 + ContextMenu.Instance.addItem({ description: "VideoOptions", event: () => { } }); + } + } + + get collectionViewType(): CollectionViewType { return CollectionViewType.Freeform; } + get subView(): any { return CollectionView.SubView(this); } + + render() { - return (
+ trace(); + return (
{this.subView} {this.props.isSelected() ? this.uIButtons : (null)}
) diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a740865ad..2fa2c9086 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -68,7 +68,11 @@ export class CollectionView extends React.Component { @action public static AddDocument(props: CollectionViewProps, doc: Document, allowDuplicates: boolean): boolean { - doc.SetNumber(KeyStore.Page, props.Document.GetNumber(KeyStore.CurPage, -1)); + var curPage = props.Document.GetNumber(KeyStore.CurPage, -1); + doc.SetNumber(KeyStore.Page, curPage); + if (curPage > 0) { + doc.Set(KeyStore.AnnotationOn, props.Document); + } if (props.Document.Get(props.fieldKey) instanceof Field) { //TODO This won't create the field if it doesn't already exist const value = props.Document.GetData(props.fieldKey, ListField, new Array()) diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index dd2f71b59..638d3b5a7 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -17,6 +17,8 @@ import { faEye } from '@fortawesome/free-solid-svg-icons'; import { faEdit } from '@fortawesome/free-solid-svg-icons'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { undoBatch } from "../../util/UndoManager"; +import { FieldWaiting } from "../../../fields/Field"; +import { NumberField } from "../../../fields/NumberField"; library.add(faEye); @@ -41,7 +43,24 @@ export class LinkBox extends React.Component { if (docView) { docView.props.focus(this.props.pairedDoc); } else { - CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc) + this.props.pairedDoc.GetAsync(KeyStore.AnnotationOn, (contextDoc: any) => { + if (!contextDoc) { + CollectionDockingView.Instance.AddRightSplit(this.props.pairedDoc); + } else if (contextDoc instanceof Document) { + this.props.pairedDoc.GetTAsync(KeyStore.Page, NumberField).then((pfield: any) => { + contextDoc.GetTAsync(KeyStore.CurPage, NumberField).then((cfield: any) => { + if (pfield != cfield) + contextDoc.SetNumber(KeyStore.CurPage, pfield.Data); + let contextView = DocumentManager.Instance.getDocumentView(contextDoc); + if (contextView) { + contextView.props.focus(contextDoc); + } else { + CollectionDockingView.Instance.AddRightSplit(contextDoc); + } + }) + }); + } + }); } } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 3a0ef2d32..e273b0b4f 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import * as htmlToImage from "html-to-image"; -import { action, computed, observable, reaction, IReactionDisposer, trace } from 'mobx'; +import { action, computed, observable, reaction, IReactionDisposer, trace, keys } from 'mobx'; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; import Measure from "react-measure"; @@ -18,6 +18,7 @@ import "./PDFBox.scss"; import { Sticky } from './Sticky'; //you should look at sticky and annotation, because they are used here import React = require("react") import { RouteStore } from "../../../server/RouteStore"; +import { NumberField } from "../../../fields/NumberField"; /** ALSO LOOK AT: Annotation.tsx, Sticky.tsx * This method renders PDF and puts all kinds of functionalities such as annotation, highlighting, @@ -60,7 +61,6 @@ export class PDFBox extends React.Component { //very useful for keeping track of X and y position throughout the PDF Canvas private initX: number = 0; private initY: number = 0; - private initPage: boolean = false; //checks if tool is on private _toolOn: boolean = false; //checks if tool is on @@ -88,17 +88,15 @@ export class PDFBox extends React.Component { @observable private _loaded: boolean = false; @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); } + @computed private get thumbnailPage() { return this.props.doc.GetNumber(KeyStore.ThumbnailPage, -1); } componentDidMount() { this._reactionDisposer = reaction( - () => this.curPage, + () => [this.curPage, this.thumbnailPage], () => { - if (this.curPage && this.initPage) { + if (this.curPage > 0 && this.thumbnailPage > 0 && this.curPage != this.thumbnailPage) { this.saveThumbnail(); this._interactive = true; - } else { - if (this.curPage > 0) - this.initPage = true; } }, { fireImmediately: true }); @@ -384,6 +382,7 @@ export class PDFBox extends React.Component { { width: me.props.doc.GetNumber(KeyStore.NativeWidth, 0), height: me.props.doc.GetNumber(KeyStore.NativeHeight, 0), quality: 0.5 }) .then(function (dataUrl: string) { me.props.doc.SetData(KeyStore.Thumbnail, new URL(dataUrl), ImageField); + me.props.doc.SetNumber(KeyStore.ThumbnailPage, me.props.doc.GetNumber(KeyStore.CurPage, -1)); }) .catch(function (error: any) { console.error('oops, something went wrong!', error); @@ -473,10 +472,10 @@ export class PDFBox extends React.Component { @computed get imageProxyRenderer() { - let field = this.props.doc.Get(KeyStore.Thumbnail); - if (field) { - let path = field == FieldWaiting ? "https://image.flaticon.com/icons/svg/66/66163.svg" : - field instanceof ImageField ? field.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; + let thumbField = this.props.doc.Get(KeyStore.Thumbnail); + if (thumbField) { + let path = thumbField == FieldWaiting || this.thumbnailPage != this.curPage ? "https://image.flaticon.com/icons/svg/66/66163.svg" : + thumbField instanceof ImageField ? thumbField.Data.href : "http://cs.brown.edu/people/bcz/prairie.jpg"; return ; } return (null); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 09ae95183..7c0db83a8 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,23 +1,27 @@ import React = require("react") import { observer } from "mobx-react"; -import { FieldWaiting } from '../../../fields/Field'; +import { FieldWaiting, Opt } from '../../../fields/Field'; import { VideoField } from '../../../fields/VideoField'; import { FieldView, FieldViewProps } from './FieldView'; import "./VideoBox.scss"; import Measure from "react-measure"; -import { action, trace, observable } from "mobx"; +import { action, trace, observable, IReactionDisposer, computed, reaction } from "mobx"; import { KeyStore } from "../../../fields/KeyStore"; import { number } from "prop-types"; @observer export class VideoBox extends React.Component { + private _reactionDisposer: Opt; + private _videoRef = React.createRef() public static LayoutString() { return FieldView.LayoutString(VideoBox) } constructor(props: FieldViewProps) { super(props); } + @computed private get curPage() { return this.props.doc.GetNumber(KeyStore.CurPage, -1); } + _loaded: boolean = false; @@ -39,7 +43,17 @@ export class VideoBox extends React.Component { } } + get player(): HTMLVideoElement | undefined { + return this._videoRef.current ? this._videoRef.current.getElementsByTagName("video")[0] : undefined; + } + @action + setVideoRef = (vref: HTMLVideoElement | null) => { + if (this.curPage >= 0 && vref) { + vref!.currentTime = this.curPage; + (vref! as any).AHackBecauseSomethingResetsTheVideoToZero = this.curPage; + } + } render() { let field = this.props.doc.GetT(this.props.fieldKey, VideoField); @@ -47,15 +61,16 @@ export class VideoBox extends React.Component { return
Loading
} let path = field.Data.href; - - //setTimeout(action(() => this._loaded = true), 500); + trace(); return ( {({ measureRef }) => - ) diff --git a/src/fields/KeyStore.ts b/src/fields/KeyStore.ts index 611e2951b..f9684b212 100644 --- a/src/fields/KeyStore.ts +++ b/src/fields/KeyStore.ts @@ -36,7 +36,9 @@ export namespace KeyStore { export const LinkDescription = new Key("LinkDescription"); export const LinkTags = new Key("LinkTag"); export const Thumbnail = new Key("Thumbnail"); + export const ThumbnailPage = new Key("ThumbnailPage"); export const CurPage = new Key("CurPage"); + export const AnnotationOn = new Key("AnnotationOn"); export const NumPages = new Key("NumPages"); export const Ink = new Key("Ink"); export const Cursors = new Key("Cursors"); -- cgit v1.2.3-70-g09d2