From d49e573d62fb9caee0568b454cb3555775a887ff Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 30 Sep 2019 11:14:52 -0400 Subject: start of presentationview cleanup --- src/client/views/nodes/PresBox.tsx | 410 +++++++++++++------------------------ 1 file changed, 147 insertions(+), 263 deletions(-) (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 5afd85430..d246da87a 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -2,21 +2,20 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction } from "mobx"; +import { action, observable, runInAction, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { List } from "../../../new_fields/List"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; -import { BoolCast, Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; -import { Utils } from "../../../Utils"; +import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; +import { ContextMenu } from "../ContextMenu"; import PresentationElement from "../presentationview/PresentationElement"; import PresentationViewList from "../presentationview/PresentationList"; import "../presentationview/PresentationView.scss"; import { FieldView, FieldViewProps } from './FieldView'; -import { ContextMenu } from "../ContextMenu"; +import PresModeMenu from "../presentationview/PresentationModeMenu"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; library.add(faArrowLeft); library.add(faArrowRight); @@ -27,147 +26,99 @@ library.add(faTimes); library.add(faMinus); library.add(faEdit); - -export interface PresViewProps { - Documents: List; -} - -const expandedWidth = 450; - @observer -export class PresBox extends React.Component { //FieldViewProps? - - +export class PresBox extends React.Component { public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); } public static Instance: PresBox; - - //Keeping track of the doc for the current presentation -- bcz: keeping a list of current presentations shouldn't be needed. Let users create them, store them, as they see fit. - @computed get curPresentation() { return this.props.Document; } - //mapping from docs to their rendered component - @observable presElementsMappings: Map = new Map(); + @observable _presElementsMappings: Map = new Map(); //variable that holds all the docs in the presentation - @observable childrenDocs: Doc[] = []; - //variable to hold if presentation is started - @observable presStatus: boolean = false; - //Mapping of guids to presentations. - @observable presentationsMapping: Map = new Map(); - //Mapping of presentations to guid, so that select option values can be given. - @observable presentationsKeyMapping: Map = new Map(); - //Variable to keep track of guid of the current presentation - @observable currentSelectedPresValue: string | undefined; - //A flag to keep track if title input is open, which is used in rendering. - @observable PresTitleInputOpen: boolean = false; - //Variable that holds reference to title input, so that new presentations get titles assigned. - @observable titleInputElement: HTMLInputElement | undefined; - @observable PresTitleChangeOpen: boolean = false; - - @observable opacity = 1; - @observable persistOpacity = true; - @observable labelOpacity = 0; - @observable presMode = false; + @observable _childrenDocs: Doc[] = []; + @observable _opacity = 1; + @observable _persistOpacity = true; + @observable _labelOpacity = 0; + // whether presentation view has been minimized + @observable _presMode = false; @observable public static CurrentPresentation: PresBox; + @computed static get miniPresentation() { + let next = () => PresBox.CurrentPresentation.next(); + let back = () => PresBox.CurrentPresentation.back(); + let startOrResetPres = () => PresBox.CurrentPresentation.startOrResetPres(); + let closePresMode = action(() => { + PresBox.CurrentPresentation._presMode = false; + CollectionDockingView.AddRightSplit(PresBox.CurrentPresentation.props.Document, undefined); + }); + return !PresBox.CurrentPresentation || !PresBox.CurrentPresentation._presMode ? (null) : + ; + } + //initilize class variables constructor(props: FieldViewProps) { super(props); runInAction(() => PresBox.CurrentPresentation = this); } - @action - toggle = (forcedValue: boolean | undefined) => { - if (forcedValue !== undefined) { - this.curPresentation.width = forcedValue ? expandedWidth : 0; - } else { - this.curPresentation.width = this.curPresentation.width === expandedWidth ? 0 : expandedWidth; - } - } - - //Second lifecycle function that gets called when component mounts. It makes sure toS - //get the back-up information from previous session for the current presentation. - async componentDidMount() { - this.setPresentationBackUps(); - } - - - /** - * The function that retrieves the backUps for the current Presentation if present, - * otherwise initializes. - */ - setPresentationBackUps = async () => { - //storing the presentation status,ie. whether it was stopped or playing - let presStatusBackUp = BoolCast(this.curPresentation.presStatus); - runInAction(() => this.presStatus = presStatusBackUp); - } - - //observable means render is re-called every time variable is changed - @observable - collapsed: boolean = false; next = async () => { - const current = NumCast(this.curPresentation.selectedDoc); + const current = NumCast(this.props.Document.selectedDoc); //asking to get document at current index let docAtCurrentNext = await this.getDocAtIndex(current + 1); - if (docAtCurrentNext === undefined) { - return; - } - let nextSelected = current + 1; + if (docAtCurrentNext !== undefined) { + let presDocs = DocListCast(this.props.Document.data); + let nextSelected = current + 1; - let presDocs = DocListCast(this.curPresentation.data); - for (; nextSelected < presDocs.length - 1; nextSelected++) { - if (!this.presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { - break; + for (; nextSelected < presDocs.length - 1; nextSelected++) { + if (!this._presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { + break; + } } - } - - this.gotoDocument(nextSelected, current); + this.gotoDocument(nextSelected, current); + } } back = async () => { - const current = NumCast(this.curPresentation.selectedDoc); + const current = NumCast(this.props.Document.selectedDoc); //requesting for the doc at current index let docAtCurrent = await this.getDocAtIndex(current); - if (docAtCurrent === undefined) { - return; - } + if (docAtCurrent !== undefined) { - //asking for its presentation id. - let curPresId = StrCast(docAtCurrent.presentId); - let prevSelected = current; - let zoomOut: boolean = false; + //asking for its presentation id. + let prevSelected = current; + let zoomOut: boolean = false; - //checking if this presentation id is mapped to a group, if so chosing the first element in group - let presDocs = DocListCast(this.curPresentation.data); - let currentsArray: Doc[] = []; - for (; prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) { - currentsArray.push(presDocs[prevSelected]); - } - prevSelected = Math.max(0, prevSelected - 1); - - //checking if any of the group members had used zooming in - currentsArray.forEach((doc: Doc) => { - //let presElem: PresentationElement | undefined = this.presElementsMappings.get(doc); - if (this.presElementsMappings.get(doc)!.props.document.showButton) { - zoomOut = true; - return; + //checking if this presentation id is mapped to a group, if so chosing the first element in group + let presDocs = DocListCast(this.props.Document.data); + let currentsArray: Doc[] = []; + for (; prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) { + currentsArray.push(presDocs[prevSelected]); } - }); - - - // if a group set that flag to zero or a single element - //If so making sure to zoom out, which goes back to state before zooming action - if (current > 0) { - if (zoomOut || this.presElementsMappings.get(docAtCurrent)!.showButton) { - let prevScale = NumCast(this.childrenDocs[prevSelected].viewScale, null); - let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[current]); - if (prevScale !== undefined && prevScale !== curScale) { - DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); + prevSelected = Math.max(0, prevSelected - 1); + + //checking if any of the group members had used zooming in + currentsArray.forEach((doc: Doc) => { + //let presElem: PresentationElement | undefined = this.presElementsMappings.get(doc); + if (this._presElementsMappings.get(doc)!.props.document.showButton) { + zoomOut = true; + return; + } + }); + + // if a group set that flag to zero or a single element + //If so making sure to zoom out, which goes back to state before zooming action + if (current > 0) { + if (zoomOut || this._presElementsMappings.get(docAtCurrent)!.showButton) { + let prevScale = NumCast(this._childrenDocs[prevSelected].viewScale, null); + let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[current]); + if (prevScale !== undefined && prevScale !== curScale) { + DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); + } } } + this.gotoDocument(prevSelected, current); } - this.gotoDocument(prevSelected, current); - } /** @@ -176,21 +127,21 @@ export class PresBox extends React.Component { //FieldViewProps? * Hide Until Presented, Hide After Presented, Fade After Presented */ showAfterPresented = (index: number) => { - this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => { + this._presElementsMappings.forEach((presElem, doc) => { //the order of cases is aligned based on priority if (presElem.props.document.hideTillShownButton) { - if (this.childrenDocs.indexOf(key) <= index) { - key.opacity = 1; + if (this._childrenDocs.indexOf(doc) <= index) { + doc.opacity = 1; } } if (presElem.props.document.hideAfterButton) { - if (this.childrenDocs.indexOf(key) < index) { - key.opacity = 0; + if (this._childrenDocs.indexOf(doc) < index) { + doc.opacity = 0; } } if (presElem.props.document.fadeButton) { - if (this.childrenDocs.indexOf(key) < index) { - key.opacity = 0.5; + if (this._childrenDocs.indexOf(doc) < index) { + doc.opacity = 0.5; } } }); @@ -202,21 +153,21 @@ export class PresBox extends React.Component { //FieldViewProps? * Hide Until Presented, Hide After Presented, Fade After Presented */ hideIfNotPresented = (index: number) => { - this.presElementsMappings.forEach((presElem: PresentationElement, key: Doc) => { + this._presElementsMappings.forEach((presElem, key) => { //the order of cases is aligned based on priority if (presElem.props.document.hideAfterButton) { - if (this.childrenDocs.indexOf(key) >= index) { + if (this._childrenDocs.indexOf(key) >= index) { key.opacity = 1; } } if (presElem.props.document.fadeButton) { - if (this.childrenDocs.indexOf(key) >= index) { + if (this._childrenDocs.indexOf(key) >= index) { key.opacity = 1; } } if (presElem.props.document.hideTillShownButton) { - if (this.childrenDocs.indexOf(key) > index) { + if (this._childrenDocs.indexOf(key) > index) { key.opacity = 0; } } @@ -229,26 +180,25 @@ export class PresBox extends React.Component { //FieldViewProps? * te option open, navigates to that element. */ navigateToElement = async (curDoc: Doc, fromDoc: number) => { - let docToJump: Doc = curDoc; - let willZoom: boolean = false; - + let docToJump = curDoc; + let willZoom = false; - let presDocs = DocListCast(this.curPresentation.data); + let presDocs = DocListCast(this.props.Document.data); let nextSelected = presDocs.indexOf(curDoc); let currentDocGroups: Doc[] = []; for (; nextSelected < presDocs.length - 1; nextSelected++) { - if (!this.presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { + if (!this._presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { break; } currentDocGroups.push(presDocs[nextSelected]); } currentDocGroups.forEach((doc: Doc, index: number) => { - if (this.presElementsMappings.get(doc)!.navButton) { + if (this._presElementsMappings.get(doc)!.navButton) { docToJump = doc; willZoom = false; } - if (this.presElementsMappings.get(doc)!.showButton) { + if (this._presElementsMappings.get(doc)!.showButton) { docToJump = doc; willZoom = true; } @@ -257,24 +207,23 @@ export class PresBox extends React.Component { //FieldViewProps? //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open - if (this.presElementsMappings.get(curDoc)!.navButton) { + if (this._presElementsMappings.get(curDoc)!.navButton) { DocumentManager.Instance.jumpToDocument(curDoc, false); - } else if (this.presElementsMappings.get(curDoc)!.showButton) { - let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + } else if (this._presElementsMappings.get(curDoc)!.showButton) { + let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[fromDoc]); //awaiting jump so that new scale can be found, since jumping is async await DocumentManager.Instance.jumpToDocument(curDoc, true); - let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); - curDoc.viewScale = newScale; + curDoc.viewScale = DocumentManager.Instance.getScaleOfDocView(curDoc); //saving the scale user was on before zooming in if (curScale !== 1) { - this.childrenDocs[fromDoc].viewScale = curScale; + this._childrenDocs[fromDoc].viewScale = curScale; } } return; } - let curScale = DocumentManager.Instance.getScaleOfDocView(this.childrenDocs[fromDoc]); + let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[fromDoc]); //awaiting jump so that new scale can be found, since jumping is async await DocumentManager.Instance.jumpToDocument(docToJump, willZoom); @@ -282,7 +231,7 @@ export class PresBox extends React.Component { //FieldViewProps? curDoc.viewScale = newScale; //saving the scale that user was on if (curScale !== 1) { - this.childrenDocs[fromDoc].viewScale = curScale; + this._childrenDocs[fromDoc].viewScale = curScale; } } @@ -291,18 +240,13 @@ export class PresBox extends React.Component { //FieldViewProps? * Async function that supposedly return the doc that is located at given index. */ getDocAtIndex = async (index: number) => { - const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); - if (!list) { - return undefined; - } - if (index < 0 || index >= list.length) { - return undefined; + const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + if (list && index >= 0 && index < list.length) { + this.props.Document.selectedDoc = index; + //awaiting async call to finish to get Doc instance + return await list[index]; } - - this.curPresentation.selectedDoc = index; - //awaiting async call to finish to get Doc instance - const doc = await list[index]; - return doc; + return undefined } /** @@ -311,23 +255,19 @@ export class PresBox extends React.Component { //FieldViewProps? */ @action public RemoveDoc = async (index: number) => { - const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); + const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); // don't replace with DocListCast -- we need to modify the document's actual stored list if (value) { - let removedDoc = await value.splice(index, 1)[0]; - - //removing the Presentation Element stored for it - this.presElementsMappings.delete(removedDoc); - + //removing the Presentation Element from the document and update mappings + this._presElementsMappings.delete(await value.splice(index, 1)[0]); } } public removeDocByRef = (doc: Doc) => { - let indexOfDoc = this.childrenDocs.indexOf(doc); - const value = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); + let indexOfDoc = this._childrenDocs.indexOf(doc); + const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); if (value) { value.splice(indexOfDoc, 1)[0]; } - //this.RemoveDoc(indexOfDoc, true); if (indexOfDoc !== - 1) { return true; } @@ -339,144 +279,92 @@ export class PresBox extends React.Component { //FieldViewProps? @action public gotoDocument = async (index: number, fromDoc: number) => { Doc.UnBrushAllDocs(); - const list = FieldValue(Cast(this.curPresentation.data, listSpec(Doc))); - if (!list) { - return; - } - if (index < 0 || index >= list.length) { - return; - } - this.curPresentation.selectedDoc = index; + const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + if (list && index >= 0 && index < list.length) { + this.props.Document.selectedDoc = index; - if (!this.presStatus) { - this.presStatus = true; - this.startPresentation(index); - } + if (!this.props.Document.presStatus) { + this.props.Document.presStatus = true; + this.startPresentation(index); + } - const doc = await list[index]; - if (this.presStatus) { - this.navigateToElement(doc, fromDoc); - this.hideIfNotPresented(index); - this.showAfterPresented(index); + const doc = await list[index]; + if (this.props.Document.presStatus) { + this.navigateToElement(doc, fromDoc); + this.hideIfNotPresented(index); + this.showAfterPresented(index); + } } } //Function that sets the store of the children docs. @action setChildrenDocs = (docList: Doc[]) => { - this.childrenDocs = docList; + this._childrenDocs = docList; } //The function that is called to render the play or pause button depending on //if presentation is running or not. renderPlayPauseButton = () => { - if (this.presStatus) { - return ; - } else { - return ; - } + return ; } //The function that starts or resets presentaton functionally, depending on status flag. @action startOrResetPres = () => { - if (this.presStatus) { + if (this.props.Document.presStatus) { this.resetPresentation(); } else { - this.presStatus = true; + this.props.Document.presStatus = true; this.startPresentation(0); - const current = NumCast(this.curPresentation.selectedDoc); - this.gotoDocument(0, current); + this.gotoDocument(0, NumCast(this.props.Document.selectedDoc)); } - this.curPresentation.presStatus = this.presStatus; } //The function that resets the presentation by removing every action done by it. It also //stops the presentaton. @action resetPresentation = () => { - this.childrenDocs.forEach((doc: Doc) => { + this._childrenDocs.forEach((doc: Doc) => { doc.opacity = 1; doc.viewScale = 1; }); - this.curPresentation.selectedDoc = 0; - this.presStatus = false; - this.curPresentation.presStatus = this.presStatus; - if (this.childrenDocs.length === 0) { - return; + this.props.Document.selectedDoc = 0; + this.props.Document.presStatus = false; + if (this._childrenDocs.length !== 0) { + DocumentManager.Instance.zoomIntoScale(this._childrenDocs[0], 1); } - DocumentManager.Instance.zoomIntoScale(this.childrenDocs[0], 1); } - //The function that starts the presentation, also checking if actions should be applied //directly at start. startPresentation = (startIndex: number) => { - this.presElementsMappings.forEach((component: PresentationElement, doc: Doc) => { + this._presElementsMappings.forEach((component, doc) => { if (component.props.document.hideTillShownButton) { - if (this.childrenDocs.indexOf(doc) > startIndex) { + if (this._childrenDocs.indexOf(doc) > startIndex) { doc.opacity = 0; } - } if (component.props.document.hideAfterButton) { - if (this.childrenDocs.indexOf(doc) < startIndex) { + if (this._childrenDocs.indexOf(doc) < startIndex) { doc.opacity = 0; } } if (component.props.document.fadeButton) { - if (this.childrenDocs.indexOf(doc) < startIndex) { + if (this._childrenDocs.indexOf(doc) < startIndex) { doc.opacity = 0.5; } } - }); - - } - - - /** - * The function that is called to render either select for presentations, or title inputting. - */ - renderSelectOrPresSelection = () => { - if (this.PresTitleInputOpen || this.PresTitleChangeOpen) { - return this.titleInputElement = e!} type="text" className="presentationView-title" placeholder="Enter Name!" onKeyDown={this.submitPresentationTitle} />; - } else { - return (null); - } - } - - /** - * The function that is called on enter press of title input. It gives the - * new presentation the title user entered. If nothing is entered, gives a default title. - */ - @action - submitPresentationTitle = (e: React.KeyboardEvent) => { - if (e.keyCode === 13) { - let presTitle = this.titleInputElement!.value; - this.titleInputElement!.value = ""; - if (this.PresTitleChangeOpen) { - this.PresTitleChangeOpen = false; - this.changePresentationTitle(presTitle); - } - } - } - /** - * The function that is called to change title of presentation to what user entered. - */ - @undoBatch - changePresentationTitle = (newTitle: string) => { - if (newTitle === "") { - return; - } - this.curPresentation.title = newTitle; } addPressElem = (keyDoc: Doc, elem: PresentationElement) => { - this.presElementsMappings.set(keyDoc, elem); + this._presElementsMappings.set(keyDoc, elem); } minimize = undoBatch(action(() => { - this.presMode = true; + this._presMode = true; this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close"); })); @@ -485,12 +373,10 @@ export class PresBox extends React.Component { //FieldViewProps? } render() { - - let width = "100%"; //NumCast(this.curPresentation.width) return ( -
!this.persistOpacity && (this.opacity = 1))} onContextMenu={this.specificContextMenu} - onPointerLeave={action(() => !this.persistOpacity && (this.opacity = 0.4))} - style={{ width: width, opacity: this.opacity, }}> +
!this._persistOpacity && (this._opacity = 1))} onContextMenu={this.specificContextMenu} + onPointerLeave={action(() => !this._persistOpacity && (this._opacity = 0.4))} + style={{ width: "100%", opacity: this._opacity, }}>
{this.renderPlayPauseButton()} @@ -500,28 +386,26 @@ export class PresBox extends React.Component { //FieldViewProps? ) => { - this.persistOpacity = e.target.checked; - this.opacity = this.persistOpacity ? 1 : 0.4; + this._persistOpacity = e.target.checked; + this._opacity = this._persistOpacity ? 1 : 0.4; })} - checked={this.persistOpacity} + checked={this._persistOpacity} style={{ position: "absolute", bottom: 5, left: 5 }} - onPointerEnter={action(() => this.labelOpacity = 1)} - onPointerLeave={action(() => this.labelOpacity = 0)} + onPointerEnter={action(() => this._labelOpacity = 1)} + onPointerLeave={action(() => this._labelOpacity = 0)} /> -

opacity {this.persistOpacity ? "persistent" : "on focus"}

+

opacity {this._persistOpacity ? "persistent" : "on focus"}

this.presElementsMappings.clear()} + clearElemMap={() => this._presElementsMappings.clear()} />
); } - - } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From d7f515f32de780884774e7bbdcc1cfe78733af45 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 30 Sep 2019 14:01:59 -0400 Subject: a bunch of changes to presentation view ... more coming. --- src/client/documents/DocumentTypes.ts | 2 +- src/client/documents/Documents.ts | 5 +- src/client/views/MainView.tsx | 1 - src/client/views/nodes/DocumentContentsView.tsx | 31 ++-- src/client/views/nodes/PresBox.tsx | 165 ++++++++------------- .../views/presentationview/PresentationElement.tsx | 19 +-- .../views/presentationview/PresentationList.tsx | 9 -- .../presentationview/PresentationModeMenu.scss | 30 ---- .../presentationview/PresentationModeMenu.tsx | 101 ------------- src/new_fields/Doc.ts | 22 ++- 10 files changed, 105 insertions(+), 280 deletions(-) delete mode 100644 src/client/views/presentationview/PresentationModeMenu.scss delete mode 100644 src/client/views/presentationview/PresentationModeMenu.tsx (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 381981e1b..8ec6e9642 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -19,5 +19,5 @@ export enum DocumentType { YOUTUBE = "youtube", DRAGBOX = "dragbox", PRES = "presentation", - LINKFOLLOW = "linkfollow", + LINKFOLLOW = "linkfollow" } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d1ec2ac39..01c36182f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -46,8 +46,6 @@ import { ComputedField } from "../../new_fields/ScriptField"; import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; -//import { PresBox } from "../views/nodes/PresBox"; -//import { PresField } from "../../new_fields/PresField"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -176,7 +174,7 @@ export namespace Docs { }], [DocumentType.LINKFOLLOW, { layout: { view: LinkFollowBox } - }] + }], ]); // All document prototypes are initialized with at least these values @@ -459,7 +457,6 @@ export namespace Docs { export function LinkFollowBoxDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.LINKFOLLOW), undefined, { ...(options || {}) }); } - export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index dadff21a7..35d527c91 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -634,7 +634,6 @@ export class MainView extends React.Component { {this.mainContent} - {PresBox.miniPresentation} {this.nodesMenu()} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 3c3cc0d91..d035cbe18 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -1,37 +1,34 @@ -import { computed, trace } from "mobx"; +import { computed } from "mobx"; import { observer } from "mobx-react"; +import { Doc } from "../../../new_fields/Doc"; +import { ScriptField } from "../../../new_fields/ScriptField"; +import { Cast } from "../../../new_fields/Types"; +import { OmitKeys, Without } from "../../../Utils"; +import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; +import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { CollectionPDFView } from "../collections/CollectionPDFView"; import { CollectionSchemaView } from "../collections/CollectionSchemaView"; import { CollectionVideoView } from "../collections/CollectionVideoView"; import { CollectionView } from "../collections/CollectionView"; +import { LinkFollowBox } from "../linking/LinkFollowBox"; +import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; import { AudioBox } from "./AudioBox"; +import { ButtonBox } from "./ButtonBox"; import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; -import { FormattedTextBox } from "./FormattedTextBox"; -import { ImageBox } from "./ImageBox"; import { DragBox } from "./DragBox"; -import { ButtonBox } from "./ButtonBox"; -import { PresBox } from "./PresBox"; -import { LinkFollowBox } from "../linking/LinkFollowBox"; +import { FieldView, FieldViewProps } from "./FieldView"; +import { FormattedTextBox } from "./FormattedTextBox"; import { IconBox } from "./IconBox"; +import { ImageBox } from "./ImageBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; +import { PresBox } from "./PresBox"; import { VideoBox } from "./VideoBox"; -import { FieldView } from "./FieldView"; import { WebBox } from "./WebBox"; -import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; -import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox"; import React = require("react"); -import { FieldViewProps } from "./FieldView"; -import { Without, OmitKeys } from "../../../Utils"; -import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; -import { List } from "../../../new_fields/List"; -import { Doc } from "../../../new_fields/Doc"; -import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { fromPromise } from "mobx-utils"; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? type BindingProps = Without; diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index d246da87a..e2dac4fd3 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -2,20 +2,19 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable, runInAction, computed } from "mobx"; +import { action, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; +import { CollectionDockingView } from "../collections/CollectionDockingView"; import { ContextMenu } from "../ContextMenu"; -import PresentationElement from "../presentationview/PresentationElement"; import PresentationViewList from "../presentationview/PresentationList"; import "../presentationview/PresentationView.scss"; import { FieldView, FieldViewProps } from './FieldView'; -import PresModeMenu from "../presentationview/PresentationModeMenu"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; library.add(faArrowLeft); library.add(faArrowRight); @@ -31,31 +30,13 @@ export class PresBox extends React.Component { public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); } public static Instance: PresBox; - //mapping from docs to their rendered component - @observable _presElementsMappings: Map = new Map(); //variable that holds all the docs in the presentation @observable _childrenDocs: Doc[] = []; - @observable _opacity = 1; - @observable _persistOpacity = true; - @observable _labelOpacity = 0; // whether presentation view has been minimized @observable _presMode = false; @observable public static CurrentPresentation: PresBox; - @computed static get miniPresentation() { - let next = () => PresBox.CurrentPresentation.next(); - let back = () => PresBox.CurrentPresentation.back(); - let startOrResetPres = () => PresBox.CurrentPresentation.startOrResetPres(); - let closePresMode = action(() => { - PresBox.CurrentPresentation._presMode = false; - CollectionDockingView.AddRightSplit(PresBox.CurrentPresentation.props.Document, undefined); - }); - return !PresBox.CurrentPresentation || !PresBox.CurrentPresentation._presMode ? (null) : - ; - } - //initilize class variables constructor(props: FieldViewProps) { super(props); @@ -71,7 +52,7 @@ export class PresBox extends React.Component { let nextSelected = current + 1; for (; nextSelected < presDocs.length - 1; nextSelected++) { - if (!this._presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { + if (!presDocs[nextSelected + 1].groupButton) { break; } } @@ -89,18 +70,16 @@ export class PresBox extends React.Component { let prevSelected = current; let zoomOut: boolean = false; - //checking if this presentation id is mapped to a group, if so chosing the first element in group - let presDocs = DocListCast(this.props.Document.data); + let presDocs = await DocListCastAsync(this.props.Document[this.props.fieldKey]); let currentsArray: Doc[] = []; - for (; prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) { + for (; presDocs && prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) { currentsArray.push(presDocs[prevSelected]); } prevSelected = Math.max(0, prevSelected - 1); //checking if any of the group members had used zooming in currentsArray.forEach((doc: Doc) => { - //let presElem: PresentationElement | undefined = this.presElementsMappings.get(doc); - if (this._presElementsMappings.get(doc)!.props.document.showButton) { + if (doc.showButton) { zoomOut = true; return; } @@ -109,7 +88,7 @@ export class PresBox extends React.Component { // if a group set that flag to zero or a single element //If so making sure to zoom out, which goes back to state before zooming action if (current > 0) { - if (zoomOut || this._presElementsMappings.get(docAtCurrent)!.showButton) { + if (zoomOut || docAtCurrent.showButton) { let prevScale = NumCast(this._childrenDocs[prevSelected].viewScale, null); let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[current]); if (prevScale !== undefined && prevScale !== curScale) { @@ -127,20 +106,20 @@ export class PresBox extends React.Component { * Hide Until Presented, Hide After Presented, Fade After Presented */ showAfterPresented = (index: number) => { - this._presElementsMappings.forEach((presElem, doc) => { + this._childrenDocs.forEach((doc, ind) => { //the order of cases is aligned based on priority - if (presElem.props.document.hideTillShownButton) { - if (this._childrenDocs.indexOf(doc) <= index) { + if (doc.hideTillShownButton) { + if (ind <= index) { doc.opacity = 1; } } - if (presElem.props.document.hideAfterButton) { - if (this._childrenDocs.indexOf(doc) < index) { + if (doc.hideAfterButton) { + if (ind < index) { doc.opacity = 0; } } - if (presElem.props.document.fadeButton) { - if (this._childrenDocs.indexOf(doc) < index) { + if (doc.fadeButton) { + if (ind < index) { doc.opacity = 0.5; } } @@ -153,21 +132,21 @@ export class PresBox extends React.Component { * Hide Until Presented, Hide After Presented, Fade After Presented */ hideIfNotPresented = (index: number) => { - this._presElementsMappings.forEach((presElem, key) => { + this._childrenDocs.forEach((key, ind) => { //the order of cases is aligned based on priority - if (presElem.props.document.hideAfterButton) { - if (this._childrenDocs.indexOf(key) >= index) { + if (key.hideAfterButton) { + if (ind >= index) { key.opacity = 1; } } - if (presElem.props.document.fadeButton) { - if (this._childrenDocs.indexOf(key) >= index) { + if (key.fadeButton) { + if (ind >= index) { key.opacity = 1; } } - if (presElem.props.document.hideTillShownButton) { - if (this._childrenDocs.indexOf(key) > index) { + if (key.hideTillShownButton) { + if (ind > index) { key.opacity = 0; } } @@ -187,18 +166,18 @@ export class PresBox extends React.Component { let nextSelected = presDocs.indexOf(curDoc); let currentDocGroups: Doc[] = []; for (; nextSelected < presDocs.length - 1; nextSelected++) { - if (!this._presElementsMappings.get(presDocs[nextSelected + 1])!.props.document.groupButton) { + if (!presDocs[nextSelected + 1].groupButton) { break; } currentDocGroups.push(presDocs[nextSelected]); } currentDocGroups.forEach((doc: Doc, index: number) => { - if (this._presElementsMappings.get(doc)!.navButton) { + if (doc.navButton) { docToJump = doc; willZoom = false; } - if (this._presElementsMappings.get(doc)!.showButton) { + if (doc.showButton) { docToJump = doc; willZoom = true; } @@ -207,9 +186,9 @@ export class PresBox extends React.Component { //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open - if (this._presElementsMappings.get(curDoc)!.navButton) { + if (curDoc.navButton) { DocumentManager.Instance.jumpToDocument(curDoc, false); - } else if (this._presElementsMappings.get(curDoc)!.showButton) { + } else if (curDoc.showButton) { let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[fromDoc]); //awaiting jump so that new scale can be found, since jumping is async await DocumentManager.Instance.jumpToDocument(curDoc, true); @@ -250,15 +229,13 @@ export class PresBox extends React.Component { } /** - * The function that removes a doc from a presentation. It also makes sure to - * do necessary updates to backUps and mappings stored locally. + * The function that removes a doc from a presentation. */ @action public RemoveDoc = async (index: number) => { const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); // don't replace with DocListCast -- we need to modify the document's actual stored list if (value) { - //removing the Presentation Element from the document and update mappings - this._presElementsMappings.delete(await value.splice(index, 1)[0]); + value.splice(index, 1); } } @@ -302,14 +279,6 @@ export class PresBox extends React.Component { this._childrenDocs = docList; } - //The function that is called to render the play or pause button depending on - //if presentation is running or not. - renderPlayPauseButton = () => { - return ; - } - //The function that starts or resets presentaton functionally, depending on status flag. @action startOrResetPres = () => { @@ -340,18 +309,18 @@ export class PresBox extends React.Component { //The function that starts the presentation, also checking if actions should be applied //directly at start. startPresentation = (startIndex: number) => { - this._presElementsMappings.forEach((component, doc) => { - if (component.props.document.hideTillShownButton) { + this._childrenDocs.map(doc => { + if (doc.hideTillShownButton) { if (this._childrenDocs.indexOf(doc) > startIndex) { doc.opacity = 0; } } - if (component.props.document.hideAfterButton) { + if (doc.hideAfterButton) { if (this._childrenDocs.indexOf(doc) < startIndex) { doc.opacity = 0; } } - if (component.props.document.fadeButton) { + if (doc.fadeButton) { if (this._childrenDocs.indexOf(doc) < startIndex) { doc.opacity = 0.5; } @@ -359,13 +328,18 @@ export class PresBox extends React.Component { }); } - addPressElem = (keyDoc: Doc, elem: PresentationElement) => { - this._presElementsMappings.set(keyDoc, elem); - } - - minimize = undoBatch(action(() => { - this._presMode = true; - this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close"); + toggleMinimize = undoBatch(action((e: React.PointerEvent) => { + if (this.props.Document.minimizedView) { + this.props.Document.minimizedView = false; + Doc.RemoveDocFromList((CurrentUserUtils.UserDocument.overlays as Doc), "data", this.props.Document); + CollectionDockingView.AddRightSplit(this.props.Document, this.props.DataDoc); + } else { + this.props.Document.minimizedView = true; + this.props.Document.x = e.clientX + 25; + this.props.Document.y = e.clientY - 25; + this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close"); + Doc.AddDocToList((CurrentUserUtils.UserDocument.overlays as Doc), "data", this.props.Document); + } })); specificContextMenu = (e: React.MouseEvent): void => { @@ -374,37 +348,24 @@ export class PresBox extends React.Component { render() { return ( -
!this._persistOpacity && (this._opacity = 1))} onContextMenu={this.specificContextMenu} - onPointerLeave={action(() => !this._persistOpacity && (this._opacity = 0.4))} - style={{ width: "100%", opacity: this._opacity, }}> -
- - {this.renderPlayPauseButton()} - - +
+
+ + + +
- ) => { - this._persistOpacity = e.target.checked; - this._opacity = this._persistOpacity ? 1 : 0.4; - })} - checked={this._persistOpacity} - style={{ position: "absolute", bottom: 5, left: 5 }} - onPointerEnter={action(() => this._labelOpacity = 1)} - onPointerLeave={action(() => this._labelOpacity = 0)} - /> -

opacity {this._persistOpacity ? "persistent" : "on focus"}

- this._presElementsMappings.clear()} - /> + {this.props.Document.minimizedView ? (null) : + }
); } diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx index 126d62c52..6e8c28d34 100644 --- a/src/client/views/presentationview/PresentationElement.tsx +++ b/src/client/views/presentationview/PresentationElement.tsx @@ -1,6 +1,6 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons'; -import { faArrowRight, faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch } from '@fortawesome/free-solid-svg-icons'; +import { faArrowDown, faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed } from "mobx"; import { observer } from "mobx-react"; @@ -22,7 +22,7 @@ library.add(fileSolid); library.add(faLocationArrow); library.add(fileRegular as any); library.add(faSearch); -library.add(faArrowRight); +library.add(faArrowDown); interface PresentationElementProps { mainDocument: Doc; @@ -33,7 +33,6 @@ interface PresentationElementProps { allListElements: Doc[]; presStatus: boolean; removeDocByRef(doc: Doc): boolean; - PresElementsMappings: Map; } /** @@ -59,14 +58,14 @@ export default class PresentationElement extends React.Component { + onExpandTabClick = (e: React.MouseEvent) => { e.stopPropagation(); - - this.openRightButton = !this.openRightButton; + this.embedInline = !this.embedInline } /** @@ -416,7 +413,7 @@ export default class PresentationElement extends React.Component e.stopPropagation()} onClick={this.onFadeDocumentAfterPresentedClick}> - +
{this.renderEmbeddedInline()} diff --git a/src/client/views/presentationview/PresentationList.tsx b/src/client/views/presentationview/PresentationList.tsx index da48a856a..483461e5a 100644 --- a/src/client/views/presentationview/PresentationList.tsx +++ b/src/client/views/presentationview/PresentationList.tsx @@ -12,11 +12,9 @@ interface PresListProps { mainDocument: Doc; deleteDocument(index: number): void; gotoDocument(index: number, fromDoc: number): Promise; - PresElementsMappings: Map; setChildrenDocs: (docList: Doc[]) => void; presStatus: boolean; removeDocByRef(doc: Doc): boolean; - clearElemMap(): void; } @@ -45,16 +43,10 @@ export default class PresentationViewList extends React.Component const children = DocListCast(this.props.mainDocument.data); this.initializeScaleViews(children); this.props.setChildrenDocs(children); - this.props.clearElemMap(); return (
{children.map((doc: Doc, index: number) => { - if (e && e !== null) { - this.props.PresElementsMappings.set(doc, e); - } - }} key={doc[Id]} mainDocument={this.props.mainDocument} document={doc} @@ -64,7 +56,6 @@ export default class PresentationViewList extends React.Component allListElements={children} presStatus={this.props.presStatus} removeDocByRef={this.props.removeDocByRef} - PresElementsMappings={this.props.PresElementsMappings} /> )}
diff --git a/src/client/views/presentationview/PresentationModeMenu.scss b/src/client/views/presentationview/PresentationModeMenu.scss deleted file mode 100644 index 336f43d20..000000000 --- a/src/client/views/presentationview/PresentationModeMenu.scss +++ /dev/null @@ -1,30 +0,0 @@ -.presMenu-cont { - position: fixed; - z-index: 10000; - height: 35px; - background: #323232; - box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); - border-radius: 0px 6px 6px 6px; - overflow: hidden; - display: flex; - - .presMenu-button { - background-color: transparent; - width: 35px; - height: 35px; - } - - .presMenu-button:hover { - background-color: #121212; - } - - .presMenu-dragger { - height: 100%; - transition: width .2s; - background-image: url("https://logodix.com/logo/1020374.png"); - background-size: 90% 100%; - background-repeat: no-repeat; - background-position: left center; - } - -} \ No newline at end of file diff --git a/src/client/views/presentationview/PresentationModeMenu.tsx b/src/client/views/presentationview/PresentationModeMenu.tsx deleted file mode 100644 index e4123a1fe..000000000 --- a/src/client/views/presentationview/PresentationModeMenu.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React = require("react"); -import { observable, action, runInAction } from "mobx"; -import "./PresentationModeMenu.scss"; -import { observer } from "mobx-react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - - -export interface PresModeMenuProps { - next: () => void; - back: () => void; - presStatus: boolean; - startOrResetPres: () => void; - closePresMode: () => void; -} - -/** - * This class is responsible for modeling of the Presentation Mode Menu. The menu allows - * user to navigate through presentation elements, and start/stop the presentation. - */ -@observer -export default class PresModeMenu extends React.Component { - - @observable private _top: number = 20; - @observable private _left: number = window.innerWidth - 160; - @observable private _opacity: number = 1; - @observable private _transition: string = "opacity 0.5s"; - @observable private _transitionDelay: string = ""; - private _offsetY: number = 0; - private _offsetX: number = 0; - - - private _mainCont: React.RefObject = React.createRef(); - - /** - * The function that changes the coordinates of the menu, depending on the - * movement of the mouse when it's being dragged. - */ - @action - dragging = (e: PointerEvent) => { - this._left = e.pageX - this._offsetX; - this._top = e.pageY - this._offsetY; - - e.stopPropagation(); - e.preventDefault(); - } - - /** - * The function that removes the event listeners that are responsible for - * dragging of the menu. - */ - dragEnd = (e: PointerEvent) => { - document.removeEventListener("pointermove", this.dragging); - document.removeEventListener("pointerup", this.dragEnd); - e.stopPropagation(); - e.preventDefault(); - } - - /** - * The function that starts the dragging of the presentation mode menu. When - * the lines on further right are clicked on. - */ - dragStart = (e: React.PointerEvent) => { - document.removeEventListener("pointermove", this.dragging); - document.addEventListener("pointermove", this.dragging); - document.removeEventListener("pointerup", this.dragEnd); - document.addEventListener("pointerup", this.dragEnd); - - this._offsetX = this._mainCont.current!.getBoundingClientRect().width - e.nativeEvent.offsetX; - this._offsetY = e.nativeEvent.offsetY; - - e.stopPropagation(); - e.preventDefault(); - } - - /** - * The function that is responsible for rendering the play or pause button, depending on the - * status of the presentation. - */ - renderPlayPauseButton = () => { - if (this.props.presStatus) { - return ; - } else { - return ; - } - } - - render() { - return ( -
- - {this.renderPlayPauseButton()} - - -
-
- ); - } -} \ No newline at end of file diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 79b73aba8..4a03fed08 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -337,11 +337,25 @@ export namespace Doc { export function IndexOf(toFind: Doc, list: Doc[]) { return list.findIndex(doc => doc === toFind || Doc.AreProtosEqual(doc, toFind)); } - export function AddDocToList(target: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) { - if (target[key] === undefined) { - Doc.GetProto(target)[key] = new List(); + export function RemoveDocFromList(listDoc: Doc, key: string, doc: Doc) { + if (listDoc[key] === undefined) { + Doc.GetProto(listDoc)[key] = new List(); } - let list = Cast(target[key], listSpec(Doc)); + let list = Cast(listDoc[key], listSpec(Doc)); + if (list) { + let ind = list.indexOf(doc); + if (ind !== -1) { + list.splice(ind, 1); + return true; + } + } + return false; + } + export function AddDocToList(listDoc: Doc, key: string, doc: Doc, relativeTo?: Doc, before?: boolean, first?: boolean, allowDuplicates?: boolean, reversed?: boolean) { + if (listDoc[key] === undefined) { + Doc.GetProto(listDoc)[key] = new List(); + } + let list = Cast(listDoc[key], listSpec(Doc)); if (list) { if (allowDuplicates !== true) { let pind = list.reduce((l, d, i) => d instanceof Doc && d[Id] === doc[Id] ? i : l, -1); -- cgit v1.2.3-70-g09d2 From 65d9b92bcadbecb6e7dd55930f96f228c6a2f4f7 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 30 Sep 2019 17:12:28 -0400 Subject: simpliified presentations to be a PresBox with PresElementBoxes. --- src/client/documents/DocumentTypes.ts | 3 +- src/client/documents/Documents.ts | 10 +- .../views/collections/CollectionDockingView.tsx | 9 +- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/client/views/nodes/PresBox.scss | 33 ++ src/client/views/nodes/PresBox.tsx | 189 +++++---- .../views/presentationview/PresElementBox.scss | 75 ++++ .../views/presentationview/PresElementBox.tsx | 329 ++++++++++++++++ .../views/presentationview/PresentationElement.tsx | 423 --------------------- .../views/presentationview/PresentationList.tsx | 64 ---- .../views/presentationview/PresentationView.scss | 113 ------ 12 files changed, 545 insertions(+), 708 deletions(-) create mode 100644 src/client/views/nodes/PresBox.scss create mode 100644 src/client/views/presentationview/PresElementBox.scss create mode 100644 src/client/views/presentationview/PresElementBox.tsx delete mode 100644 src/client/views/presentationview/PresentationElement.tsx delete mode 100644 src/client/views/presentationview/PresentationList.tsx delete mode 100644 src/client/views/presentationview/PresentationView.scss (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 8ec6e9642..e5d5885cd 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -19,5 +19,6 @@ export enum DocumentType { YOUTUBE = "youtube", DRAGBOX = "dragbox", PRES = "presentation", - LINKFOLLOW = "linkfollow" + LINKFOLLOW = "linkfollow", + PRESELEMENT = "preselement" } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 01c36182f..0d04d044e 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,7 +1,6 @@ import { HistogramField } from "../northstar/dash-fields/HistogramField"; import { HistogramBox } from "../northstar/dash-nodes/HistogramBox"; import { HistogramOperation } from "../northstar/operations/HistogramOperation"; -import { CollectionPDFView } from "../views/collections/CollectionPDFView"; import { CollectionVideoView } from "../views/collections/CollectionVideoView"; import { CollectionView } from "../views/collections/CollectionView"; import { CollectionViewType } from "../views/collections/CollectionBaseView"; @@ -46,6 +45,7 @@ import { ComputedField } from "../../new_fields/ScriptField"; import { ProxyField } from "../../new_fields/Proxy"; import { DocumentType } from "./DocumentTypes"; import { LinkFollowBox } from "../views/linking/LinkFollowBox"; +import { PresElementBox } from "../views/presentationview/PresElementBox"; var requestImageSize = require('../util/request-image-size'); var path = require('path'); @@ -175,6 +175,9 @@ export namespace Docs { [DocumentType.LINKFOLLOW, { layout: { view: LinkFollowBox } }], + [DocumentType.PRESELEMENT, { + layout: { view: PresElementBox } + }], ]); // All document prototypes are initialized with at least these values @@ -457,6 +460,11 @@ export namespace Docs { export function LinkFollowBoxDocument(options?: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.LINKFOLLOW), undefined, { ...(options || {}) }); } + + export function PresElementBoxDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) }); + } + export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id); } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 963851a12..d83530d4f 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -31,6 +31,8 @@ import { SubCollectionViewProps } from "./CollectionSubView"; import React = require("react"); import { ButtonSelector } from './ParentDocumentSelector'; import { DocumentType } from '../../documents/DocumentTypes'; +import { compileFunction } from 'vm'; +import { ComputedField } from '../../../new_fields/ScriptField'; library.add(faFile); const _global = (window /* browser */ || global /* node */) as any; @@ -571,11 +573,14 @@ export class DockedFrameRenderer extends React.Component { //add this new doc to props.Document let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; if (curPres) { + let pinDoc = Docs.Create.PresElementBoxDocument(); + Doc.GetProto(pinDoc).target = doc; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); const data = Cast(curPres.data, listSpec(Doc)); if (data) { - data.push(doc); + data.push(pinDoc); } else { - curPres.data = new List([doc]); + curPres.data = new List([pinDoc]); } if (!DocumentManager.Instance.getDocumentView(curPres)) { this.addDocTab(curPres, undefined, "onRight"); diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index e5a4a68bf..a6ee9c2c6 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -95,7 +95,7 @@ export class LinkMenuItem extends React.Component { @action.bound async followDefault() { if (LinkFollowBox.Instance !== undefined) { - LinkFollowBox.setAddDocTab(this.props.addDocTab);; + LinkFollowBox.setAddDocTab(this.props.addDocTab); LinkFollowBox.Instance.setLinkDocs(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc); LinkFollowBox.Instance.defaultLinkBehavior(); } diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index d035cbe18..75dd27f46 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -26,6 +26,7 @@ import { ImageBox } from "./ImageBox"; import { KeyValueBox } from "./KeyValueBox"; import { PDFBox } from "./PDFBox"; import { PresBox } from "./PresBox"; +import { PresElementBox } from "../presentationview/PresElementBox"; import { VideoBox } from "./VideoBox"; import { WebBox } from "./WebBox"; import React = require("react"); @@ -100,7 +101,7 @@ export class DocumentContentsView extends React.Component { public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); } - public static Instance: PresBox; - //variable that holds all the docs in the presentation - @observable _childrenDocs: Doc[] = []; - - // whether presentation view has been minimized - @observable _presMode = false; - @observable public static CurrentPresentation: PresBox; - - //initilize class variables - constructor(props: FieldViewProps) { - super(props); - runInAction(() => PresBox.CurrentPresentation = this); - } + @computed get childDocs() { return DocListCast(this.props.Document[this.props.fieldKey]); } next = async () => { const current = NumCast(this.props.Document.selectedDoc); @@ -89,8 +78,8 @@ export class PresBox extends React.Component { //If so making sure to zoom out, which goes back to state before zooming action if (current > 0) { if (zoomOut || docAtCurrent.showButton) { - let prevScale = NumCast(this._childrenDocs[prevSelected].viewScale, null); - let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[current]); + let prevScale = NumCast(this.childDocs[prevSelected].viewScale, null); + let curScale = DocumentManager.Instance.getScaleOfDocView(this.childDocs[current]); if (prevScale !== undefined && prevScale !== curScale) { DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); } @@ -106,22 +95,16 @@ export class PresBox extends React.Component { * Hide Until Presented, Hide After Presented, Fade After Presented */ showAfterPresented = (index: number) => { - this._childrenDocs.forEach((doc, ind) => { + this.childDocs.forEach((doc, ind) => { //the order of cases is aligned based on priority - if (doc.hideTillShownButton) { - if (ind <= index) { - doc.opacity = 1; - } + if (doc.hideTillShownButton && ind <= index) { + (doc.target as Doc).opacity = 1; } - if (doc.hideAfterButton) { - if (ind < index) { - doc.opacity = 0; - } + if (doc.hideAfterButton && ind < index) { + (doc.target as Doc).opacity = 0; } - if (doc.fadeButton) { - if (ind < index) { - doc.opacity = 0.5; - } + if (doc.fadeButton && ind < index) { + (doc.target as Doc).opacity = 0.5; } }); } @@ -132,23 +115,17 @@ export class PresBox extends React.Component { * Hide Until Presented, Hide After Presented, Fade After Presented */ hideIfNotPresented = (index: number) => { - this._childrenDocs.forEach((key, ind) => { + this.childDocs.forEach((key, ind) => { //the order of cases is aligned based on priority - if (key.hideAfterButton) { - if (ind >= index) { - key.opacity = 1; - } + if (key.hideAfterButton && ind >= index) { + (key.target as Doc).opacity = 1; } - if (key.fadeButton) { - if (ind >= index) { - key.opacity = 1; - } + if (key.fadeButton && ind >= index) { + (key.target as Doc).opacity = 1; } - if (key.hideTillShownButton) { - if (ind > index) { - key.opacity = 0; - } + if (key.hideTillShownButton && ind > index) { + (key.target as Doc).opacity = 0; } }); } @@ -158,11 +135,12 @@ export class PresBox extends React.Component { * has the option open and last in the group. If not in the group, and it has * te option open, navigates to that element. */ - navigateToElement = async (curDoc: Doc, fromDoc: number) => { + navigateToElement = async (curDoc: Doc, fromDocIndex: number) => { + let fromDoc = this.childDocs[fromDocIndex].target as Doc; let docToJump = curDoc; let willZoom = false; - let presDocs = DocListCast(this.props.Document.data); + let presDocs = DocListCast(this.props.Document[this.props.fieldKey]); let nextSelected = presDocs.indexOf(curDoc); let currentDocGroups: Doc[] = []; for (; nextSelected < presDocs.length - 1; nextSelected++) { @@ -186,31 +164,32 @@ export class PresBox extends React.Component { //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open + let target = await curDoc.target as Doc; if (curDoc.navButton) { - DocumentManager.Instance.jumpToDocument(curDoc, false); + DocumentManager.Instance.jumpToDocument(target, false); } else if (curDoc.showButton) { - let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[fromDoc]); + let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(curDoc, true); - curDoc.viewScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + await DocumentManager.Instance.jumpToDocument(target, true); + curDoc.viewScale = DocumentManager.Instance.getScaleOfDocView(target); //saving the scale user was on before zooming in if (curScale !== 1) { - this._childrenDocs[fromDoc].viewScale = curScale; + fromDoc.viewScale = curScale; } } return; } - let curScale = DocumentManager.Instance.getScaleOfDocView(this._childrenDocs[fromDoc]); + let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(docToJump, willZoom); - let newScale = DocumentManager.Instance.getScaleOfDocView(curDoc); + await DocumentManager.Instance.jumpToDocument(await docToJump.target as Doc, willZoom); + let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.target as Doc); curDoc.viewScale = newScale; //saving the scale that user was on if (curScale !== 1) { - this._childrenDocs[fromDoc].viewScale = curScale; + fromDoc.viewScale = curScale; } } @@ -219,34 +198,25 @@ export class PresBox extends React.Component { * Async function that supposedly return the doc that is located at given index. */ getDocAtIndex = async (index: number) => { - const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + const list = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc))); if (list && index >= 0 && index < list.length) { this.props.Document.selectedDoc = index; //awaiting async call to finish to get Doc instance - return await list[index]; + return list[index]; } - return undefined + return undefined; } - /** - * The function that removes a doc from a presentation. - */ - @action - public RemoveDoc = async (index: number) => { - const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); // don't replace with DocListCast -- we need to modify the document's actual stored list - if (value) { - value.splice(index, 1); - } - } - public removeDocByRef = (doc: Doc) => { - let indexOfDoc = this._childrenDocs.indexOf(doc); + @undoBatch + public removeDocument = (doc: Doc) => { const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); if (value) { - value.splice(indexOfDoc, 1)[0]; - } - if (indexOfDoc !== - 1) { - return true; + let indexOfDoc = value.indexOf(doc); + if (indexOfDoc !== - 1) { + value.splice(indexOfDoc, 1)[0]; + return true; + } } return false; } @@ -256,7 +226,7 @@ export class PresBox extends React.Component { @action public gotoDocument = async (index: number, fromDoc: number) => { Doc.UnBrushAllDocs(); - const list = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + const list = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc))); if (list && index >= 0 && index < list.length) { this.props.Document.selectedDoc = index; @@ -273,11 +243,6 @@ export class PresBox extends React.Component { } } } - //Function that sets the store of the children docs. - @action - setChildrenDocs = (docList: Doc[]) => { - this._childrenDocs = docList; - } //The function that starts or resets presentaton functionally, depending on status flag. @action @@ -295,33 +260,33 @@ export class PresBox extends React.Component { //stops the presentaton. @action resetPresentation = () => { - this._childrenDocs.forEach((doc: Doc) => { + this.childDocs.forEach((doc: Doc) => { doc.opacity = 1; doc.viewScale = 1; }); this.props.Document.selectedDoc = 0; this.props.Document.presStatus = false; - if (this._childrenDocs.length !== 0) { - DocumentManager.Instance.zoomIntoScale(this._childrenDocs[0], 1); + if (this.childDocs.length !== 0) { + DocumentManager.Instance.zoomIntoScale(this.childDocs[0], 1); } } //The function that starts the presentation, also checking if actions should be applied //directly at start. startPresentation = (startIndex: number) => { - this._childrenDocs.map(doc => { + this.childDocs.map(doc => { if (doc.hideTillShownButton) { - if (this._childrenDocs.indexOf(doc) > startIndex) { + if (this.childDocs.indexOf(doc) > startIndex) { doc.opacity = 0; } } if (doc.hideAfterButton) { - if (this._childrenDocs.indexOf(doc) < startIndex) { + if (this.childDocs.indexOf(doc) < startIndex) { doc.opacity = 0; } } if (doc.fadeButton) { - if (this._childrenDocs.indexOf(doc) < startIndex) { + if (this.childDocs.indexOf(doc) < startIndex) { doc.opacity = 0.5; } } @@ -346,26 +311,46 @@ export class PresBox extends React.Component { ContextMenu.Instance.addItem({ description: "Make Current Presentation", event: action(() => Doc.UserDoc().curPresentation = this.props.Document), icon: "asterisk" }); } + /** + * Initially every document starts with a viewScale 1, which means + * that they will be displayed in a canvas with scale 1. + */ + @action + initializeScaleViews = (docList: Doc[]) => { + docList.forEach((doc: Doc) => { + let curScale = NumCast(doc.viewScale, null); + if (curScale === undefined) { + doc.viewScale = 1; + } + }); + } + render() { + this.initializeScaleViews(this.childDocs); return ( -
-
- - + - - + +
{this.props.Document.minimizedView ? (null) : - } +
+ {this.childDocs.map((doc, index) => + + )} +
}
); } diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss new file mode 100644 index 000000000..c7d846718 --- /dev/null +++ b/src/client/views/presentationview/PresElementBox.scss @@ -0,0 +1,75 @@ +.presElementBox-item { + padding: 10px; + display: inline-block; + width: 100%; + outline-color: maroon; + outline-style: dashed; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + transition: all .1s; + + .documentView-node { + position: absolute; + z-index: 1; + } +} + +.presElementBox-item-above { + border-top: black 2px solid; +} + +.presElementBox-item-below { + border-bottom: black 2px solid; +} + +.presElementBox-item:hover { + transition: all .1s; + background: #AAAAAA; + border-radius: 12px; +} + +.presElementBox-selected { + background: gray; + color: black; + border-radius: 12px; + box-shadow: black 2px 2px 5px; +} + + +.presElementBox-icon { + float: right; +} + +.presElementBox-interaction { + color: gray; + float: left; +} + +.presElementBox-interaction-selected { + color: white; + float: left; +} + +.presElementBox-name { + font-size: 15px; + display: inline-block; +} + +.presElementBox-embedded { + position: relative; + margin-top: 15; +} + +.presElementBox-embeddedMask { + width:100%; + height:100%; + position: absolute; + left:0; + top:0; + background: transparent; + z-index:2; +} \ No newline at end of file diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx new file mode 100644 index 000000000..fe2aea27b --- /dev/null +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -0,0 +1,329 @@ +import { library } from '@fortawesome/fontawesome-svg-core'; +import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons'; +import { faArrowDown, faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, computed } from "mobx"; +import { observer } from "mobx-react"; +import { Doc } from "../../../new_fields/Doc"; +import { Id } from "../../../new_fields/FieldSymbols"; +import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction, returnEmptyString, returnFalse, returnOne } from "../../../Utils"; +import { DocumentType } from "../../documents/DocumentTypes"; +import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; +import { SelectionManager } from "../../util/SelectionManager"; +import { Transform } from "../../util/Transform"; +import { DocumentView } from "../nodes/DocumentView"; +import React = require("react"); +import "./PresElementBox.scss"; +import { FieldViewProps, FieldView } from '../nodes/FieldView'; + + +library.add(faArrowUp); +library.add(fileSolid); +library.add(faLocationArrow); +library.add(fileRegular as any); +library.add(faSearch); +library.add(faArrowDown); + +interface PresElementProps { + presentationDoc: Doc; + index: number; + gotoDocument(index: number, fromDoc: number): Promise; +} + +/** + * This class models the view a document added to presentation will have in the presentation. + * It involves some functionality for its buttons and options. + */ +@observer +export class PresElementBox extends React.Component { + + public static LayoutString() { return FieldView.LayoutString(PresElementBox); } + private header?: HTMLDivElement | undefined; + private listdropDisposer?: DragManager.DragDropDisposer; + private presElRef: React.RefObject = React.createRef(); + + @computed get currentIndex() { return NumCast(this.props.presentationDoc.selectedDoc); } + @computed get showButton() { return BoolCast(this.props.Document.showButton); } + @computed get navButton() { return BoolCast(this.props.Document.navButton); } + @computed get hideTillShownButton() { return BoolCast(this.props.Document.hideTillShownButton); } + @computed get fadeButton() { return BoolCast(this.props.Document.fadeButton); } + @computed get hideAfterButton() { return BoolCast(this.props.Document.hideAfterButton); } + @computed get groupButton() { return BoolCast(this.props.Document.groupButton); } + @computed get embedInline() { return BoolCast(this.props.Document.embedOpen); } + + set embedInline(value: boolean) { this.props.Document.embedOpen = value; } + set showButton(val: boolean) { this.props.Document.showButton = val; } + set navButton(val: boolean) { this.props.Document.navButton = val; } + set hideTillShownButton(val: boolean) { this.props.Document.hideTillShownButton = val; } + set fadeButton(val: boolean) { this.props.Document.fadeButton = val; } + set hideAfterButton(val: boolean) { this.props.Document.hideAfterButton = val; } + set groupButton(val: boolean) { this.props.Document.groupButton = val; } + + //Lifecycle function that makes sure that button BackUp is received when mounted. + componentDidMount() { + if (this.presElRef.current) { + this.header = this.presElRef.current; + this.createListDropTarget(this.presElRef.current); + } + } + + componentWillUnmount() { + this.listdropDisposer && this.listdropDisposer(); + } + /** + * The function that is called on click to turn Hiding document till press option on/off. + * It also sets the beginning and end opacitys. + */ + @action + onHideDocumentUntilPressClick = (e: React.MouseEvent) => { + e.stopPropagation(); + this.hideTillShownButton = !this.hideTillShownButton; + if (!this.hideTillShownButton) { + if (this.props.index >= this.currentIndex) { + (this.props.Document.target as Doc).opacity = 1; + } + } else { + if (this.props.presentationDoc.presStatus) { + if (this.props.index > this.currentIndex) { + (this.props.Document.target as Doc).opacity = 0; + } + } + } + } + + /** + * The function that is called on click to turn Hiding document after presented option on/off. + * It also makes sure that the option swithches from fade-after to this one, since both + * can't coexist. + */ + @action + onHideDocumentAfterPresentedClick = (e: React.MouseEvent) => { + e.stopPropagation(); + this.hideAfterButton = !this.hideAfterButton; + if (!this.hideAfterButton) { + if (this.props.index <= this.currentIndex) { + (this.props.Document.target as Doc).opacity = 1; + } + } else { + if (this.fadeButton) this.fadeButton = false; + if (this.props.presentationDoc.presStatus) { + if (this.props.index < this.currentIndex) { + (this.props.Document.target as Doc).opacity = 0; + } + } + } + } + + /** + * The function that is called on click to turn fading document after presented option on/off. + * It also makes sure that the option swithches from hide-after to this one, since both + * can't coexist. + */ + @action + onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => { + e.stopPropagation(); + this.fadeButton = !this.fadeButton; + if (!this.fadeButton) { + if (this.props.index <= this.currentIndex) { + (this.props.Document.target as Doc).opacity = 1; + } + } else { + this.hideAfterButton = false; + if (this.props.presentationDoc.presStatus) { + if (this.props.index < this.currentIndex) { + (this.props.Document.target as Doc).opacity = 0.5; + } + } + } + } + + /** + * The function that is called on click to turn navigation option of docs on/off. + */ + @action + onNavigateDocumentClick = (e: React.MouseEvent) => { + e.stopPropagation(); + this.navButton = !this.navButton; + if (this.navButton) { + this.showButton = false; + if (this.currentIndex === this.props.index) { + this.props.gotoDocument(this.props.index, this.props.index); + } + } + } + + /** + * The function that is called on click to turn zoom option of docs on/off. + */ + @action + onZoomDocumentClick = (e: React.MouseEvent) => { + e.stopPropagation(); + + this.showButton = !this.showButton; + if (!this.showButton) { + this.props.Document.viewScale = 1; + } else { + this.navButton = false; + if (this.currentIndex === this.props.index) { + this.props.gotoDocument(this.props.index, this.props.index); + } + } + } + + /** + * Creating a drop target for drag and drop when called. + */ + protected createListDropTarget = (ele: HTMLDivElement) => { + this.listdropDisposer && this.listdropDisposer(); + ele && (this.listdropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.listDrop.bind(this) } })); + } + + /** + * Returns a local transformed coordinate array for given coordinates. + */ + ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord]; + + /** + * This method is called when a element is dropped on a already esstablished target. + * It makes sure to do appropirate action depending on if the item is dropped before + * or after the target. + */ + listDrop = async (e: Event, de: DragManager.DropEvent) => { + let x = this.ScreenToLocalListTransform(de.x, de.y); + let rect = this.header!.getBoundingClientRect(); + let bounds = this.ScreenToLocalListTransform(rect.left, rect.top + rect.height / 2); + let before = x[1] < bounds[1]; + if (de.data instanceof DragManager.DocumentDragData) { + let addDoc = (doc: Doc) => Doc.AddDocToList(this.props.presentationDoc, "data", doc, this.props.Document, before); + e.stopPropagation(); + //where does treeViewId come from + let movedDocs = (de.data.options === this.props.presentationDoc[Id] ? de.data.draggedDocuments : de.data.droppedDocuments); + //console.log("How is this causing an issue"); + document.removeEventListener("pointermove", this.onDragMove, true); + return (de.data.dropAction || de.data.userDropAction) ? + de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.props.presentationDoc, "data", d, this.props.Document, before) || added, false) + : (de.data.moveDocument) ? + movedDocs.reduce((added: boolean, d: Doc) => de.data.moveDocument(d, this.props.Document, addDoc) || added, false) + : de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.props.presentationDoc, "data", d, this.props.Document, before), false); + } + document.removeEventListener("pointermove", this.onDragMove, true); + + return false; + } + + //This is used to add dragging as an event. + onPointerEnter = (e: React.PointerEvent): void => { + if (e.buttons === 1 && SelectionManager.GetIsDragging()) { + this.header!.className = "presElementBox-item" + (this.currentIndex === this.props.index ? "presElementBox-selected" : ""); + + document.addEventListener("pointermove", this.onDragMove, true); + } + } + + //This is used to remove the dragging when dropped. + onPointerLeave = (e: React.PointerEvent): void => { + this.header!.className = "presElementBox-item" + (this.currentIndex === this.props.index ? " presElementBox-selected" : ""); + + document.removeEventListener("pointermove", this.onDragMove, true); + } + + /** + * This method is passed in to be used when dragging a document. + * It makes it possible to show dropping lines on drop targets. + */ + onDragMove = (e: PointerEvent): void => { + Doc.UnBrushDoc(this.props.Document); + let x = this.ScreenToLocalListTransform(e.clientX, e.clientY); + let rect = this.header!.getBoundingClientRect(); + let bounds = this.ScreenToLocalListTransform(rect.left, rect.top + rect.height / 2); + this.header!.className = "presElementBox-item presElementBox-item-" + (x[1] < bounds[1] ? "above" : "below"); + e.stopPropagation(); + } + + /** + * This method is passed in to on down event of presElement, so that drag and + * drop can be completed with DragManager functionality. + */ + @action + move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => { + return this.props.Document !== target && (this.props.removeDocument ? this.props.removeDocument(doc) : false) && addDoc(doc); + } + + /** + * The function that is responsible for rendering the a preview or not for this + * presentation element. + */ + renderEmbeddedInline = () => { + if (!this.embedInline || !(this.props.Document.target instanceof Doc)) { + return (null); + } + + let propDocWidth = NumCast(this.props.Document.nativeWidth); + let propDocHeight = NumCast(this.props.Document.nativeHeight); + let scale = () => 175 / NumCast(this.props.Document.nativeWidth, 175); + return ( +
+ 350} + PanelHeight={() => 90} + focus={emptyFunction} + backgroundColor={returnEmptyString} + parentActive={returnFalse} + whenActiveChanged={returnFalse} + bringToFront={emptyFunction} + zoomToScale={emptyFunction} + getScale={returnOne} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + ContentScaling={scale} + /> +
+
+ ); + } + + render() { + let p = this.props; + + let className = "presElementBox-item" + (this.currentIndex === p.index ? " presElementBox-selected" : ""); + let dropAction = StrCast(this.props.Document.dropAction) as dropActionType; + let onItemDown = SetupDrag(this.presElRef, () => p.Document, this.move, dropAction, this.props.presentationDoc[Id], true); + return ( +
p.gotoDocument(p.index, this.currentIndex)}> + + {`${p.index + 1}. ${p.Document.title}`} + + +
+ + + + + + + + +
+ {this.renderEmbeddedInline()} +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/presentationview/PresentationElement.tsx b/src/client/views/presentationview/PresentationElement.tsx deleted file mode 100644 index 6e8c28d34..000000000 --- a/src/client/views/presentationview/PresentationElement.tsx +++ /dev/null @@ -1,423 +0,0 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { faFile as fileRegular } from '@fortawesome/free-regular-svg-icons'; -import { faArrowDown, faArrowUp, faFile as fileSolid, faFileDownload, faLocationArrow, faSearch } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed } from "mobx"; -import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnEmptyString, returnFalse, returnOne } from "../../../Utils"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; -import { SelectionManager } from "../../util/SelectionManager"; -import { Transform } from "../../util/Transform"; -import { ContextMenu } from "../ContextMenu"; -import { DocumentView } from "../nodes/DocumentView"; -import React = require("react"); - - -library.add(faArrowUp); -library.add(fileSolid); -library.add(faLocationArrow); -library.add(fileRegular as any); -library.add(faSearch); -library.add(faArrowDown); - -interface PresentationElementProps { - mainDocument: Doc; - document: Doc; - index: number; - deleteDocument(index: number): void; - gotoDocument(index: number, fromDoc: number): Promise; - allListElements: Doc[]; - presStatus: boolean; - removeDocByRef(doc: Doc): boolean; -} - -/** - * This class models the view a document added to presentation will have in the presentation. - * It involves some functionality for its buttons and options. - */ -@observer -export default class PresentationElement extends React.Component { - - private header?: HTMLDivElement | undefined; - private listdropDisposer?: DragManager.DragDropDisposer; - private presElRef: React.RefObject = React.createRef(); - - componentWillUnmount() { - this.listdropDisposer && this.listdropDisposer(); - } - - @computed get currentIndex() { return NumCast(this.props.mainDocument.selectedDoc); } - - @computed get showButton() { return BoolCast(this.props.document.showButton); } - @computed get navButton() { return BoolCast(this.props.document.navButton); } - @computed get hideTillShownButton() { return BoolCast(this.props.document.hideTillShownButton); } - @computed get fadeButton() { return BoolCast(this.props.document.fadeButton); } - @computed get hideAfterButton() { return BoolCast(this.props.document.hideAfterButton); } - @computed get groupButton() { return BoolCast(this.props.document.groupButton); } - @computed get expandInlineButton() { return BoolCast(this.props.document.expandInlineButton); } - set showButton(val: boolean) { this.props.document.showButton = val; } - set navButton(val: boolean) { this.props.document.navButton = val; } - set hideTillShownButton(val: boolean) { this.props.document.hideTillShownButton = val; } - set fadeButton(val: boolean) { this.props.document.fadeButton = val; } - set hideAfterButton(val: boolean) { this.props.document.hideAfterButton = val; } - set groupButton(val: boolean) { this.props.document.groupButton = val; } - set expandInlineButton(val: boolean) { this.props.document.expandInlineButton = val; } - - //Lifecycle function that makes sure that button BackUp is received when mounted. - async componentDidMount() { - if (this.presElRef.current) { - this.header = this.presElRef.current; - this.createListDropTarget(this.presElRef.current); - } - } - - //Lifecycle function that makes sure button BackUp is received when not re-mounted bu re-rendered. - async componentDidUpdate() { - if (this.presElRef.current) { - this.header = this.presElRef.current; - this.createListDropTarget(this.presElRef.current); - } - } - - @action - onGroupClick = (e: React.MouseEvent) => { - this.groupButton = !this.groupButton; - } - - /** - * The function that is called on click to turn Hiding document till press option on/off. - * It also sets the beginning and end opacitys. - */ - @action - onHideDocumentUntilPressClick = (e: React.MouseEvent) => { - e.stopPropagation(); - this.hideTillShownButton = !this.hideTillShownButton; - if (!this.hideTillShownButton) { - if (this.props.index >= this.currentIndex) { - this.props.document.opacity = 1; - } - } else { - if (this.props.presStatus) { - if (this.props.index > this.currentIndex) { - this.props.document.opacity = 0; - } - } - } - } - - /** - * The function that is called on click to turn Hiding document after presented option on/off. - * It also makes sure that the option swithches from fade-after to this one, since both - * can't coexist. - */ - @action - onHideDocumentAfterPresentedClick = (e: React.MouseEvent) => { - e.stopPropagation(); - this.hideAfterButton = !this.hideAfterButton; - if (!this.hideAfterButton) { - if (this.props.index <= this.currentIndex) { - this.props.document.opacity = 1; - } - } else { - if (this.fadeButton) this.fadeButton = false; - if (this.props.presStatus) { - if (this.props.index < this.currentIndex) { - this.props.document.opacity = 0; - } - } - } - } - - /** - * The function that is called on click to turn fading document after presented option on/off. - * It also makes sure that the option swithches from hide-after to this one, since both - * can't coexist. - */ - @action - onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => { - e.stopPropagation(); - this.fadeButton = !this.fadeButton; - if (!this.fadeButton) { - if (this.props.index <= this.currentIndex) { - this.props.document.opacity = 1; - } - } else { - this.hideAfterButton = false; - if (this.props.presStatus) { - if (this.props.index < this.currentIndex) { - this.props.document.opacity = 0.5; - } - } - } - } - - /** - * The function that is called on click to turn navigation option of docs on/off. - */ - @action - onNavigateDocumentClick = (e: React.MouseEvent) => { - e.stopPropagation(); - this.navButton = !this.navButton; - if (this.navButton) { - this.showButton = false; - if (this.currentIndex === this.props.index) { - this.props.gotoDocument(this.props.index, this.props.index); - } - } - } - - /** - * The function that is called on click to turn zoom option of docs on/off. - */ - @action - onZoomDocumentClick = (e: React.MouseEvent) => { - e.stopPropagation(); - - this.showButton = !this.showButton; - if (!this.showButton) { - this.props.document.viewScale = 1; - } else { - this.navButton = false; - if (this.currentIndex === this.props.index) { - this.props.gotoDocument(this.props.index, this.props.index); - } - } - } - - /** - * Function that expands the target inline - */ - @action - onExpandTabClick = (e: React.MouseEvent) => { - e.stopPropagation(); - this.embedInline = !this.embedInline - } - - /** - * Creating a drop target for drag and drop when called. - */ - protected createListDropTarget = (ele: HTMLDivElement) => { - this.listdropDisposer && this.listdropDisposer(); - if (ele) { - this.listdropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.listDrop.bind(this) } }); - } - } - - /** - * Returns a local transformed coordinate array for given coordinates. - */ - ScreenToLocalListTransform = (xCord: number, yCord: number) => { - return [xCord, yCord]; - } - - /** - * This method is called when a element is dropped on a already esstablished target. - * It makes sure to do appropirate action depending on if the item is dropped before - * or after the target. - */ - listDrop = async (e: Event, de: DragManager.DropEvent) => { - let x = this.ScreenToLocalListTransform(de.x, de.y); - let rect = this.header!.getBoundingClientRect(); - let bounds = this.ScreenToLocalListTransform(rect.left, rect.top + rect.height / 2); - let before = x[1] < bounds[1]; - if (de.data instanceof DragManager.DocumentDragData) { - let addDoc = (doc: Doc) => Doc.AddDocToList(this.props.mainDocument, "data", doc, this.props.document, before); - e.stopPropagation(); - //where does treeViewId come from - let movedDocs = (de.data.options === this.props.mainDocument[Id] ? de.data.draggedDocuments : de.data.droppedDocuments); - //console.log("How is this causing an issue"); - document.removeEventListener("pointermove", this.onDragMove, true); - return (de.data.dropAction || de.data.userDropAction) ? - de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.props.mainDocument, "data", d, this.props.document, before) || added, false) - : (de.data.moveDocument) ? - movedDocs.reduce((added: boolean, d: Doc) => de.data.moveDocument(d, this.props.document, addDoc) || added, false) - : de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.props.mainDocument, "data", d, this.props.document, before), false); - } - document.removeEventListener("pointermove", this.onDragMove, true); - - return false; - } - - //This is used to add dragging as an event. - onPointerEnter = (e: React.PointerEvent): void => { - if (e.buttons === 1 && SelectionManager.GetIsDragging()) { - - this.header!.className = "presentationView-item"; - - if (this.currentIndex === this.props.index) { - //this doc is selected - this.header!.className = "presentationView-item presentationView-selected"; - } - document.addEventListener("pointermove", this.onDragMove, true); - } - } - - //This is used to remove the dragging when dropped. - onPointerLeave = (e: React.PointerEvent): void => { - this.header!.className = "presentationView-item"; - - if (this.currentIndex === this.props.index) { - //this doc is selected - this.header!.className = "presentationView-item presentationView-selected"; - - } - document.removeEventListener("pointermove", this.onDragMove, true); - } - - /** - * This method is passed in to be used when dragging a document. - * It makes it possible to show dropping lines on drop targets. - */ - onDragMove = (e: PointerEvent): void => { - Doc.UnBrushDoc(this.props.document); - let x = this.ScreenToLocalListTransform(e.clientX, e.clientY); - let rect = this.header!.getBoundingClientRect(); - let bounds = this.ScreenToLocalListTransform(rect.left, rect.top + rect.height / 2); - let before = x[1] < bounds[1]; - this.header!.className = "presentationView-item"; - if (before) { - this.header!.className += " presentationView-item-above"; - } - else if (!before) { - this.header!.className += " presentationView-item-below"; - } - e.stopPropagation(); - } - - /** - * This method is passed in to on down event of presElement, so that drag and - * drop can be completed with DragManager functionality. - */ - @action - move: DragManager.MoveFunction = (doc: Doc, target: Doc, addDoc) => { - return this.props.document !== target && this.props.removeDocByRef(doc) && addDoc(doc); - } - /** - * This function is a getter to get if a document is in previewMode. - */ - private get embedInline() { - return BoolCast(this.props.document.embedOpen); - } - - /** - * This function sets document in presentation preview mode as the given value. - */ - private set embedInline(value: boolean) { - this.props.document.embedOpen = value; - } - - /** - * The function that recreates that context menu of presentation elements. - */ - onContextMenu = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - ContextMenu.Instance.addItem({ description: this.embedInline ? "Collapse Inline" : "Expand Inline", event: () => this.embedInline = !this.embedInline, icon: "expand" }); - ContextMenu.Instance.displayMenu(e.clientX, e.clientY); - } - - /** - * The function that is responsible for rendering the a preview or not for this - * presentation element. - */ - renderEmbeddedInline = () => { - if (!this.embedInline) { - return (null); - } - - let propDocWidth = NumCast(this.props.document.nativeWidth); - let propDocHeight = NumCast(this.props.document.nativeHeight); - let scale = () => { - let newScale = 175 / NumCast(this.props.document.nativeWidth, 175); - return newScale; - }; - return ( -
- 350} - PanelHeight={() => 90} - focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnFalse} - whenActiveChanged={returnFalse} - bringToFront={emptyFunction} - zoomToScale={emptyFunction} - getScale={returnOne} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - ContentScaling={scale} - /> -
-
- ); - } - - render() { - let p = this.props; - let title = p.document.title; - - let className = " presentationView-item"; - if (this.currentIndex === p.index) { - //this doc is selected - className += " presentationView-selected"; - } - let dropAction = StrCast(this.props.document.dropAction) as dropActionType; - let onItemDown = SetupDrag(this.presElRef, () => p.document, this.move, dropAction, this.props.mainDocument[Id], true); - return ( -
{ p.gotoDocument(p.index, this.currentIndex); e.stopPropagation(); }}> - - {`${p.index + 1}. ${title}`} - - -

- - - - - - - - -
- {this.renderEmbeddedInline()} -
- ); - } -} \ No newline at end of file diff --git a/src/client/views/presentationview/PresentationList.tsx b/src/client/views/presentationview/PresentationList.tsx deleted file mode 100644 index 483461e5a..000000000 --- a/src/client/views/presentationview/PresentationList.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { action } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; -import { Id } from "../../../new_fields/FieldSymbols"; -import { NumCast } from "../../../new_fields/Types"; -import PresentationElement from "./PresentationElement"; -import "./PresentationView.scss"; -import React = require("react"); - - -interface PresListProps { - mainDocument: Doc; - deleteDocument(index: number): void; - gotoDocument(index: number, fromDoc: number): Promise; - setChildrenDocs: (docList: Doc[]) => void; - presStatus: boolean; - removeDocByRef(doc: Doc): boolean; -} - - -@observer -/** - * Component that takes in a document prop and a boolean whether it's collapsed or not. - */ -export default class PresentationViewList extends React.Component { - - - /** - * Initially every document starts with a viewScale 1, which means - * that they will be displayed in a canvas with scale 1. - */ - @action - initializeScaleViews = (docList: Doc[]) => { - docList.forEach((doc: Doc) => { - let curScale = NumCast(doc.viewScale, null); - if (curScale === undefined) { - doc.viewScale = 1; - } - }); - } - - render() { - const children = DocListCast(this.props.mainDocument.data); - this.initializeScaleViews(children); - this.props.setChildrenDocs(children); - return ( -
- {children.map((doc: Doc, index: number) => - - )} -
- ); - } -} diff --git a/src/client/views/presentationview/PresentationView.scss b/src/client/views/presentationview/PresentationView.scss deleted file mode 100644 index 5c40a8808..000000000 --- a/src/client/views/presentationview/PresentationView.scss +++ /dev/null @@ -1,113 +0,0 @@ -.presentationView-cont { - position: absolute; - z-index: 2; - box-shadow: #AAAAAA .2vw .2vw .4vw; - right: 0; - top: 0; - bottom: 0; - letter-spacing: 2px; - overflow: hidden; - transition: 0.7s opacity ease; - pointer-events: all; -} - -.presentationView-item { - padding: 10px; - display: inline-block; - width: 100%; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - transition: all .1s; - - .documentView-node { - - position: absolute; - z-index: 1; - } -} - -.presentationView-item-above { - border-top: black 2px solid; -} - -.presentationView-item-below { - border-bottom: black 2px solid; -} - -.presentationView-listCont { - padding-left: 10px; - padding-right: 10px; -} - -.presentationView-item:hover { - transition: all .1s; - background: #AAAAAA; - border-radius: 12px; -} - -.presentationView-selected { - background: gray; - color: black; - border-radius: 12px; - box-shadow: black 2px 2px 5px; -} - -.presentationView-heading { - background: gray; - padding: 10px; - display: inline-block; - width: 100%; -} - -.presentationView-title { - padding-top: 3px; - padding-bottom: 3px; - font-size: 25px; - display: inline-block; - width: calc(100% - 200px); - letter-spacing: 2px; -} - -.presentation-icon { - float: right; -} - -.presentation-interaction { - color: gray; - float: left; -} - -.presentation-interaction-selected { - color: white; - float: left; -} - -.presentationView-name { - font-size: 15px; - display: inline-block; -} - -.presentation-button { - margin-right: 2.5%; - margin-left: 2.5%; - width: 20%; - border-radius: 5px; -} - -.presentation-buttons { - padding: 10px; -} - -.presentation-next:hover { - transition: all .1s; - background: #AAAAAA -} - -.presentation-back:hover { - transition: all .1s; - background: #AAAAAA -} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 3a2ac5286e556be726440547309937945f650393 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 30 Sep 2019 17:42:00 -0400 Subject: minor presentation fixes --- src/client/views/nodes/PresBox.tsx | 25 ++++----- .../views/presentationview/PresElementBox.tsx | 60 +++++++++++----------- 2 files changed, 44 insertions(+), 41 deletions(-) (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 526416e57..d3a24eb7a 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -37,7 +37,7 @@ export class PresBox extends React.Component { //asking to get document at current index let docAtCurrentNext = await this.getDocAtIndex(current + 1); if (docAtCurrentNext !== undefined) { - let presDocs = DocListCast(this.props.Document.data); + let presDocs = DocListCast(this.props.Document[this.props.fieldKey]); let nextSelected = current + 1; for (; nextSelected < presDocs.length - 1; nextSelected++) { @@ -210,7 +210,7 @@ export class PresBox extends React.Component { @undoBatch public removeDocument = (doc: Doc) => { - const value = FieldValue(Cast(this.props.Document.data, listSpec(Doc))); + const value = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc))); if (value) { let indexOfDoc = value.indexOf(doc); if (indexOfDoc !== - 1) { @@ -296,14 +296,14 @@ export class PresBox extends React.Component { toggleMinimize = undoBatch(action((e: React.PointerEvent) => { if (this.props.Document.minimizedView) { this.props.Document.minimizedView = false; - Doc.RemoveDocFromList((CurrentUserUtils.UserDocument.overlays as Doc), "data", this.props.Document); + Doc.RemoveDocFromList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document); CollectionDockingView.AddRightSplit(this.props.Document, this.props.DataDoc); } else { this.props.Document.minimizedView = true; this.props.Document.x = e.clientX + 25; this.props.Document.y = e.clientY - 25; this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close"); - Doc.AddDocToList((CurrentUserUtils.UserDocument.overlays as Doc), "data", this.props.Document); + Doc.AddDocToList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document); } })); @@ -325,6 +325,11 @@ export class PresBox extends React.Component { }); } + selectElement = (doc: Doc) => { + let index = DocListCast(this.props.Document[this.props.fieldKey]).indexOf(doc); + index !== -1 && this.gotoDocument(index, NumCast(this.props.Document.selectedDoc)); + } + render() { this.initializeScaleViews(this.childDocs); return ( @@ -339,15 +344,11 @@ export class PresBox extends React.Component {
{this.props.Document.minimizedView ? (null) :
- {this.childDocs.map((doc, index) => - + )}
} diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index fe2aea27b..1b4c841e7 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -4,7 +4,7 @@ import { faArrowDown, faArrowUp, faFile as fileSolid, faFileDownload, faLocation import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed } from "mobx"; import { observer } from "mobx-react"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnEmptyString, returnFalse, returnOne } from "../../../Utils"; @@ -16,6 +16,7 @@ import { DocumentView } from "../nodes/DocumentView"; import React = require("react"); import "./PresElementBox.scss"; import { FieldViewProps, FieldView } from '../nodes/FieldView'; +import { PresBox } from '../nodes/PresBox'; library.add(faArrowUp); @@ -26,9 +27,7 @@ library.add(faSearch); library.add(faArrowDown); interface PresElementProps { - presentationDoc: Doc; - index: number; - gotoDocument(index: number, fromDoc: number): Promise; + presBox: PresBox; } /** @@ -43,7 +42,10 @@ export class PresElementBox extends React.Component = React.createRef(); - @computed get currentIndex() { return NumCast(this.props.presentationDoc.selectedDoc); } + @computed get myIndex() { return DocListCast(this.props.presBox.props.Document[this.props.presBox.props.fieldKey]).indexOf(this.props.Document) } + @computed get presentationDoc() { return this.props.presBox.props.Document; } + @computed get presentationFieldKey() { return this.props.presBox.props.fieldKey; } + @computed get currentIndex() { return NumCast(this.presentationDoc.selectedDoc); } @computed get showButton() { return BoolCast(this.props.Document.showButton); } @computed get navButton() { return BoolCast(this.props.Document.navButton); } @computed get hideTillShownButton() { return BoolCast(this.props.Document.hideTillShownButton); } @@ -80,12 +82,12 @@ export class PresElementBox extends React.Component= this.currentIndex) { + if (this.myIndex >= this.currentIndex) { (this.props.Document.target as Doc).opacity = 1; } } else { - if (this.props.presentationDoc.presStatus) { - if (this.props.index > this.currentIndex) { + if (this.presentationDoc.presStatus) { + if (this.myIndex > this.currentIndex) { (this.props.Document.target as Doc).opacity = 0; } } @@ -102,13 +104,13 @@ export class PresElementBox extends React.Component Doc.AddDocToList(this.props.presentationDoc, "data", doc, this.props.Document, before); + let addDoc = (doc: Doc) => Doc.AddDocToList(this.presentationDoc, this.presentationFieldKey, doc, this.props.Document, before); e.stopPropagation(); //where does treeViewId come from - let movedDocs = (de.data.options === this.props.presentationDoc[Id] ? de.data.draggedDocuments : de.data.droppedDocuments); + let movedDocs = (de.data.options === this.presentationDoc[Id] ? de.data.draggedDocuments : de.data.droppedDocuments); //console.log("How is this causing an issue"); document.removeEventListener("pointermove", this.onDragMove, true); return (de.data.dropAction || de.data.userDropAction) ? - de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.props.presentationDoc, "data", d, this.props.Document, before) || added, false) + de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.presentationDoc, this.presentationFieldKey, d, this.props.Document, before) || added, false) : (de.data.moveDocument) ? movedDocs.reduce((added: boolean, d: Doc) => de.data.moveDocument(d, this.props.Document, addDoc) || added, false) - : de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.props.presentationDoc, "data", d, this.props.Document, before), false); + : de.data.droppedDocuments.reduce((added: boolean, d: Doc) => Doc.AddDocToList(this.presentationDoc, this.presentationFieldKey, d, this.props.Document, before), false); } document.removeEventListener("pointermove", this.onDragMove, true); @@ -215,7 +217,7 @@ export class PresElementBox extends React.Component { if (e.buttons === 1 && SelectionManager.GetIsDragging()) { - this.header!.className = "presElementBox-item" + (this.currentIndex === this.props.index ? "presElementBox-selected" : ""); + this.header!.className = "presElementBox-item" + (this.currentIndex === this.myIndex ? "presElementBox-selected" : ""); document.addEventListener("pointermove", this.onDragMove, true); } @@ -223,7 +225,7 @@ export class PresElementBox extends React.Component { - this.header!.className = "presElementBox-item" + (this.currentIndex === this.props.index ? " presElementBox-selected" : ""); + this.header!.className = "presElementBox-item" + (this.currentIndex === this.myIndex ? " presElementBox-selected" : ""); document.removeEventListener("pointermove", this.onDragMove, true); } @@ -298,18 +300,18 @@ export class PresElementBox extends React.Component p.Document, this.move, dropAction, this.props.presentationDoc[Id], true); + let onItemDown = SetupDrag(this.presElRef, () => p.Document, this.move, dropAction, this.presentationDoc[Id], true); return ( -
p.gotoDocument(p.index, this.currentIndex)}> + onClick={e => p.focus(p.Document)}> - {`${p.index + 1}. ${p.Document.title}`} + {`${this.myIndex + 1}. ${p.Document.title}`}
-- cgit v1.2.3-70-g09d2 From 8790670f8e2173dd2a578500b3f03ad08ef793f2 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 30 Sep 2019 20:56:10 -0400 Subject: switching presentatons to be colllections --- .../views/collections/CollectionSchemaView.tsx | 9 ++- .../views/collections/CollectionStackingView.scss | 2 +- .../views/collections/CollectionStackingView.tsx | 4 +- .../views/collections/CollectionTreeView.scss | 4 +- .../views/collections/CollectionTreeView.tsx | 18 +++--- src/client/views/collections/CollectionView.tsx | 8 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +----- src/client/views/nodes/PresBox.scss | 4 +- src/client/views/nodes/PresBox.tsx | 73 ++++++++++++++-------- .../views/presentationview/PresElementBox.scss | 1 + .../views/presentationview/PresElementBox.tsx | 46 +++++++------- 11 files changed, 101 insertions(+), 88 deletions(-) (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 8d931f812..77d86b2fa 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -166,6 +166,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { width={this.previewWidth} height={this.previewHeight} getTransform={this.getPreviewTransform} + CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document} CollectionView={this.props.CollectionView} moveDocument={this.props.moveDocument} addDocument={this.props.addDocument} @@ -906,8 +907,10 @@ interface CollectionSchemaPreviewProps { width: () => number; height: () => number; ruleProvider: Doc | undefined; + focus?: (doc: Doc) => void; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; CollectionView?: CollectionView | CollectionPDFView | CollectionVideoView; + CollectionDoc?: Doc; onClick?: ScriptField; getTransform: () => Transform; addDocument: (document: Doc, allowDuplicates?: boolean) => boolean; @@ -994,7 +997,7 @@ export class CollectionSchemaPreview extends React.Component - doc) { getDisplayDoc(layoutDoc: Doc, dataDoc: Doc | undefined, dxf: () => Transform, width: () => number) { let height = () => this.getDocHeight(layoutDoc); let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]()); - return doc) { width={width} height={height} getTransform={finalDxf} + focus={this.props.focus} + CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document} CollectionView={this.props.CollectionView} addDocument={this.props.addDocument} moveDocument={this.props.moveDocument} diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 197e57808..ab01a1086 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -8,11 +8,11 @@ box-sizing: border-box; height: 100%; width:100%; - position: absolute; + position: relative; top:0; padding-top: 20px; padding-left: 10px; - padding-right: 0px; + padding-right: 10px; background: $light-color-secondary; font-size: 13px; overflow: auto; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index e5313f68c..a133e2c51 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,12 +1,13 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faAngleRight, faCamera, faExpand, faTrash, faBell, faCaretDown, faCaretRight, faArrowsAltH, faCaretSquareDown, faCaretSquareRight, faTrashAlt, faPlus, faMinus } from '@fortawesome/free-solid-svg-icons'; +import { faAngleRight, faArrowsAltH, faBell, faCamera, faCaretDown, faCaretRight, faCaretSquareDown, faCaretSquareRight, faExpand, faMinus, faPlus, faTrash, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, trace, untracked } from "mobx"; +import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym, Opt, Field } from '../../../new_fields/Doc'; +import { Doc, DocListCast, Field, HeightSym, Opt, WidthSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { Document, listSpec } from '../../../new_fields/Schema'; +import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types'; import { emptyFunction, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -17,18 +18,16 @@ import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; +import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; +import { KeyValueBox } from '../nodes/KeyValueBox'; import { Templates } from '../Templates'; import { CollectionViewType } from './CollectionBaseView'; -import { CollectionDockingView } from './CollectionDockingView'; import { CollectionSchemaPreview } from './CollectionSchemaView'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; -import { KeyValueBox } from '../nodes/KeyValueBox'; -import { ContextMenuProps } from '../ContextMenuItem'; export interface TreeViewProps { @@ -250,8 +249,8 @@ class TreeView extends React.Component { } docWidth = () => { let aspect = NumCast(this.props.document.nativeHeight) / NumCast(this.props.document.nativeWidth); - if (aspect) return Math.min(this.props.document[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 5)); - return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 5) : this.props.panelWidth() - 5; + if (aspect) return Math.min(this.props.document[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT / aspect, this.props.panelWidth() - 20)); + return NumCast(this.props.document.nativeWidth) ? Math.min(this.props.document[WidthSym](), this.props.panelWidth() - 20) : this.props.panelWidth() - 20; } docHeight = () => { let bounds = this.boundsOfCollectionDocument; @@ -331,6 +330,7 @@ class TreeView extends React.Component { width={this.docWidth} height={this.docHeight} getTransform={this.docTransform} + CollectionDoc={this.props.containingCollection} CollectionView={undefined} addDocument={emptyFunction as any} moveDocument={this.props.moveDocument} diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 90fa00202..6eb444dde 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,25 +1,23 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faEye } from '@fortawesome/free-regular-svg-icons'; -import { faColumns, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree, faCopy } from '@fortawesome/free-solid-svg-icons'; +import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; -import { Doc, DocListCastAsync } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; -import { StrCast, Cast } from '../../../new_fields/Types'; +import { StrCast } from '../../../new_fields/Types'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from "./CollectionDockingView"; +import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionSchemaView } from "./CollectionSchemaView"; import { CollectionStackingView } from './CollectionStackingView'; import { CollectionTreeView } from "./CollectionTreeView"; import { CollectionViewBaseChrome } from './CollectionViewChromes'; -import { ImageField } from '../../../new_fields/URLField'; -import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines'; export const COLLECTION_BORDER_WIDTH = 2; library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index a9699d17b..6fc809f7f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -443,11 +443,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { return { + ...this.props, DataDoc: childData, Document: childLayout, - addDocument: this.props.addDocument, - removeDocument: this.props.removeDocument, - moveDocument: this.props.moveDocument, ruleProvider: this.Document.isRuleProvider && childLayout.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider, //bcz: hack! - currently ruleProviders apply to documents in nested colleciton, not direct children of themselves onClick: undefined, // this.props.onClick, // bcz: check this out -- I don't think we want to inherit click handlers, or we at least need a way to ignore them ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform, @@ -460,37 +458,23 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { focus: this.focusDocument, backgroundColor: this.getClusterColor, parentActive: this.props.active, - whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, - addDocTab: this.props.addDocTab, - pinToPres: this.props.pinToPres, zoomToScale: this.zoomToScale, getScale: this.getScale }; } getDocumentViewProps(layoutDoc: Doc): DocumentViewProps { return { - DataDoc: this.props.DataDoc, - Document: this.props.Document, - addDocument: this.props.addDocument, - removeDocument: this.props.removeDocument, - moveDocument: this.props.moveDocument, - ruleProvider: this.props.ruleProvider, - onClick: this.props.onClick, + ...this.props, ScreenToLocalTransform: this.getTransform, - renderDepth: this.props.renderDepth, PanelWidth: layoutDoc[WidthSym], PanelHeight: layoutDoc[HeightSym], ContentScaling: returnOne, ContainingCollectionView: this.props.CollectionView, - ContainingCollectionDoc: this.props.ContainingCollectionDoc, focus: this.focusDocument, backgroundColor: returnEmptyString, parentActive: this.props.active, - whenActiveChanged: this.props.whenActiveChanged, bringToFront: this.bringToFront, - addDocTab: this.props.addDocTab, - pinToPres: this.props.pinToPres, zoomToScale: this.zoomToScale, getScale: this.getScale }; diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss index 2aadd77aa..e5a79ab11 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/PresBox.scss @@ -15,6 +15,7 @@ pointer-events: all; .presBox-listCont { + position: relative; padding-left: 10px; padding-right: 10px; } @@ -29,5 +30,4 @@ border-radius: 5px; } } -} - +} \ No newline at end of file diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index d3a24eb7a..ab777d534 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -2,7 +2,7 @@ import React = require("react"); import { library } from '@fortawesome/fontawesome-svg-core'; import { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, observable, runInAction, computed } from "mobx"; +import { action, computed, reaction, IReactionDisposer } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; @@ -10,12 +10,15 @@ import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; +import { CollectionViewType } from "../collections/CollectionBaseView"; import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; -import "./PresBox.scss"; import { FieldView, FieldViewProps } from './FieldView'; -import { PresElementBox } from "../presentationview/PresElementBox"; -import { Id } from "../../../new_fields/FieldSymbols"; +import "./PresBox.scss"; +import { DocumentType } from "../../documents/DocumentTypes"; +import { Docs } from "../../documents/Documents"; +import { ComputedField } from "../../../new_fields/ScriptField"; library.add(faArrowLeft); library.add(faArrowRight); @@ -29,6 +32,29 @@ library.add(faEdit); @observer export class PresBox extends React.Component { public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); } + _docListChangedReaction: IReactionDisposer | undefined; + componentDidMount() { + this._docListChangedReaction = reaction(() => { + const value = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc))); + return value ? value.slice() : value; + }, () => { + const value = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc))); + if (value) { + value.forEach((item, i) => { + if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) { + let pinDoc = Docs.Create.PresElementBoxDocument(); + Doc.GetProto(pinDoc).target = item; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); + value.splice(i, 1, pinDoc); + } + }) + } + }); + } + + componentWillUnmount() { + this._docListChangedReaction && this._docListChangedReaction(); + } @computed get childDocs() { return DocListCast(this.props.Document[this.props.fieldKey]); } @@ -275,20 +301,14 @@ export class PresBox extends React.Component { //directly at start. startPresentation = (startIndex: number) => { this.childDocs.map(doc => { - if (doc.hideTillShownButton) { - if (this.childDocs.indexOf(doc) > startIndex) { - doc.opacity = 0; - } + if (doc.hideTillShownButton && this.childDocs.indexOf(doc) > startIndex) { + doc.opacity = 0; } - if (doc.hideAfterButton) { - if (this.childDocs.indexOf(doc) < startIndex) { - doc.opacity = 0; - } + if (doc.hideAfterButton && this.childDocs.indexOf(doc) < startIndex) { + doc.opacity = 0; } - if (doc.fadeButton) { - if (this.childDocs.indexOf(doc) < startIndex) { - doc.opacity = 0.5; - } + if (doc.fadeButton && this.childDocs.indexOf(doc) < startIndex) { + doc.opacity = 0.5; } }); } @@ -316,8 +336,13 @@ export class PresBox extends React.Component { * that they will be displayed in a canvas with scale 1. */ @action - initializeScaleViews = (docList: Doc[]) => { + initializeScaleViews = (docList: Doc[], viewtype: number) => { + this.props.Document.chromeStatus = "disabled"; + let hgt = (viewtype === CollectionViewType.Tree) ? 50 : 72; docList.forEach((doc: Doc) => { + doc.presBox = this.props.Document; + doc.presBoxKey = this.props.fieldKey; + doc.height = hgt; let curScale = NumCast(doc.viewScale, null); if (curScale === undefined) { doc.viewScale = 1; @@ -325,13 +350,14 @@ export class PresBox extends React.Component { }); } + selectElement = (doc: Doc) => { let index = DocListCast(this.props.Document[this.props.fieldKey]).indexOf(doc); index !== -1 && this.gotoDocument(index, NumCast(this.props.Document.selectedDoc)); } render() { - this.initializeScaleViews(this.childDocs); + this.initializeScaleViews(this.childDocs, NumCast(this.props.Document.viewType)); return (
@@ -344,14 +370,9 @@ export class PresBox extends React.Component {
{this.props.Document.minimizedView ? (null) :
- {this.childDocs.map(doc => - - )} -
} + +
+ }
); } diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss index c7d846718..51f2d2ab8 100644 --- a/src/client/views/presentationview/PresElementBox.scss +++ b/src/client/views/presentationview/PresElementBox.scss @@ -1,6 +1,7 @@ .presElementBox-item { padding: 10px; display: inline-block; + pointer-events: all; width: 100%; outline-color: maroon; outline-style: dashed; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 1b4c841e7..7692800c3 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -12,11 +12,11 @@ import { DocumentType } from "../../documents/DocumentTypes"; import { DragManager, dropActionType, SetupDrag } from "../../util/DragManager"; import { SelectionManager } from "../../util/SelectionManager"; import { Transform } from "../../util/Transform"; +import { CollectionViewType } from '../collections/CollectionBaseView'; import { DocumentView } from "../nodes/DocumentView"; -import React = require("react"); +import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./PresElementBox.scss"; -import { FieldViewProps, FieldView } from '../nodes/FieldView'; -import { PresBox } from '../nodes/PresBox'; +import React = require("react"); library.add(faArrowUp); @@ -25,26 +25,22 @@ library.add(faLocationArrow); library.add(fileRegular as any); library.add(faSearch); library.add(faArrowDown); - -interface PresElementProps { - presBox: PresBox; -} - /** * This class models the view a document added to presentation will have in the presentation. * It involves some functionality for its buttons and options. */ @observer -export class PresElementBox extends React.Component { +export class PresElementBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(PresElementBox); } private header?: HTMLDivElement | undefined; private listdropDisposer?: DragManager.DragDropDisposer; private presElRef: React.RefObject = React.createRef(); + private _embedHeight = 100; - @computed get myIndex() { return DocListCast(this.props.presBox.props.Document[this.props.presBox.props.fieldKey]).indexOf(this.props.Document) } - @computed get presentationDoc() { return this.props.presBox.props.Document; } - @computed get presentationFieldKey() { return this.props.presBox.props.fieldKey; } + @computed get myIndex() { return DocListCast(this.presentationDoc[this.presentationFieldKey]).indexOf(this.props.Document); } + @computed get presentationDoc() { return this.props.Document.presBox as Doc; } + @computed get presentationFieldKey() { return StrCast(this.props.Document.presBoxKey); } @computed get currentIndex() { return NumCast(this.presentationDoc.selectedDoc); } @computed get showButton() { return BoolCast(this.props.Document.showButton); } @computed get navButton() { return BoolCast(this.props.Document.navButton); } @@ -266,12 +262,12 @@ export class PresElementBox extends React.Component 175 / NumCast(this.props.Document.nativeWidth, 175); return (
p.Document, this.move, dropAction, this.presentationDoc[Id], true); @@ -310,18 +307,25 @@ export class PresElementBox extends React.Component p.focus(p.Document)}> - - {`${this.myIndex + 1}. ${p.Document.title}`} - - -
+ {treecontainer ? (null) : <> + + {`${this.myIndex + 1}. ${p.Document.title}`} + + +
+ + } - - + +
{this.renderEmbeddedInline()} -- cgit v1.2.3-70-g09d2 From 2a393bd745667fa920d105bf69827c75dc687f08 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 1 Oct 2019 01:33:57 -0400 Subject: final updates to presentation view to improve layout. fixes for chromeHeight --- .../views/collections/CollectionDockingView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 6 ++-- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 19 +++++++---- .../views/collections/CollectionViewChromes.tsx | 3 +- src/client/views/nodes/PresBox.tsx | 10 ++++-- .../views/presentationview/PresElementBox.scss | 14 ++++++-- .../views/presentationview/PresElementBox.tsx | 38 ++++++++++------------ 8 files changed, 52 insertions(+), 42 deletions(-) (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index d83530d4f..246546c9e 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -573,7 +573,7 @@ export class DockedFrameRenderer extends React.Component { //add this new doc to props.Document let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; if (curPres) { - let pinDoc = Docs.Create.PresElementBoxDocument(); + let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); Doc.GetProto(pinDoc).target = doc; Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); const data = Cast(curPres.data, listSpec(Doc)); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index cb272ef1c..c7060f110 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -203,9 +203,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - // bcz: this is here for now to account for the presentation stacking view being offset from the document top in PresBox's. Should generalize this somehow. - let offsethack = Number(this._masonryGridRef && this._masonryGridRef.parentElement!.parentElement!.offsetTop); - let where = [de.x, de.y - offsethack]; + let where = [de.x, de.y]; let targInd = -1; let plusOne = false; if (de.data instanceof DragManager.DocumentDragData) { @@ -285,7 +283,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { let outerXf = Utils.GetScreenTransform(this._masonryGridRef!); let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY); return this.props.ScreenToLocalTransform(). - translate(offset[0], offset[1]). + translate(offset[0], offset[1] + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)). scale(NumCast(doc.width, 1) / this.columnWidth); } masonryChildren(docs: Doc[]) { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index a133e2c51..6ce5d8e20 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -81,7 +81,7 @@ class TreeView extends React.Component { private _header?: React.RefObject = React.createRef(); private _treedropDisposer?: DragManager.DragDropDisposer; private _dref = React.createRef(); - get defaultExpandedView() { return this.childDocs ? this.fieldKey : "fields"; } + get defaultExpandedView() { return this.childDocs ? this.fieldKey : this.props.document.defaultExpandedView ? StrCast(this.props.document.defaultExpandedView) : ""; } @observable _overrideTreeViewOpen = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state @computed get treeViewOpen() { return (BoolCast(this.props.document.treeViewOpen) && !this.props.preventTreeViewOpen) || this._overrideTreeViewOpen; } set treeViewOpen(c: boolean) { if (this.props.preventTreeViewOpen) this._overrideTreeViewOpen = c; else this.props.document.treeViewOpen = c; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 6eb444dde..534246326 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -50,20 +50,25 @@ export class CollectionView extends React.Component { this._reactionDisposer && this._reactionDisposer(); } + // bcz: Argh? What's the height of the collection chomes?? + chromeHeight = () => { + return (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0); + } + private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; switch (this.isAnnotationOverlay ? CollectionViewType.Freeform : type) { - case CollectionViewType.Schema: return (); + case CollectionViewType.Schema: return (); // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip - case CollectionViewType.Docking: return (); - case CollectionViewType.Tree: return (); - case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } - case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } - case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (); } + case CollectionViewType.Docking: return (); + case CollectionViewType.Tree: return (); + case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } + case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } + case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (); } case CollectionViewType.Freeform: default: this.props.Document.freeformLayoutEngine = undefined; - return (); + return (); } return (null); } diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 47b300efc..23001f0f5 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -10,7 +10,6 @@ import { ScriptField } from "../../../new_fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { Utils, emptyFunction } from "../../../Utils"; import { DragManager } from "../../util/DragManager"; -import { CompileScript } from "../../util/Scripting"; import { undoBatch } from "../../util/UndoManager"; import { EditableView } from "../EditableView"; import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss"; @@ -375,7 +374,7 @@ export class CollectionViewBaseChrome extends React.Component +
{this.props.Document.minimizedView ? (null) :
- +
}
diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss index 51f2d2ab8..34c170be2 100644 --- a/src/client/views/presentationview/PresElementBox.scss +++ b/src/client/views/presentationview/PresElementBox.scss @@ -1,10 +1,12 @@ .presElementBox-item { padding: 10px; display: inline-block; + background-color: #eeeeee; pointer-events: all; width: 100%; outline-color: maroon; outline-style: dashed; + border-radius: 12px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; @@ -40,9 +42,10 @@ box-shadow: black 2px 2px 5px; } - -.presElementBox-icon { +.presElementBox-closeIcon { float: right; + border-radius: 20px; + transform:scale(0.7); } .presElementBox-interaction { @@ -57,12 +60,17 @@ .presElementBox-name { font-size: 15px; + position: absolute; display: inline-block; + width: calc(100% - 45px); + text-overflow: ellipsis; + overflow: hidden; + white-space: pre; } .presElementBox-embedded { position: relative; - margin-top: 15; + margin-top: 30; } .presElementBox-embeddedMask { diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 0e3d8bb7f..fb9474cde 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -31,7 +31,6 @@ library.add(faArrowDown); export class PresElementBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(PresElementBox); } - private _embedHeight = 100; @computed get myIndex() { return DocListCast(this.presentationDoc[this.presentationFieldKey]).indexOf(this.props.Document); } @computed get presentationDoc() { return this.props.Document.presBox as Doc; } @@ -43,9 +42,9 @@ export class PresElementBox extends React.Component { @computed get fadeButton() { return BoolCast(this.props.Document.fadeButton); } @computed get hideAfterButton() { return BoolCast(this.props.Document.hideAfterButton); } @computed get groupButton() { return BoolCast(this.props.Document.groupButton); } - @computed get embedInline() { return BoolCast(this.props.Document.embedOpen); } + @computed get embedOpen() { return BoolCast(this.props.Document.embedOpen); } - set embedInline(value: boolean) { this.props.Document.embedOpen = value; } + set embedOpen(value: boolean) { this.props.Document.embedOpen = value; } set showButton(val: boolean) { this.props.Document.showButton = val; } set navButton(val: boolean) { this.props.Document.navButton = val; } set hideTillShownButton(val: boolean) { this.props.Document.hideTillShownButton = val; } @@ -162,7 +161,7 @@ export class PresElementBox extends React.Component { * presentation element. */ renderEmbeddedInline = () => { - if (!this.embedInline || !(this.props.Document.target instanceof Doc)) { + if (!this.embedOpen || !(this.props.Document.target instanceof Doc)) { return (null); } @@ -171,7 +170,7 @@ export class PresElementBox extends React.Component { let scale = () => 175 / NumCast(this.props.Document.nativeWidth, 175); return (
{ let treecontainer = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.viewType === CollectionViewType.Tree; let className = "presElementBox-item" + (this.currentIndex === this.myIndex ? " presElementBox-selected" : ""); + let pbi = "presElementBox-interaction"; return (
p.focus(p.Document)}> + style={{ outlineWidth: Doc.IsBrushed(p.Document.target as Doc) ? `1px` : "0px", }} + onClick={e => { p.focus(p.Document); e.stopPropagation(); }}> {treecontainer ? (null) : <> {`${this.myIndex + 1}. ${p.Document.title}`} - +
} - - - - - - - - -
+ + + + + + + + +
{this.renderEmbeddedInline()}
); -- cgit v1.2.3-70-g09d2 From 9e1234abe64d289927122ad641e3bcaf0b9eaf6e Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 1 Oct 2019 02:54:03 -0400 Subject: bugs, bugs. this time with fittobox when called from schema view which was passing width and height as functions, not values. switched to PanelWidth and PanelHeight --- .../views/collections/CollectionSchemaView.tsx | 32 +++++++++++----------- .../views/collections/CollectionStackingView.tsx | 4 +-- .../views/collections/CollectionTreeView.tsx | 4 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +-- src/client/views/nodes/PresBox.tsx | 4 +-- .../views/presentationview/PresElementBox.tsx | 22 ++++++--------- 6 files changed, 33 insertions(+), 37 deletions(-) (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 77d86b2fa..59cce7386 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -163,8 +163,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { childDocs={this.childDocs} renderDepth={this.props.renderDepth} ruleProvider={this.props.Document.isRuleProvider && layoutDoc && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider} - width={this.previewWidth} - height={this.previewHeight} + PanelWidth={this.previewWidth} + PanelHeight={this.previewHeight} getTransform={this.getPreviewTransform} CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document} CollectionView={this.props.CollectionView} @@ -904,8 +904,8 @@ interface CollectionSchemaPreviewProps { childDocs?: Doc[]; renderDepth: number; fitToBox?: boolean; - width: () => number; - height: () => number; + PanelWidth: () => number; + PanelHeight: () => number; ruleProvider: Doc | undefined; focus?: (doc: Doc) => void; showOverlays?: (doc: Doc) => { title?: string, caption?: string }; @@ -928,12 +928,12 @@ interface CollectionSchemaPreviewProps { export class CollectionSchemaPreview extends React.Component{ private dropDisposer?: DragManager.DragDropDisposer; _mainCont?: HTMLDivElement; - private get nativeWidth() { return NumCast(this.props.Document!.nativeWidth, this.props.width()); } - private get nativeHeight() { return NumCast(this.props.Document!.nativeHeight, this.props.height()); } + private get nativeWidth() { return NumCast(this.props.Document!.nativeWidth, this.props.PanelWidth()); } + private get nativeHeight() { return NumCast(this.props.Document!.nativeHeight, this.props.PanelHeight()); } private contentScaling = () => { - let wscale = this.props.width() / (this.nativeWidth ? this.nativeWidth : this.props.width()); - if (wscale * this.nativeHeight > this.props.height()) { - return this.props.height() / (this.nativeHeight ? this.nativeHeight : this.props.height()); + let wscale = this.props.PanelWidth() / (this.nativeWidth ? this.nativeWidth : this.props.PanelWidth()); + if (wscale * this.nativeHeight > this.props.PanelHeight()) { + return this.props.PanelHeight() / (this.nativeHeight ? this.nativeHeight : this.props.PanelHeight()); } return wscale; } @@ -962,10 +962,10 @@ export class CollectionSchemaPreview extends React.Component this.nativeWidth ? this.nativeWidth * this.contentScaling() : this.props.width(); - private PanelHeight = () => this.nativeHeight ? this.nativeHeight * this.contentScaling() : this.props.height(); + private PanelWidth = () => this.nativeWidth ? this.nativeWidth * this.contentScaling() : this.props.PanelWidth(); + private PanelHeight = () => this.nativeHeight ? this.nativeHeight * this.contentScaling() : this.props.PanelHeight(); private getTransform = () => this.props.getTransform().translate(-this.centeringOffset, 0).scale(1 / this.contentScaling()); - get centeringOffset() { return this.nativeWidth ? (this.props.width() - this.nativeWidth * this.contentScaling()) / 2 : 0; } + get centeringOffset() { return this.nativeWidth ? (this.props.PanelWidth() - this.nativeWidth * this.contentScaling()) / 2 : 0; } @action onPreviewScriptChange = (e: React.ChangeEvent) => { this.props.setPreviewScript(e.currentTarget.value); @@ -987,15 +987,15 @@ export class CollectionSchemaPreview extends React.Component
; return (
- {!this.props.Document || !this.props.width ? (null) : ( + style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}> + {!this.props.Document || !this.props.PanelWidth ? (null) : (
doc) { ruleProvider={this.props.Document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.Document : this.props.ruleProvider} fitToBox={this.props.fitToBox} onClick={layoutDoc.isTemplate ? this.onClickHandler : this.onChildClickHandler} - width={width} - height={height} + PanelWidth={width} + PanelHeight={height} getTransform={finalDxf} focus={this.props.focus} CollectionDoc={this.props.CollectionView && this.props.CollectionView.props.Document} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6ce5d8e20..ffd1f9170 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -327,8 +327,8 @@ class TreeView extends React.Component { showOverlays={this.noOverlays} ruleProvider={this.props.document.isRuleProvider && layoutDoc.type !== DocumentType.TEXT ? this.props.document : this.props.ruleProvider} fitToBox={this.boundsOfCollectionDocument !== undefined} - width={this.docWidth} - height={this.docHeight} + PanelWidth={this.docWidth} + PanelHeight={this.docHeight} getTransform={this.docTransform} CollectionDoc={this.props.containingCollection} CollectionView={undefined} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 6fc809f7f..27b9a06f5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -745,8 +745,8 @@ class CollectionFreeFormViewPannableContents extends React.Component otherwise, reactions won't fire - return
+ const zoom = this.props.zoomScaling(); + return
{this.props.children}
; } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 66608fb8d..f44ca99b9 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -47,7 +47,7 @@ export class PresBox extends React.Component { Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); value.splice(i, 1, pinDoc); } - }) + }); } }); } @@ -343,7 +343,7 @@ export class PresBox extends React.Component { doc.presBox = this.props.Document; doc.presBoxKey = this.props.fieldKey; doc.collapsedHeight = hgt; - doc.height = ComputedField.MakeFunction("this.collapsedHeight + Number(this.embedOpen ? 100:0)") + doc.height = ComputedField.MakeFunction("this.collapsedHeight + Number(this.embedOpen ? 100:0)"); let curScale = NumCast(doc.viewScale, null); if (curScale === undefined) { doc.viewScale = 1; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index fb9474cde..de3242d32 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -15,6 +15,7 @@ import { DocumentView } from "../nodes/DocumentView"; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./PresElementBox.scss"; import React = require("react"); +import { CollectionSchemaPreview } from '../collections/CollectionSchemaView'; library.add(faArrowUp); @@ -173,28 +174,23 @@ export class PresElementBox extends React.Component { height: propDocHeight === 0 ? NumCast(this.props.Document.height) - NumCast(this.props.Document.collapsedHeight) : propDocHeight * scale(), width: propDocWidth === 0 ? "auto" : propDocWidth * scale(), }}> - this.props.PanelWidth() - 20} + PanelHeight={() => 100} + setPreviewScript={emptyFunction} + getTransform={Transform.Identity} + active={this.props.active} + moveDocument={this.props.moveDocument!} renderDepth={1} - PanelWidth={() => 350} - PanelHeight={() => 90} focus={emptyFunction} - backgroundColor={returnEmptyString} - parentActive={returnFalse} whenActiveChanged={returnFalse} - bringToFront={emptyFunction} - zoomToScale={emptyFunction} - getScale={returnOne} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - ContentScaling={scale} />
-- cgit v1.2.3-70-g09d2 From 9427474b473d70974784a1517a1be902fb8d18ee Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 2 Oct 2019 18:26:55 -0400 Subject: many fixes to pdfs, linking, annotations, presentations. --- src/client/documents/Documents.ts | 24 ++-- src/client/util/DocumentManager.ts | 125 +++++++++++---------- src/client/util/RichTextSchema.tsx | 9 +- src/client/util/SharingManager.tsx | 2 +- .../views/collections/CollectionBaseView.tsx | 9 +- .../views/collections/CollectionDockingView.tsx | 10 +- src/client/views/collections/CollectionSubView.tsx | 1 + .../views/collections/CollectionTreeView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +- src/client/views/linking/LinkFollowBox.tsx | 11 +- src/client/views/nodes/DocumentView.tsx | 44 ++------ src/client/views/nodes/FormattedTextBox.tsx | 79 ++++++------- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 17 ++- src/client/views/nodes/PresBox.tsx | 24 ++-- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/pdf/Annotation.tsx | 17 ++- src/client/views/pdf/PDFMenu.tsx | 6 +- src/client/views/pdf/PDFViewer.tsx | 72 +++++------- .../views/presentationview/PresElementBox.tsx | 32 +++--- src/new_fields/Doc.ts | 2 + 21 files changed, 224 insertions(+), 276 deletions(-) (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 4d9698532..2d323ea4b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -653,32 +653,30 @@ export namespace DocUtils { } }); } - export function MakeLink(source: Doc, target: Doc, targetContext?: Doc, title: string = "", description: string = "", sourceContext?: Doc, id?: string, anchored1?: boolean) { - let sv = DocumentManager.Instance.getDocumentView(source); - if (sv && sv.props.ContainingCollectionDoc === target) return; - if (target === CurrentUserUtils.UserDocument) return undefined; + export function MakeLink(source: {doc:Doc,ctx?:Doc}, target: {doc:Doc,ctx?:Doc}, title: string = "", description: string = "", id?: string, anchored1?: boolean) { + let sv = DocumentManager.Instance.getDocumentView(source.doc); + if (sv && sv.props.ContainingCollectionDoc === target.doc) return; + if (target.doc === CurrentUserUtils.UserDocument) return undefined; let linkDocProto = new Doc(id, true); UndoManager.RunInBatch(() => { linkDocProto.type = DocumentType.LINK; - linkDocProto.targetContext = targetContext; - linkDocProto.sourceContext = sourceContext; - linkDocProto.title = title === "" ? source.title + " to " + target.title : title; + linkDocProto.targetContext = target.ctx; + linkDocProto.sourceContext = source.ctx; + linkDocProto.title = title === "" ? source.doc.title + " to " + target.doc.title : title; linkDocProto.linkDescription = description; - linkDocProto.anchor1 = source; - linkDocProto.anchor1Page = source.curPage; + linkDocProto.anchor1 = source.doc; linkDocProto.anchor1Groups = new List([]); linkDocProto.anchor1anchored = anchored1; - linkDocProto.anchor2 = target; - linkDocProto.anchor2Page = target.curPage; + linkDocProto.anchor2 = target.doc; linkDocProto.anchor2Groups = new List([]); LinkManager.Instance.addLink(linkDocProto); - Doc.GetProto(source).links = ComputedField.MakeFunction("links(this)"); - Doc.GetProto(target).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(source.doc).links = ComputedField.MakeFunction("links(this)"); + Doc.GetProto(target.doc).links = ComputedField.MakeFunction("links(this)"); }, "make link"); return linkDocProto; } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 4e6968f08..4ebcdf83c 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,7 +1,7 @@ import { action, computed, observable } from 'mobx'; import { Doc, DocListCastAsync } from '../../new_fields/Doc'; import { Id } from '../../new_fields/FieldSymbols'; -import { Cast, NumCast } from '../../new_fields/Types'; +import { Cast, NumCast, StrCast } from '../../new_fields/Types'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; @@ -56,9 +56,9 @@ export class DocumentManager { return this.getDocumentViewsById(doc[Id]); } - public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null { + public getDocumentViewById(id: string, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { - let toReturn: DocumentView | null = null; + let toReturn: DocumentView | undefined; let passes = preferredCollection ? [preferredCollection, undefined] : [undefined]; for (let pass of passes) { @@ -81,10 +81,14 @@ export class DocumentManager { return toReturn; } - public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | null { + public getDocumentView(toFind: Doc, preferredCollection?: CollectionView | CollectionPDFView | CollectionVideoView): DocumentView | undefined { return this.getDocumentViewById(toFind[Id], preferredCollection); } + public getFirstDocumentView(toFind: Doc): DocumentView | undefined { + const views = this.getDocumentViews(toFind); + return views.length ? views[0] : undefined; + } public getDocumentViews(toFind: Doc): DocumentView[] { let toReturn: DocumentView[] = []; @@ -127,72 +131,70 @@ export class DocumentManager { return pairs; } - - @undoBatch - public jumpToDocument = async (docDelegate: Doc, willZoom: boolean, forceDockFunc: boolean = false, dockFunc?: (doc: Doc) => void, linkPage?: number, docContext?: Doc): Promise => { - let doc = Doc.GetProto(docDelegate); - const contextDoc = await Cast(doc.annotationOn, Doc); - if (contextDoc) { - contextDoc.scrollY = NumCast(doc.y) - NumCast(contextDoc.height) / 2 * 72 / 96; - } - - let docView: DocumentView | null; - // using forceDockFunc as a flag for splitting linked to doc to the right...can change later if needed - if (!forceDockFunc && (docView = DocumentManager.Instance.getDocumentView(doc))) { - Doc.BrushDoc(docView.props.Document); - if (linkPage !== undefined) docView.props.Document.curPage = linkPage; - UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus"); + public jumpToDocument = async (targetDoc: Doc, willZoom: boolean, dockFunc?: (doc: Doc) => void, docContext?: Doc, closeContextIfNotFound: boolean = false): Promise => { + const docView = DocumentManager.Instance.getFirstDocumentView(targetDoc); + const annotatedDoc = await Cast(targetDoc.annotationOn, Doc); + if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? + annotatedDoc && docView.props.focus(annotatedDoc, false); + docView.props.focus(targetDoc, willZoom); } else { - if (!contextDoc) { - let docs = docContext ? await DocListCastAsync(docContext.data) : undefined; - let found = false; - // bcz: this just searches within the context for the target -- perhaps it should recursively search through all children? - docs && docs.map(d => found = found || Doc.AreProtosEqual(d, docDelegate)); - if (docContext && found) { - let targetContextView: DocumentView | null; - - if (!forceDockFunc && docContext && (targetContextView = DocumentManager.Instance.getDocumentView(docContext))) { - docContext.panTransformType = "Ease"; - targetContextView.props.focus(docDelegate, willZoom); - } else { - (dockFunc || CollectionDockingView.AddRightSplit)(docContext, undefined); - setTimeout(() => { - let dv = DocumentManager.Instance.getDocumentView(docContext); - dv && this.jumpToDocument(docDelegate, willZoom, forceDockFunc, - doc => dv!.props.focus(dv!.props.Document, true, 1, () => dv!.props.addDocTab(doc, undefined, "inPlace")), - linkPage); - }, 1050); - } - } else { - const actualDoc = Doc.MakeAlias(docDelegate); - Doc.BrushDoc(actualDoc); - if (linkPage !== undefined) actualDoc.curPage = linkPage; - (dockFunc || CollectionDockingView.AddRightSplit)(actualDoc, undefined); - } + const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; + const contextDoc = contextDocs && contextDocs.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; + const targetDocContext = (annotatedDoc ? annotatedDoc : contextDoc); + + if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default + (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); } else { - let contextView: DocumentView | null; - Doc.BrushDoc(docDelegate); - let contextViews = DocumentManager.Instance.getDocumentViews(contextDoc); - if (!forceDockFunc && contextViews.length) { - contextView = contextViews[0]; - SelectionManager.SelectDoc(contextView, false); // force unrendered annotations to be created - contextDoc.panTransformType = "Ease"; + const targetDocContextView = DocumentManager.Instance.getFirstDocumentView(targetDocContext); + if (targetDocContextView) { // we have a context view and aren't forced to create a new one ... focus on the context + targetDocContext.panTransformType = "Ease"; + targetDocContext.scrollY = 0; + targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom); + + // now find the target document within the context setTimeout(() => { - SelectionManager.DeselectDoc(contextView!); - docView = DocumentManager.Instance.getDocumentView(docDelegate); - docView && contextView!.props.focus(contextView!.props.Document, willZoom); - docView && UndoManager.RunInBatch(() => docView!.props.focus(docView!.props.Document, willZoom), "focus"); + const retryDocView = DocumentManager.Instance.getDocumentView(targetDoc); + if (retryDocView) { + retryDocView.props.focus(targetDoc, willZoom); // focus on the target if it now exists in the context + } else { + if (closeContextIfNotFound && targetDocContextView.props.removeDocument) targetDocContextView.props.removeDocument(targetDocContextView.props.Document); + (dockFunc || CollectionDockingView.AddRightSplit)(Doc.BrushDoc(Doc.MakeAlias(targetDoc)), undefined); // otherwise create a new view of the target + } }, 0); - } else { - (dockFunc || CollectionDockingView.AddRightSplit)(contextDoc, undefined); + } else { // there's no context view so we need to create one first and try again + targetDocContext.scrollY = 0; + (dockFunc || CollectionDockingView.AddRightSplit)(targetDocContext, undefined); setTimeout(() => { - this.jumpToDocument(docDelegate, willZoom, forceDockFunc, dockFunc, linkPage); - }, 1000); + const foundTargetDocContextView = DocumentManager.Instance.getDocumentView(targetDocContext); + if (foundTargetDocContextView) { // we should always find a target context here.... + this.jumpToDocument(targetDoc, willZoom, dockFunc, undefined, true); // so call jump to doc again and if the doc isn't found, it will be created. + } + }, 2000); // the long timeout gives the context view a chance to create its children. think pdf's which need to be activated to render their annotations. } } } } + public async FollowLink(doc: Doc, focus: (doc: Doc, maxLocation: string) => void, zoom: boolean = false, reverse: boolean = false, currentContext?: Doc) { + let linkDocs = LinkManager.Instance.getAllRelatedLinks(doc); + SelectionManager.DeselectAll(); + let firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, doc) && !linkDoc.anchor1anchored); + let secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, doc) && !linkDoc.anchor2anchored); + const firstDocWithoutView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); + const secondDocWithoutView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); + let first = firstDocWithoutView ? [firstDocWithoutView] : firstDocs; + let second = secondDocWithoutView ? [secondDocWithoutView] : secondDocs; + let linkFollowDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor2 as Doc] : undefined; + let linkFollowDocContexts = first.length ? [await (first[0].targetContext) as Doc, await (first[0].sourceContext) as Doc] : second.length ? [await (second[0].sourceContext) as Doc, await (second[0].targetContext) as Doc] : [undefined, undefined]; + if (linkFollowDocs && !linkFollowDocs.some(l => l instanceof Promise)) { + let maxLocation = StrCast(linkFollowDocs[0].maximizeLocation, "inTab"); + let targetContext = !Doc.AreProtosEqual(linkFollowDocContexts[reverse ? 1 : 0], currentContext) ? linkFollowDocContexts[reverse ? 1 : 0] : undefined; + DocumentManager.Instance.jumpToDocument(linkFollowDocs[reverse ? 1 : 0], zoom, + // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards + (doc: Doc) => focus(doc, maxLocation), targetContext); + } + } + @action zoomIntoScale = (docDelegate: Doc, scale: number) => { let docView = DocumentManager.Instance.getDocumentView(Doc.GetProto(docDelegate)); @@ -202,8 +204,7 @@ export class DocumentManager { getScaleOfDocView = (docDelegate: Doc) => { let doc = Doc.GetProto(docDelegate); - let docView: DocumentView | null; - docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentManager.Instance.getDocumentView(doc); if (docView) { return docView.props.getScale(); } else { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 64821d8db..49bd93942 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -627,17 +627,16 @@ export class ImageResizeView { let jumpToDoc = await Cast(linkDoc.anchor2, Doc); if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey); return; } } if (targetContext) { - DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(targetContext, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } else if (jumpToDoc) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } else { - DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, false, document => addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(linkDoc, e.ctrlKey, document => addDocTab(document, undefined, location ? location : "inTab")); } } }); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index f427e40b1..1cde2aa8e 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -192,7 +192,7 @@ export default class SharingManager extends React.Component<{}> { onClick={() => { let context: Opt; if (this.targetDoc && this.targetDocView && (context = this.targetDocView.props.ContainingCollectionView)) { - DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, undefined, undefined, context.props.Document); + DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document); } }} onPointerEnter={action(() => { diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index e928887e2..38d050e5c 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -5,7 +5,7 @@ import { Doc, DocListCast } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { listSpec } from '../../../new_fields/Schema'; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast } from '../../../new_fields/Types'; +import { BoolCast, Cast, NumCast, PromiseValue, StrCast, FieldValue } from '../../../new_fields/Types'; import { DocumentManager } from '../../util/DocumentManager'; import { SelectionManager } from '../../util/SelectionManager'; import { ContextMenu } from '../ContextMenu'; @@ -130,8 +130,11 @@ export class CollectionBaseView extends React.Component { let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => - annotationOn === this.dataDoc.Document && (doc.annotationOn = undefined)); + PromiseValue(Cast(doc.annotationOn, Doc)).then(annotationOn => { + if (Doc.AreProtosEqual(annotationOn, FieldValue(Cast(this.dataDoc.extendsDoc, Doc)))) { + Doc.GetProto(doc).annotationOn = undefined; + } + }); if (index !== -1) { value.splice(index, 1); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 98aff41d3..14e513157 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -574,8 +574,8 @@ export class DockedFrameRenderer extends React.Component { let curPres = Cast(CurrentUserUtils.UserDocument.curPresentation, Doc) as Doc; if (curPres) { let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); - Doc.GetProto(pinDoc).target = doc; - Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); + Doc.GetProto(pinDoc).presentationTargetDoc = doc; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()'); const data = Cast(curPres.data, listSpec(Doc)); if (data) { data.push(pinDoc); @@ -614,8 +614,8 @@ export class DockedFrameRenderer extends React.Component { } } - panelWidth = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelWidth : Math.min(this._panelWidth, Math.max(NumCast(this._document!.width), this.nativeWidth())); - panelHeight = () => this._document!.ignoreAspect || this._document!.fitWidth ? this._panelHeight : Math.min(this._panelHeight, Math.max(NumCast(this._document!.height), this.nativeHeight())); + panelWidth = () => this._document && this._document.maxWidth ? Math.min(Math.max(NumCast(this._document.width), NumCast(this._document.nativeWidth)), this._panelWidth) : this._panelWidth; + panelHeight = () => this._panelHeight; nativeWidth = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeWidth) || this._panelWidth : 0; nativeHeight = () => !this._document!.ignoreAspect && !this._document!.fitWidth ? NumCast(this._document!.nativeHeight) || this._panelHeight : 0; @@ -644,7 +644,7 @@ export class DockedFrameRenderer extends React.Component { } return Transform.Identity(); } - get previewPanelCenteringOffset() { return this.nativeWidth() && !BoolCast(this._document!.ignoreAspect) ? (this._panelWidth - this.nativeWidth() / this.ScreenToLocalTransform().Scale) / 2 : 0; } + get previewPanelCenteringOffset() { return this.nativeWidth() && !this._document!.ignoreAspect ? (this._panelWidth - this.nativeWidth() * this.contentScaling()) / 2 : 0; } addDocTab = (doc: Doc, dataDoc: Opt, location: string) => { SelectionManager.DeselectAll(); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 155f2718b..28d1eb384 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -31,6 +31,7 @@ export interface CollectionViewProps extends FieldViewProps { moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; PanelWidth: () => number; PanelHeight: () => number; + VisibleHeight?: () => number; chromeCollapsed: boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index ffd1f9170..882a0f144 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -217,7 +217,7 @@ class TreeView extends React.Component { if (de.data instanceof DragManager.LinkDragData) { let sourceDoc = de.data.linkSourceDocument; let destDoc = this.props.document; - DocUtils.MakeLink(sourceDoc, destDoc); + DocUtils.MakeLink({doc:sourceDoc}, {doc:destDoc}); e.stopPropagation(); } if (de.data instanceof DragManager.DocumentDragData) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index eb40e0bcb..4bdede48a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -403,8 +403,13 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { SelectionManager.DeselectAll(); if (this.props.Document.scrollHeight) { let annotOn = Cast(doc.annotationOn, Doc) as Doc; - let offset = annotOn && (NumCast(annotOn.height) / 2 * 72 / 96); - this.props.Document.scrollY = NumCast(doc.y) - offset; + if (!annotOn) { + this.props.focus(doc); + } else { + let contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn.height); + let offset = annotOn && (contextHgt / 2 * 96 / 72); + this.props.Document.scrollY = NumCast(doc.y) - offset; + } } else { const newPanX = NumCast(doc.x) + NumCast(doc.width) / 2; const newPanY = NumCast(doc.y) + NumCast(doc.height) / 2; @@ -416,6 +421,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.setPan(newPanX, newPanY); this.Document.panTransformType = "Ease"; + Doc.BrushDoc(this.props.Document); this.props.focus(this.props.Document); willZoom && this.setScaleToZoom(doc, scale); diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 72fff8e53..53b720a9e 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -171,7 +171,7 @@ export class LinkFollowBox extends React.Component { @undoBatch openFullScreen = () => { if (LinkFollowBox.destinationDoc) { - let view: DocumentView | null = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc); + let view = DocumentManager.Instance.getDocumentView(LinkFollowBox.destinationDoc); view && CollectionDockingView.Instance && CollectionDockingView.Instance.OpenFullScreen(view); } } @@ -250,10 +250,10 @@ export class LinkFollowBox extends React.Component { let dockingFunc = (document: Doc) => { (LinkFollowBox._addDocTab || this.props.addDocTab)(document, undefined, "inTab"); SelectionManager.DeselectAll(); }; if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 && targetContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, async document => dockingFunc(document), undefined, targetContext); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, async document => dockingFunc(document), targetContext); } else if (LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor1 && sourceContext) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, document => dockingFunc(sourceContext!)); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, document => dockingFunc(sourceContext!)); if (LinkFollowBox.sourceDoc && LinkFollowBox.destinationDoc) { if (guid) { let views = DocumentManager.Instance.getDocumentViews(jumpToDoc); @@ -264,12 +264,11 @@ export class LinkFollowBox extends React.Component { } } else if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, undefined, undefined, - NumCast((LinkFollowBox.destinationDoc === LinkFollowBox.linkDoc.anchor2 ? LinkFollowBox.linkDoc.anchor2Page : LinkFollowBox.linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom); } else { - DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, false, dockingFunc); + DocumentManager.Instance.jumpToDocument(jumpToDoc, shouldZoom, dockingFunc); } this.highlightDoc(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a903f8fe2..3273abc1d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -206,7 +206,7 @@ export class DocumentView extends DocComponent(Docu buttonClick = async (altKey: boolean, ctrlKey: boolean) => { let maximizedDocs = await DocListCastAsync(this.props.Document.maximizedDocs); let summarizedDocs = await DocListCastAsync(this.props.Document.summarizedDocs); - let linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); + let linkDocs = LinkManager.Instance.getAllRelatedLinks(this.props.Document); let expandedDocs: Doc[] = []; expandedDocs = maximizedDocs ? [...maximizedDocs, ...expandedDocs] : expandedDocs; expandedDocs = summarizedDocs ? [...summarizedDocs, ...expandedDocs] : expandedDocs; @@ -223,28 +223,10 @@ export class DocumentView extends DocComponent(Docu expandedDocs.forEach(maxDoc => (!this.props.addDocTab(maxDoc, undefined, "close") && this.props.addDocTab(maxDoc, undefined, maxLocation))); } } - else if (linkedDocs.length) { - SelectionManager.DeselectAll(); - let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document) && !d.anchor1anchored); - let second = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor2 as Doc, this.props.Document) && !d.anchor2anchored); - let firstUnshown = first.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0); - let secondUnshown = second.filter(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0); - if (firstUnshown.length) first = [firstUnshown[0]]; - if (secondUnshown.length) second = [secondUnshown[0]]; - let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : second.length ? [second[0].anchor1 as Doc, second[0].anchor1 as Doc] : undefined; - - // @TODO: shouldn't always follow target context - let linkedFwdContextDocs = [first.length ? await (first[0].targetContext) as Doc : undefined, undefined]; - let linkedFwdPage = [first.length ? NumCast(first[0].anchor2Page, undefined) : undefined, undefined]; - - if (linkedFwdDocs && !linkedFwdDocs.some(l => l instanceof Promise)) { - let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab"); - let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionDoc) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined; - DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, - // open up target if it's not already in view ... by zooming into the button document first and setting flag to reset zoom afterwards - doc => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)), - linkedFwdPage[altKey ? 1 : 0], targetContext); - } + else if (linkDocs.length) { + DocumentManager.Instance.FollowLink(this.props.Document, + (doc: Doc, maxLocation: string) => this.props.focus(this.props.Document, true, 1, () => this.props.addDocTab(doc, undefined, maxLocation)), + ctrlKey, altKey, this.props.ContainingCollectionDoc); } } @@ -352,15 +334,9 @@ export class DocumentView extends DocComponent(Docu if (de.data instanceof DragManager.AnnotationDragData) { /// this whole section for handling PDF annotations looks weird. Need to rethink this to make it cleaner e.stopPropagation(); - let sourceDoc = de.data.annotationDocument; - let targetDoc = this.props.Document; - let annotations = await DocListCastAsync(sourceDoc.annotations); - sourceDoc.linkedToDoc = true; - de.data.targetContext = this.props.ContainingCollectionDoc; - targetDoc.targetContext = de.data.targetContext; - annotations && annotations.forEach(anno => anno.target = targetDoc); - - DocUtils.MakeLink(sourceDoc, targetDoc, this.props.ContainingCollectionDoc, `Link from ${StrCast(sourceDoc.title)}`); + (de.data as any).linkedToDoc = true; + + DocUtils.MakeLink({ doc: de.data.annotationDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, `Link from ${StrCast(de.data.annotationDocument.title)}`); } if (de.data instanceof DragManager.DocumentDragData && de.data.applyAsTemplate) { Doc.ApplyTemplateTo(de.data.draggedDocuments[0], this.props.Document); @@ -371,7 +347,7 @@ export class DocumentView extends DocComponent(Docu // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true); // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView); de.data.linkSourceDocument !== this.props.Document && - (de.data.linkDocument = DocUtils.MakeLink(de.data.linkSourceDocument, this.props.Document, this.props.ContainingCollectionDoc, undefined, "in-text link being created")); // TODODO this is where in text links get passed + (de.data.linkDocument = DocUtils.MakeLink({ doc: de.data.linkSourceDocument }, { doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, "in-text link being created")); // TODODO this is where in text links get passed } } @@ -407,7 +383,7 @@ export class DocumentView extends DocComponent(Docu let portalID = (this.Document.title + ".portal").replace(/^-/, "").replace(/\([0-9]*\)$/, ""); DocServer.GetRefField(portalID).then(existingPortal => { let portal = existingPortal instanceof Doc ? existingPortal : Docs.Create.FreeformDocument([], { width: (this.Document.width || 0) + 10, height: this.Document.height || 0, title: portalID }); - DocUtils.MakeLink(this.props.Document, portal, undefined, portalID); + DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: portal }, portalID, "portal link"); this.Document.isButton = true; }); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 449f56b16..749886d9a 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -9,7 +9,7 @@ import { Fragment, Node, Node as ProsNode, NodeType, Slice, Mark, ResolvedPos } import { EditorState, Plugin, Transaction, TextSelection, NodeSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { DateField } from '../../../new_fields/DateField'; -import { Doc, DocListCast, Opt, WidthSym } from "../../../new_fields/Doc"; +import { Doc, DocListCast, Opt, WidthSym, DocListCastAsync } from "../../../new_fields/Doc"; import { Copy, Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; import { RichTextField } from "../../../new_fields/RichTextField"; @@ -230,7 +230,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.dataDoc[key] = doc || Docs.Create.FreeformDocument([], { title: value, width: 500, height: 500 }, value); DocUtils.Publish(this.dataDoc[key] as Doc, value, this.props.addDocument, this.props.removeDocument); if (linkDoc) { (linkDoc as Doc).anchor2 = this.dataDoc[key] as Doc; } - else DocUtils.MakeLink(this.dataDoc, this.dataDoc[key] as Doc, undefined, "Ref:" + value, undefined, undefined, id, true); + else DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: this.dataDoc[key] as Doc }, "Ref:" + value, "link to named target", id, true); }); }); }); @@ -342,7 +342,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let model: NodeType = [".mov", ".mp4"].includes(url) ? schema.nodes.video : schema.nodes.image; let pos = this._editorView!.posAtCoords({ left: de.x, top: de.y }); this._editorView!.dispatch(this._editorView!.state.tr.insert(pos!.pos, model.create({ src: url, docid: target[Id] }))); - DocUtils.MakeLink(this.dataDoc, target, undefined, "ImgRef:" + target.title, undefined, undefined, target[Id]); + DocUtils.MakeLink({ doc: this.dataDoc, ctx: this.props.ContainingCollectionDoc }, { doc: target }, "ImgRef:" + target.title); this.tryUpdateHeight(); e.stopPropagation(); } else if (de.data instanceof DragManager.DocumentDragData) { @@ -667,55 +667,42 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe handlePaste = (view: EditorView, event: Event, slice: Slice): boolean => { let cbe = event as ClipboardEvent; - let docId: string; - let regionId: string; - if (!cbe.clipboardData) { - return false; - } - let linkId: string; - docId = cbe.clipboardData.getData("dash/pdfOrigin"); - regionId = cbe.clipboardData.getData("dash/pdfRegion"); - if (!docId || !regionId) { - return false; - } - - DocServer.GetRefField(docId).then(doc => { - DocServer.GetRefField(regionId).then(region => { - if (!(doc instanceof Doc) || !(region instanceof Doc)) { - return; - } - - let annotations = DocListCast(region.annotations); - annotations.forEach(anno => anno.target = this.props.Document); - let fieldExtDoc = Doc.fieldExtensionDoc(doc, "data"); - let targetAnnotations = DocListCast(fieldExtDoc.annotations); - if (targetAnnotations) { - targetAnnotations.push(region); - fieldExtDoc.annotations = new List(targetAnnotations); - } + const pdfDocId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfOrigin"); + const pdfRegionId = cbe.clipboardData && cbe.clipboardData.getData("dash/pdfRegion"); + if (pdfDocId && pdfRegionId) { + DocServer.GetRefField(pdfDocId).then(pdfDoc => { + DocServer.GetRefField(pdfRegionId).then(pdfRegion => { + if ((pdfDoc instanceof Doc) && (pdfRegion instanceof Doc)) { + setTimeout(async () => { + let targetAnnotations = await DocListCastAsync(Doc.fieldExtensionDoc(pdfDoc, "data").annotations);// bcz: NO... this assumes the pdf is using its 'data' field. need to have the PDF's view handle updating its own annotations + targetAnnotations && targetAnnotations.push(pdfRegion); + }); - let link = DocUtils.MakeLink(this.props.Document, region, doc); - if (link) { - cbe.clipboardData!.setData("dash/linkDoc", link[Id]); - linkId = link[Id]; - let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(doc.title))); - slice = new Slice(frag, slice.openStart, slice.openEnd); - var tr = view.state.tr.replaceSelection(slice); - view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); - } + let link = DocUtils.MakeLink({ doc: this.props.Document, ctx: this.props.ContainingCollectionDoc }, { doc: pdfRegion, ctx: pdfDoc }, "note on " + pdfDoc.title, "pasted PDF link"); + if (link) { + cbe.clipboardData!.setData("dash/linkDoc", link[Id]); + let linkId = link[Id]; + let frag = addMarkToFrag(slice.content, (node: Node) => addLinkMark(node, StrCast(pdfDoc.title), linkId)); + slice = new Slice(frag, slice.openStart, slice.openEnd); + var tr = view.state.tr.replaceSelection(slice); + view.dispatch(tr.scrollIntoView().setMeta("paste", true).setMeta("uiEvent", "paste")); + } + } + }); }); - }); + return true; + } + return false; - return true; function addMarkToFrag(frag: Fragment, marker: (node: Node) => Node) { const nodes: Node[] = []; frag.forEach(node => nodes.push(marker(node))); return Fragment.fromArray(nodes); } - function addLinkMark(node: Node, title: string) { + function addLinkMark(node: Node, title: string, linkId: string) { if (!node.isText) { - const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title)); + const content = addMarkToFrag(node.content, (node: Node) => addLinkMark(node, title, linkId)); return node.copy(content); } const marks = [...node.marks]; @@ -879,16 +866,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe if (jumpToDoc) { if (DocumentManager.Instance.getDocumentView(jumpToDoc)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey, undefined, undefined, NumCast((jumpToDoc === linkDoc.anchor2 ? linkDoc.anchor2Page : linkDoc.anchor1Page))); + DocumentManager.Instance.jumpToDocument(jumpToDoc, e.altKey); return; } } if (targetContext && (!jumpToDoc || targetContext !== await jumpToDoc.annotationOn)) { - DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), undefined, targetContext); + DocumentManager.Instance.jumpToDocument(jumpToDoc || targetContext, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab"), targetContext); } else if (jumpToDoc) { - DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(jumpToDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); } else { - DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); + DocumentManager.Instance.jumpToDocument(linkDoc, ctrlKey, document => this.props.addDocTab(document, undefined, location ? location : "inTab")); } } }); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9e9fe1324..fe4f75cad 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -299,7 +299,7 @@ export class ImageBox extends DocComponent(ImageD let rotation = NumCast(this.dataDoc.rotation) % 180; let realsize = rotation === 90 || rotation === 270 ? { height: size.width, width: size.height } : size; let aspect = realsize.height / realsize.width; - if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(NumCast(layoutdoc.height) - realsize.height) > 1 || Math.abs(NumCast(layoutdoc.width) - realsize.width) > 1)) { + if (layoutdoc.nativeHeight !== 0 && layoutdoc.nativeWidth !== 0 && (Math.abs(1 - NumCast(layoutdoc.nativeHeight) / NumCast(layoutdoc.nativeWidth) / (realsize.height / realsize.width)) > 0.1)) { setTimeout(action(() => { layoutdoc.height = layoutdoc[WidthSym]() * aspect; layoutdoc.nativeHeight = realsize.height; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 617b580dd..88cd2cdc4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -57,11 +57,8 @@ export class PDFBox extends DocComponent(PdfDocumen } loaded = (nw: number, nh: number, np: number) => { this.dataDoc.numPages = np; - if (!this.Document.nativeWidth || !this.Document.nativeHeight || !this.Document.scrollHeight) { - let oldaspect = (this.Document.nativeHeight || 0) / (this.Document.nativeWidth || 1); - this.Document.nativeWidth = nw * 96 / 72; - this.Document.nativeHeight = this.Document.nativeHeight ? nw * 96 / 72 * oldaspect : nh * 96 / 72; - } + this.Document.nativeWidth = nw * 96 / 72; + this.Document.nativeHeight = nh * 96 / 72; !this.Document.fitWidth && !this.Document.ignoreAspect && (this.Document.height = this.Document[WidthSym]() * (nh / nw)); } @@ -177,12 +174,14 @@ export class PDFBox extends DocComponent(PdfDocumen ContextMenu.Instance.addItem({ description: "Pdf Funcs...", subitems: funcs, icon: "asterisk" }); } + _initialScale: number | undefined; render() { const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField); let classname = "pdfBox-cont" + (InkingControl.Instance.selectedTool || !this.active ? "" : "-interactive"); let noPdf = !(pdfUrl instanceof PdfField) || !this._pdf; - if (!noPdf && (this.props.isSelected() || this.props.ScreenToLocalTransform().Scale < 2.5)) this._everActive = true; - return (!this._everActive ? + if (this._initialScale === undefined) this._initialScale = this.props.ScreenToLocalTransform().Scale; + if (this.props.isSelected() || this.props.Document.scrollY !== undefined) this._everActive = true; + return (noPdf || (!this._everActive && this.props.ScreenToLocalTransform().Scale > 2.5) ?
{` ${this.props.Document.title}`} @@ -203,11 +202,11 @@ export class PDFBox extends DocComponent(PdfDocumen setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView} renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth} Document={this.props.Document} DataDoc={this.dataDoc} ContentScaling={this.props.ContentScaling} - addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} + addDocTab={this.props.addDocTab} GoToPage={this.gotoPage} focus={this.props.focus} pinToPres={this.props.pinToPres} addDocument={this.props.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} - fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this.props.ScreenToLocalTransform().Scale < 2.5 ? true : false} /> + fieldKey={this.props.fieldKey} fieldExtensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> {this.settingsPanel()}
); } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index f44ca99b9..180ed9032 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -43,8 +43,8 @@ export class PresBox extends React.Component { value.forEach((item, i) => { if (item instanceof Doc && item.type !== DocumentType.PRESELEMENT) { let pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); - Doc.GetProto(pinDoc).target = item; - Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.target instanceof Doc) && this.target.title.toString()'); + Doc.GetProto(pinDoc).presentationTargetDoc = item; + Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('(this.presentationTargetDoc instanceof Doc) && this.presentationTargetDoc.title.toString()'); value.splice(i, 1, pinDoc); } }); @@ -124,13 +124,13 @@ export class PresBox extends React.Component { this.childDocs.forEach((doc, ind) => { //the order of cases is aligned based on priority if (doc.hideTillShownButton && ind <= index) { - (doc.target as Doc).opacity = 1; + (doc.presentationTargetDoc as Doc).opacity = 1; } if (doc.hideAfterButton && ind < index) { - (doc.target as Doc).opacity = 0; + (doc.presentationTargetDoc as Doc).opacity = 0; } if (doc.fadeButton && ind < index) { - (doc.target as Doc).opacity = 0.5; + (doc.presentationTargetDoc as Doc).opacity = 0.5; } }); } @@ -145,13 +145,13 @@ export class PresBox extends React.Component { //the order of cases is aligned based on priority if (key.hideAfterButton && ind >= index) { - (key.target as Doc).opacity = 1; + (key.presentationTargetDoc as Doc).opacity = 1; } if (key.fadeButton && ind >= index) { - (key.target as Doc).opacity = 1; + (key.presentationTargetDoc as Doc).opacity = 1; } if (key.hideTillShownButton && ind > index) { - (key.target as Doc).opacity = 0; + (key.presentationTargetDoc as Doc).opacity = 0; } }); } @@ -162,7 +162,7 @@ export class PresBox extends React.Component { * te option open, navigates to that element. */ navigateToElement = async (curDoc: Doc, fromDocIndex: number) => { - let fromDoc = this.childDocs[fromDocIndex].target as Doc; + let fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc; let docToJump = curDoc; let willZoom = false; @@ -190,7 +190,7 @@ export class PresBox extends React.Component { //docToJump stayed same meaning, it was not in the group or was the last element in the group if (docToJump === curDoc) { //checking if curDoc has navigation open - let target = await curDoc.target as Doc; + let target = await curDoc.presentationTargetDoc as Doc; if (curDoc.navButton) { DocumentManager.Instance.jumpToDocument(target, false); } else if (curDoc.showButton) { @@ -210,8 +210,8 @@ export class PresBox extends React.Component { let curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(await docToJump.target as Doc, willZoom); - let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.target as Doc); + await DocumentManager.Instance.jumpToDocument(await docToJump.presentationTargetDoc as Doc, willZoom); + let newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc); curDoc.viewScale = newScale; //saving the scale that user was on if (curScale !== 1) { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index b7d9a1eab..573197117 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -129,7 +129,7 @@ export class VideoBox extends DocComponent(VideoD width: 150, height: height / width * 150, title: "--snapshot" + NumCast(this.props.Document.curPage) + " image-" }); this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.addDocument && this.props.ContainingCollectionView.props.addDocument(imageSummary, false); - DocUtils.MakeLink(imageSummary, this.props.Document); + DocUtils.MakeLink({doc:imageSummary}, {doc: this.props.Document}, "snapshot from " + this.props.Document.title, "video frame snapshot"); } }); } diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 3ed85f6a5..f7f52b3ef 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, HeightSym, WidthSym, Opt } from "../../../new_fields/Doc"; +import { Doc, DocListCast, HeightSym, WidthSym, Opt, DocListCastAsync } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; import { List } from "../../../new_fields/List"; import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; @@ -14,6 +14,7 @@ interface IAnnotationProps { fieldExtensionDoc: Doc; addDocTab: (document: Doc, dataDoc: Opt, where: string) => boolean; pinToPres: (document: Doc) => void; + focus: (doc: Doc) => void; } export default class Annotation extends React.Component { @@ -60,6 +61,7 @@ class RegionAnnotation extends React.Component { } componentWillUnmount() { + this._brushDisposer && this._brushDisposer(); this._reactionDisposer && this._reactionDisposer(); } @@ -94,14 +96,11 @@ class RegionAnnotation extends React.Component { PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); } else if (e.button === 0) { - let targetDoc = await Cast(this.props.document.target, Doc); - if (targetDoc) { - let context = await Cast(targetDoc.targetContext, Doc); - if (context) { - DocumentManager.Instance.jumpToDocument(targetDoc, false, false, - ((doc) => this.props.addDocTab(targetDoc!, undefined, e.ctrlKey ? "onRight" : "inTab")), - undefined, undefined); - } + let annoGroup = await Cast(this.props.document.group, Doc); + if (annoGroup) { + DocumentManager.Instance.FollowLink(annoGroup, + (doc: Doc, maxLocation: string) => this.props.addDocTab(doc, undefined, e.ctrlKey ? "onRight" : "inTab"), + false, false, undefined); } } } diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index 2202351ee..e62542014 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -31,7 +31,7 @@ export default class PDFMenu extends React.Component { @observable public Pinned: boolean = false; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = emptyFunction; - public Highlight: (d: Doc | undefined, color: string) => void = emptyFunction; + public Highlight: (color: string) => void = emptyFunction; public Delete: () => void = emptyFunction; public Snippet: (marquee: { left: number, top: number, width: number, height: number }) => void = emptyFunction; public AddTag: (key: string, value: string) => boolean = returnFalse; @@ -156,11 +156,11 @@ export default class PDFMenu extends React.Component { @action highlightClicked = (e: React.MouseEvent) => { if (!this.Pinned) { - this.Highlight(undefined, "#f4f442"); + this.Highlight("#f4f442"); } else { this.Highlighting = !this.Highlighting; - this.Highlight(undefined, "#f4f442"); + this.Highlight("#f4f442"); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 33226eac4..20dfc4d8c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -10,7 +10,6 @@ import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import smoothScroll, { Utils, emptyFunction, returnOne } from "../../../Utils"; -import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; import { DragManager } from "../../util/DragManager"; import { CompiledScript, CompileScript } from "../../util/Scripting"; @@ -44,6 +43,7 @@ interface IViewerProps { select: (isCtrlPressed: boolean) => void; startupLive: boolean; renderDepth: number; + focus: (doc: Doc) => void; isSelected: () => boolean; loaded: (nw: number, nh: number, np: number) => void; active: () => boolean; @@ -108,7 +108,8 @@ export class PDFViewer extends React.Component { // file address of the pdf this._coverPath = JSON.parse(await rp.get(Utils.prepend(`/thumbnail${this.props.url.substring("files/".length, this.props.url.length - ".pdf".length)}-${NumCast(this.props.Document.curPage, 1)}.PNG`))); runInAction(() => this._showWaiting = this._showCover = true); - this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1 && this.setupPdfJsViewer(), { fireImmediately: this.props.startupLive }); + this.props.startupLive && this.setupPdfJsViewer(); + this._selectionReactionDisposer = reaction(() => this.props.isSelected(), () => (this.props.isSelected() && SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true }); this._reactionDisposer = reaction( () => this.props.Document.scrollY, (scrollY) => { @@ -136,19 +137,11 @@ export class PDFViewer extends React.Component { if (this.props.active() && e.clipboardData) { e.clipboardData.setData("text/plain", this._selectionText); e.clipboardData.setData("dash/pdfOrigin", this.props.Document[Id]); - e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument(undefined, "#0390fc")[Id]); + e.clipboardData.setData("dash/pdfRegion", this.makeAnnotationDocument("#0390fc")[Id]); e.preventDefault(); } } - paste = (e: ClipboardEvent) => { - if (e.clipboardData && e.clipboardData.getData("dash/pdfOrigin") === this.props.Document[Id]) { - let linkDocId = e.clipboardData.getData("dash/linkDoc"); - linkDocId && DocServer.GetRefField(linkDocId).then(async (link) => - (link instanceof Doc) && (Doc.GetProto(link).anchor2 = this.makeAnnotationDocument(await Cast(Doc.GetProto(link), Doc), "#0390fc", false))); - } - } - setSelectionText = (text: string) => this._selectionText = text; @action @@ -194,13 +187,6 @@ export class PDFViewer extends React.Component { { fireImmediately: true } ); - document.removeEventListener("copy", this.copy); - document.addEventListener("copy", this.copy); - document.addEventListener("pagesinit", action(() => { - this.pdfViewer.currentScaleValue = this._zoomed = 1; - this.gotoPage(NumCast(this.props.Document.curPage, 1)); - })); - document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); this.createPdfViewer(); } @@ -212,6 +198,13 @@ export class PDFViewer extends React.Component { } return; } + document.removeEventListener("copy", this.copy); + document.addEventListener("copy", this.copy); + document.addEventListener("pagesinit", action(() => { + this.pdfViewer.currentScaleValue = this._zoomed = 1; + this.gotoPage(NumCast(this.props.Document.curPage, 1)); + })); + document.addEventListener("pagerendered", action(() => this._showCover = this._showWaiting = false)); var pdfLinkService = new PDFJSViewer.PDFLinkService(); let pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, @@ -229,19 +222,18 @@ export class PDFViewer extends React.Component { } @action - makeAnnotationDocument = (sourceDoc: Doc | undefined, color: string, createLink: boolean = true): Doc => { + makeAnnotationDocument = (color: string): Doc => { let mainAnnoDoc = Docs.Create.InstanceFromProto(new Doc(), "", {}); let mainAnnoDocProto = Doc.GetProto(mainAnnoDoc); let annoDocs: Doc[] = []; let minY = Number.MAX_VALUE; - if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1 && !createLink) { + if (this._savedAnnotations.size() === 1 && this._savedAnnotations.values()[0].length === 1) { let anno = this._savedAnnotations.values()[0][0]; let annoDoc = Docs.Create.FreeformDocument([], { backgroundColor: "rgba(255, 0, 0, 0.1)", title: "Annotation on " + StrCast(this.props.Document.title) }); if (anno.style.left) annoDoc.x = parseInt(anno.style.left); if (anno.style.top) annoDoc.y = parseInt(anno.style.top); if (anno.style.height) annoDoc.height = parseInt(anno.style.height); if (anno.style.width) annoDoc.width = parseInt(anno.style.width); - annoDoc.target = sourceDoc; annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; @@ -258,7 +250,6 @@ export class PDFViewer extends React.Component { if (anno.style.top) annoDoc.y = parseInt(anno.style.top); if (anno.style.height) annoDoc.height = parseInt(anno.style.height); if (anno.style.width) annoDoc.width = parseInt(anno.style.width); - annoDoc.target = sourceDoc; annoDoc.group = mainAnnoDoc; annoDoc.color = color; annoDoc.type = AnnotationTypes.Region; @@ -272,9 +263,6 @@ export class PDFViewer extends React.Component { } mainAnnoDocProto.title = "Annotation on " + StrCast(this.props.Document.title); mainAnnoDocProto.annotationOn = this.props.Document; - if (sourceDoc && createLink) { - DocUtils.MakeLink(sourceDoc, mainAnnoDocProto, undefined, `Annotation from ${StrCast(this.props.Document.title)}`); - } this._savedAnnotations.clear(); this.Index = -1; return mainAnnoDoc; @@ -529,7 +517,7 @@ export class PDFViewer extends React.Component { } if (PDFMenu.Instance.Highlighting) { - this.highlight(undefined, "goldenrod"); + this.highlight("goldenrod"); } else { PDFMenu.Instance.StartDrag = this.startDrag; @@ -540,9 +528,9 @@ export class PDFViewer extends React.Component { } @action - highlight = (targetDoc: Doc | undefined, color: string) => { + highlight = (color: string) => { // creates annotation documents for current highlights - let annotationDoc = this.makeAnnotationDocument(targetDoc, color, false); + let annotationDoc = this.makeAnnotationDocument(color); Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, annotationDoc); return annotationDoc; } @@ -555,20 +543,14 @@ export class PDFViewer extends React.Component { startDrag = (e: PointerEvent, ele: HTMLElement): void => { e.preventDefault(); e.stopPropagation(); - let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "New Annotation" }); - targetDoc.targetPage = this.getPageFromScroll(this._marqueeY); - let annotationDoc = this.highlight(undefined, "red"); - annotationDoc.linkedToDoc = false; + let targetDoc = Docs.Create.TextDocument({ width: 200, height: 200, title: "Note linked to " + this.props.Document.title }); + let annotationDoc = this.highlight("red"); let dragData = new DragManager.AnnotationDragData(this.props.Document, annotationDoc, targetDoc); DragManager.StartAnnotationDrag([ele], dragData, e.pageX, e.pageY, { handlers: { - dragComplete: () => { - if (!annotationDoc.linkedToDoc) { - let annotations = DocListCast(annotationDoc.annotations); - annotations && annotations.forEach(anno => anno.target = targetDoc); - DocUtils.MakeLink(annotationDoc, targetDoc, dragData.targetContext, `Annotation from ${StrCast(this.props.Document.title)}`); - } - } + dragComplete: () => !(dragData as any).linkedToDoc && + DocUtils.MakeLink({ doc: annotationDoc }, { doc: dragData.dropDocument, ctx: dragData.targetContext }, `Annotation from ${StrCast(this.props.Document.title)}`, "link from PDF") + }, hideSource: false }); @@ -602,6 +584,7 @@ export class PDFViewer extends React.Component { @action.bound removeDocument(doc: Doc): boolean { + Doc.GetProto(doc).annotationOn = undefined; //TODO This won't create the field if it doesn't already exist let targetDataDoc = this.props.fieldExtensionDoc; let targetField = this.props.fieldExt; @@ -634,10 +617,10 @@ export class PDFViewer extends React.Component { getCoverImage = () => { if (!this.props.Document[HeightSym]() || !this.props.Document.nativeHeight) { - setTimeout(() => { + setTimeout((() => { this.props.Document.height = this.props.Document[WidthSym]() * this._coverPath.height / this._coverPath.width; this.props.Document.nativeHeight = nativeWidth * this._coverPath.height / this._coverPath.width; - }, 0); + }).bind(this), 0); } let nativeWidth = NumCast(this.props.Document.nativeWidth); let nativeHeight = NumCast(this.props.Document.nativeHeight); @@ -659,7 +642,7 @@ export class PDFViewer extends React.Component { @computed get annotationLayer() { return
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => - )} + )}
; } @computed get pdfViewerDiv() { @@ -686,7 +669,8 @@ export class PDFViewer extends React.Component { setPreviewCursor={this.setPreviewCursor} PanelHeight={() => NumCast(this.props.Document.scrollHeight, NumCast(this.props.Document.nativeHeight))} PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : NumCast(this.props.Document.nativeWidth)} - focus={emptyFunction} + VisibleHeight={() => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96} + focus={this.props.focus} isSelected={this.props.isSelected} select={emptyFunction} active={this.active} @@ -694,7 +678,7 @@ export class PDFViewer extends React.Component { whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} moveDocument={this.moveDocument} - addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} + addDocument={(doc: Doc, allow: boolean | undefined) => { Doc.GetProto(doc).annotationOn = this.props.Document; Doc.AddDocToList(this.props.fieldExtensionDoc, this.props.fieldExt, doc); return true; }} CollectionView={this.props.ContainingCollectionView} ScreenToLocalTransform={this.scrollXf} ruleProvider={undefined} diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index de3242d32..daf000dc7 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -63,13 +63,11 @@ export class PresElementBox extends React.Component { this.hideTillShownButton = !this.hideTillShownButton; if (!this.hideTillShownButton) { if (this.myIndex >= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { - if (this.presentationDoc.presStatus) { - if (this.myIndex > this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0; - } + if (this.presentationDoc.presStatus && this.myIndex > this.currentIndex) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0; } } } @@ -85,14 +83,12 @@ export class PresElementBox extends React.Component { this.hideAfterButton = !this.hideAfterButton; if (!this.hideAfterButton) { if (this.myIndex <= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { if (this.fadeButton) this.fadeButton = false; - if (this.presentationDoc.presStatus) { - if (this.myIndex < this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0; - } + if (this.presentationDoc.presStatus && this.myIndex < this.currentIndex) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0; } } } @@ -108,14 +104,12 @@ export class PresElementBox extends React.Component { this.fadeButton = !this.fadeButton; if (!this.fadeButton) { if (this.myIndex <= this.currentIndex) { - (this.props.Document.target as Doc).opacity = 1; + (this.props.Document.presentationTargetDoc as Doc).opacity = 1; } } else { this.hideAfterButton = false; - if (this.presentationDoc.presStatus) { - if (this.myIndex < this.currentIndex) { - (this.props.Document.target as Doc).opacity = 0.5; - } + if (this.presentationDoc.presStatus && (this.myIndex < this.currentIndex)) { + (this.props.Document.presentationTargetDoc as Doc).opacity = 0.5; } } } @@ -162,7 +156,7 @@ export class PresElementBox extends React.Component { * presentation element. */ renderEmbeddedInline = () => { - if (!this.embedOpen || !(this.props.Document.target instanceof Doc)) { + if (!this.embedOpen || !(this.props.Document.presentationTargetDoc instanceof Doc)) { return (null); } @@ -175,8 +169,8 @@ export class PresElementBox extends React.Component { width: propDocWidth === 0 ? "auto" : propDocWidth * scale(), }}> { let pbi = "presElementBox-interaction"; return (
{ p.focus(p.Document); e.stopPropagation(); }}> {treecontainer ? (null) : <> diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index aeffc81c4..58304cebb 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -665,10 +665,12 @@ export namespace Doc { export function BrushDoc(doc: Doc) { brushManager.BrushedDoc.set(doc, true); brushManager.BrushedDoc.set(Doc.GetDataDoc(doc), true); + return doc; } export function UnBrushDoc(doc: Doc) { brushManager.BrushedDoc.delete(doc); brushManager.BrushedDoc.delete(Doc.GetDataDoc(doc)); + return doc; } export class HighlightBrush { -- cgit v1.2.3-70-g09d2 From 3ba540a0aecc624f9c81f32102441d6d137ade85 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 5 Oct 2019 10:35:33 -0400 Subject: fixed adding to presentation box when minimized --- src/client/views/OverlayView.tsx | 11 ++++++----- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 5 +++-- src/client/views/nodes/PresBox.tsx | 9 ++++----- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index bb6dd5076..95c7b2683 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -145,6 +145,7 @@ export class OverlayView extends React.Component { return (null); } return CurrentUserUtils.UserDocument.overlays instanceof Doc && DocListCast(CurrentUserUtils.UserDocument.overlays.data).map(d => { + d.inOverlay = true; let offsetx = 0, offsety = 0; let onPointerMove = action((e: PointerEvent) => { if (e.buttons === 1) { @@ -170,14 +171,14 @@ export class OverlayView extends React.Component { document.addEventListener("pointerup", onPointerUp); }; return
- { - if (this.props.Document.lockedPosition || this.isAnnotationOverlay) return; + if (this.props.Document.lockedPosition || this.props.Document.inOverlay || this.isAnnotationOverlay) return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -360,7 +360,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @action setPan(panX: number, panY: number) { - if (!this.props.Document.lockedPosition) { + if (!this.props.Document.lockedPosition || this.props.Document.inOverlay) { this.props.Document.panTransformType = "None"; var scale = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 54fafc20b..e50008fdf 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -111,6 +111,7 @@ export const documentSchema = createSchema({ type: "string", // enumerated type of document maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab) lockedPosition: "boolean", // whether the document can be spatially manipulated + inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently borderRounding: "string", // border radius rounding of document searchFields: "string", // the search fields to display when this document matches a search in its metadata heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc) @@ -243,7 +244,7 @@ export class DocumentView extends DocComponent(Docu this._hitTemplateDrag = true; } } - if (this.active && e.button === 0 && !this.Document.lockedPosition) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); + if (this.active && e.button === 0 && !this.Document.lockedPosition && !this.Document.inOverlay) e.stopPropagation(); // events stop at the lowest document that is active. if right dragging, we let it go through though to allow for context menu clicks. PointerMove callbacks should remove themselves if the move event gets stopPropagated by a lower-level handler (e.g, marquee drag); document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -253,7 +254,7 @@ export class DocumentView extends DocComponent(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive()) && !this.Document.lockedPosition) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this) || this.props.parentActive()) && !this.Document.lockedPosition && !this.Document.inOverlay) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { if (!e.altKey && !this.topMost && e.buttons === 1) { document.removeEventListener("pointermove", this.onPointerMove); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 180ed9032..15fafb022 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -314,12 +314,11 @@ export class PresBox extends React.Component { } toggleMinimize = undoBatch(action((e: React.PointerEvent) => { - if (this.props.Document.minimizedView) { - this.props.Document.minimizedView = false; + if (this.props.Document.inOverlay) { Doc.RemoveDocFromList((CurrentUserUtils.UserDocument.overlays as Doc), this.props.fieldKey, this.props.Document); CollectionDockingView.AddRightSplit(this.props.Document, this.props.DataDoc); + this.props.Document.inOverlay = false; } else { - this.props.Document.minimizedView = true; this.props.Document.x = e.clientX + 25; this.props.Document.y = e.clientY - 25; this.props.addDocTab && this.props.addDocTab(this.props.Document, this.props.DataDoc, "close"); @@ -370,9 +369,9 @@ export class PresBox extends React.Component { - +
- {this.props.Document.minimizedView ? (null) : + {this.props.Document.inOverlay ? (null) :
-- cgit v1.2.3-70-g09d2 From 891b9706ddabc0a73ea6b25dc504297d6efb90fe Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 21 Oct 2019 22:03:02 -0400 Subject: big cleanup of layoutStrings, fieldExt, fieldKey, etc --- src/client/apis/youtube/YoutubeBox.tsx | 2 +- src/client/documents/Documents.ts | 55 ++++---- src/client/northstar/dash-nodes/HistogramBox.tsx | 2 +- .../util/Import & Export/DirectoryImportBox.tsx | 2 +- src/client/views/CollectionLinearView.tsx | 4 +- src/client/views/DocComponent.tsx | 45 +++---- src/client/views/DocumentDecorations.tsx | 13 +- src/client/views/MainView.tsx | 5 +- .../views/collections/CollectionSchemaView.tsx | 1 - .../views/collections/CollectionStackingView.tsx | 2 - src/client/views/collections/CollectionSubView.tsx | 9 +- src/client/views/collections/CollectionView.tsx | 18 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 15 +-- .../collections/collectionFreeForm/MarqueeView.tsx | 24 ++-- src/client/views/linking/LinkFollowBox.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 8 +- src/client/views/nodes/ButtonBox.tsx | 20 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 18 +-- src/client/views/nodes/ColorBox.tsx | 10 +- src/client/views/nodes/DocuLinkBox.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 71 ++-------- src/client/views/nodes/FieldView.tsx | 5 +- src/client/views/nodes/FontIconBox.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 27 ++-- src/client/views/nodes/IconBox.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 18 +-- src/client/views/nodes/KeyValueBox.tsx | 5 +- src/client/views/nodes/PDFBox.tsx | 8 +- src/client/views/nodes/PresBox.tsx | 2 +- src/client/views/nodes/QueryBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 40 +++--- src/client/views/nodes/WebBox.tsx | 9 +- src/client/views/pdf/PDFViewer.tsx | 17 ++- .../views/presentationview/PresElementBox.tsx | 148 ++++++++++----------- src/new_fields/Doc.ts | 3 +- src/new_fields/documentSchemas.ts | 51 +++++++ 36 files changed, 319 insertions(+), 348 deletions(-) create mode 100644 src/new_fields/documentSchemas.ts (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index d73988bb8..bed812852 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -40,7 +40,7 @@ export class YoutubeBox extends React.Component { @observable curVideoTemplates: VideoTemplate[] = []; - public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(YoutubeBox, fieldKey); } /** * When component mounts, last search's results are laoded in based on the back up stored diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 937d3c058..a400e68a3 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -84,7 +84,8 @@ export interface DocumentOptions { columnWidth?: number; fontSize?: number; curPage?: number; - currentTimecode?: number; + currentTimecode?: number; // the current timecode of a time-based document (e.g., current time of a video) + displayTimecode?: number; // the time that a document should be displayed (e.g., time an annotation should be displayed on a video) documentText?: string; borderRounding?: string; boxShadow?: string; @@ -119,11 +120,11 @@ export namespace Docs { export namespace Prototypes { - type LayoutSource = { LayoutString: (ext?: string) => string }; + type LayoutSource = { LayoutString: (key: string) => string }; type PrototypeTemplate = { layout: { view: LayoutSource, - ext?: string, // optional extension field for layout source + dataField: string }, options?: Partial }; @@ -133,80 +134,80 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.TEXT, { - layout: { view: FormattedTextBox }, + layout: { view: FormattedTextBox, dataField: data }, options: { height: 150, backgroundColor: "#f1efeb", defaultBackgroundColor: "#f1efeb" } }], [DocumentType.HIST, { - layout: { view: HistogramBox }, + layout: { view: HistogramBox, dataField: data }, options: { height: 300, backgroundColor: "black" } }], [DocumentType.QUERY, { - layout: { view: QueryBox }, + layout: { view: QueryBox, dataField: data }, options: { width: 400 } }], [DocumentType.COLOR, { - layout: { view: ColorBox }, + layout: { view: ColorBox, dataField: data }, options: { nativeWidth: 220, nativeHeight: 300 } }], [DocumentType.IMG, { - layout: { view: ImageBox }, + layout: { view: ImageBox, dataField: data }, options: {} }], [DocumentType.WEB, { - layout: { view: WebBox }, + layout: { view: WebBox, dataField: data }, options: { height: 300 } }], [DocumentType.COL, { - layout: { view: CollectionView }, + layout: { view: CollectionView, dataField: data }, options: { panX: 0, panY: 0, scale: 1, width: 500, height: 500 } }], [DocumentType.KVP, { - layout: { view: KeyValueBox }, + layout: { view: KeyValueBox, dataField: data }, options: { height: 150 } }], [DocumentType.VID, { - layout: { view: VideoBox }, + layout: { view: VideoBox, dataField: data }, options: { currentTimecode: 0 }, }], [DocumentType.AUDIO, { - layout: { view: AudioBox }, + layout: { view: AudioBox, dataField: data }, options: { height: 32 } }], [DocumentType.PDF, { - layout: { view: PDFBox }, + layout: { view: PDFBox, dataField: data }, options: { nativeWidth: 1200, curPage: 1 } }], [DocumentType.ICON, { - layout: { view: IconBox }, + layout: { view: IconBox, dataField: data }, options: { width: Number(MINIMIZED_ICON_SIZE), height: Number(MINIMIZED_ICON_SIZE) }, }], [DocumentType.IMPORT, { - layout: { view: DirectoryImportBox }, + layout: { view: DirectoryImportBox, dataField: data }, options: { height: 150 } }], [DocumentType.LINKDOC, { data: new List(), - layout: { view: EmptyBox }, + layout: { view: EmptyBox, dataField: data }, }], [DocumentType.YOUTUBE, { - layout: { view: YoutubeBox } + layout: { view: YoutubeBox, dataField: data } }], [DocumentType.BUTTON, { - layout: { view: ButtonBox }, + layout: { view: ButtonBox, dataField: data }, }], [DocumentType.PRES, { - layout: { view: PresBox }, + layout: { view: PresBox, dataField: data }, options: {} }], [DocumentType.FONTICON, { - layout: { view: FontIconBox }, + layout: { view: FontIconBox, dataField: data }, options: { width: 40, height: 40, borderRounding: "100%" }, }], [DocumentType.LINKFOLLOW, { - layout: { view: LinkFollowBox } + layout: { view: LinkFollowBox, dataField: data } }], [DocumentType.PRESELEMENT, { - layout: { view: PresElementBox } + layout: { view: PresElementBox, dataField: data } }], ]); @@ -285,7 +286,7 @@ export namespace Docs { // synthesize the default options, the type and title from computed values and // whatever options pertain to this specific prototype let options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) }; - options.layout = layout.view.LayoutString(); + options.layout = layout.view.LayoutString(layout.dataField); return Doc.assign(new Doc(prototypeId, true), { ...options, baseLayout: options.layout }); } @@ -701,12 +702,12 @@ export namespace DocUtils { linkDocProto.linkDescription = description; linkDocProto.anchor1 = source.doc; - linkDocProto.anchor1Context = source.ctx; - linkDocProto.anchor1Timecode = source.doc.currentTimecode; - linkDocProto.anchor1Groups = new List([]); linkDocProto.anchor2 = target.doc; + linkDocProto.anchor1Context = source.ctx; linkDocProto.anchor2Context = target.ctx; + linkDocProto.anchor1Groups = new List([]); linkDocProto.anchor2Groups = new List([]); + linkDocProto.anchor1Timecode = source.doc.currentTimecode; linkDocProto.anchor2Timecode = target.doc.currentTimecode; linkDocProto.layoutKey1 = DocuLinkBox.LayoutString("anchor1"); linkDocProto.layoutKey2 = DocuLinkBox.LayoutString("anchor2"); diff --git a/src/client/northstar/dash-nodes/HistogramBox.tsx b/src/client/northstar/dash-nodes/HistogramBox.tsx index b81eafbee..854135648 100644 --- a/src/client/northstar/dash-nodes/HistogramBox.tsx +++ b/src/client/northstar/dash-nodes/HistogramBox.tsx @@ -24,7 +24,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; @observer export class HistogramBox extends React.Component { - public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(HistogramBox, fieldStr); } + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(HistogramBox, fieldStr); } private _dropXRef = React.createRef(); private _dropYRef = React.createRef(); private _dropXDisposer?: DragManager.DragDropDisposer; diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index d74b51993..f27d05487 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -50,7 +50,7 @@ export default class DirectoryImportBox extends React.Component @observable private uploading = false; @observable private removeHover = false; - public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DirectoryImportBox, fieldKey); } constructor(props: FieldViewProps) { super(props); diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 4e03b8c95..1f28ef35d 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -10,7 +10,8 @@ import { Transform } from '../util/Transform'; import "./CollectionLinearView.scss"; import { CollectionViewType } from './collections/CollectionBaseView'; import { CollectionSubView } from './collections/CollectionSubView'; -import { documentSchema, DocumentView } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; +import { documentSchema } from '../../new_fields/documentSchemas'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -75,7 +76,6 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { (schemaCtor: (doc: Doc) => T) { class Component extends React.Component

{ //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - @computed - get Document(): T { - return schemaCtor(this.props.Document); - } - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc!) : Doc.GetProto(this.props.Document); } - @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } + @computed get Document(): T { return schemaCtor(this.props.Document); } + @computed get layoutDoc() { return PositionDocument(Doc.Layout(this.props.Document)); } } return Component; } - -/// DocStaticProps return a base class for React views of document fields that are interactive only when selected (e.g. ColorBox) -interface DocStaticProps { +/// DocStaticProps return a base class for React document views that have data extensions but aren't annotatable (e.g. AudioBox, FormattedTextBox) +interface DocExtendableProps { Document: Doc; DataDoc?: Doc; fieldKey: string; isSelected: () => boolean; renderDepth: number; } -export function DocStaticComponent

(schemaCtor: (doc: Doc) => T) { +export function DocExtendableComponent

(schemaCtor: (doc: Doc) => T) { class Component extends React.Component

{ //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - @computed - get Document(): T { - return schemaCtor(this.props.Document); - } - @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc!) : Doc.GetProto(this.props.Document); } + @computed get Document(): T { return schemaCtor(this.props.Document); } + @computed get layoutDoc() { return Doc.Layout(this.props.Document); } + @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplateField ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } active = () => !this.props.Document.isBackground && (this.props.Document.forceActive || this.props.isSelected() || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools } @@ -59,23 +51,20 @@ interface DocAnnotatableProps { isSelected: () => boolean; renderDepth: number; } -export function DocAnnotatableComponent

(schemaCtor: (doc: Doc) => T, fieldExt: string) { +export function DocAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { class Component extends React.Component

{ _isChildActive = false; - _fieldExt = fieldExt; //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then - @computed - get Document(): T { - return schemaCtor(this.props.Document); - } + @computed get Document(): T { return schemaCtor(this.props.Document); } + @computed get layoutDoc() { return Doc.Layout(this.props.Document); } @computed get dataDoc() { return (this.props.DataDoc && this.props.Document.isTemplateField ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } - @computed get fieldExt() { return this._fieldExt; } + @computed get annotationsKey() { return "annotations"; } @action.bound removeDocument(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = undefined; - let value = this.extensionDoc && Cast(this.extensionDoc[this._fieldExt], listSpec(Doc), []); + let value = this.extensionDoc && Cast(this.extensionDoc[this.annotationsKey], listSpec(Doc), []); let index = value ? Doc.IndexOf(doc, value.map(d => d as Doc), true) : -1; return index !== -1 && value && value.splice(index, 1) ? true : false; } @@ -88,7 +77,7 @@ export function DocAnnotatableComponent

(schema @action.bound addDocument(doc: Doc): boolean { Doc.GetProto(doc).annotationOn = this.props.Document; - return this.extensionDoc && Doc.AddDocToList(this.extensionDoc, this._fieldExt, doc) ? true : false; + return this.extensionDoc && Doc.AddDocToList(this.extensionDoc, this.annotationsKey, doc) ? true : false; } whenActiveChanged = (isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 252f90d46..b46caf3ea 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -4,27 +4,26 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCastAsync } from "../../new_fields/Doc"; +import { PositionDocument } from '../../new_fields/documentSchemas'; import { List } from "../../new_fields/List"; import { ObjectField } from '../../new_fields/ObjectField'; -import { BoolCast, Cast, NumCast, StrCast } from "../../new_fields/Types"; +import { Cast, NumCast, StrCast } from "../../new_fields/Types"; import { CurrentUserUtils } from '../../server/authentication/models/current_user_utils'; -import { emptyFunction, Utils } from "../../Utils"; +import { Utils } from "../../Utils"; import { Docs, DocUtils } from "../documents/Documents"; import { DocumentManager } from "../util/DocumentManager"; import { DragManager } from "../util/DragManager"; import { SelectionManager } from "../util/SelectionManager"; +import { TooltipTextMenu } from '../util/TooltipTextMenu'; import { undoBatch, UndoManager } from "../util/UndoManager"; import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss"; import { CollectionView } from "./collections/CollectionView"; import { DocumentButtonBar } from './DocumentButtonBar'; import './DocumentDecorations.scss'; -import { PositionDocument } from './nodes/CollectionFreeFormDocumentView'; import { DocumentView } from "./nodes/DocumentView"; import { FieldView } from "./nodes/FieldView"; -import { FormattedTextBox } from "./nodes/FormattedTextBox"; import { IconBox } from "./nodes/IconBox"; import React = require("react"); -import { TooltipTextMenu } from '../util/TooltipTextMenu'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -281,7 +280,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let selectedDocs = SelectionManager.SelectedDocuments().map(sd => sd); if (selectedDocs.length > 1) { - this._iconDoc = this._iconDoc ? this._iconDoc : this.createIcon(SelectionManager.SelectedDocuments(), CollectionView.LayoutString()); + this._iconDoc = this._iconDoc ? this._iconDoc : this.createIcon(SelectionManager.SelectedDocuments(), CollectionView.LayoutString("")); this.moveIconDoc(this._iconDoc); } else { this.getIconDoc(selectedDocs[0]).then(icon => icon && this.moveIconDoc(this._iconDoc = icon)); @@ -339,7 +338,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let iconDoc: Doc | undefined = await Cast(doc.minimizedDoc, Doc); if (!iconDoc || !DocumentManager.Instance.getDocumentView(iconDoc)) { - const layout = StrCast(doc.layout, FieldView.LayoutString(DocumentView)); + const layout = StrCast(doc.layout, FieldView.LayoutString(DocumentView, "")); iconDoc = this.createIcon([docView], layout); } return iconDoc; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d4b92a110..55a61f098 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -270,7 +270,6 @@ export class MainView extends React.Component { doc) { this._heightMap.set(key, sectionHeight); } - get layoutDoc() { return Doc.Layout(this.props.Document); } - get Sections() { if (!this.sectionFilter || this.sectionHeaders instanceof Promise) return new Map(); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 7f16fe9e0..55365de8c 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -41,7 +41,7 @@ export interface SubCollectionViewProps extends CollectionViewProps { ruleProvider: Doc | undefined; children?: never | (() => JSX.Element[]) | React.ReactNode; isAnnotationOverlay?: boolean; - fieldExt: string; + annotationsKey: string; } export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @@ -74,12 +74,15 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { this._childLayoutDisposer && this._childLayoutDisposer(); } + @computed get dataDoc() { return this.props.DataDoc && this.props.Document.isTemplateField ? Doc.GetProto(this.props.DataDoc!) : Doc.GetProto(this.props.Document); } + @computed get extensionDoc() { return Doc.fieldExtensionDoc(this.dataDoc, this.props.fieldKey); } + // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc. // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through // to its children which may be templates. - // The name of the data field comes from fieldExt if it's an extension, or fieldKey otherwise. + // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey' @computed get dataField() { - return this.props.fieldExt ? (this.extensionDoc ? this.extensionDoc[this.props.fieldExt] : undefined) : this.dataDoc[this.props.fieldKey]; + return this.props.annotationsKey ? (this.extensionDoc ? this.extensionDoc[this.props.annotationsKey] : undefined) : this.dataDoc[this.props.fieldKey]; } get childLayoutPairs() { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index f3b0b17f8..f8eb28ade 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -32,7 +32,7 @@ library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faF @observer export class CollectionView extends React.Component { - public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(CollectionView, fieldStr); } + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } private _reactionDisposer: IReactionDisposer | undefined; @observable private _isLightboxOpen = false; @@ -63,18 +63,18 @@ export class CollectionView extends React.Component { private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => { let props = { ...this.props, ...renderProps }; switch (type) { - case CollectionViewType.Schema: return (); + case CollectionViewType.Schema: return (); // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip - case CollectionViewType.Docking: return (); - case CollectionViewType.Tree: return (); - case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } - case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } - case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (); } - case CollectionViewType.Linear: { return (); } + case CollectionViewType.Docking: return (); + case CollectionViewType.Tree: return (); + case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } + case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } + case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (); } + case CollectionViewType.Linear: { return (); } case CollectionViewType.Freeform: default: this.props.Document.freeformLayoutEngine = undefined; - return (); + return (); } return (null); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index cc89dc2d4..0419bc3fa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -25,8 +25,8 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss" import { ContextMenu } from "../../ContextMenu"; import { ContextMenuProps } from "../../ContextMenuItem"; import { InkingCanvas } from "../../InkingCanvas"; -import { CollectionFreeFormDocumentView, positionSchema } from "../../nodes/CollectionFreeFormDocumentView"; -import { documentSchema, DocumentViewProps } from "../../nodes/DocumentView"; +import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; +import { DocumentViewProps } from "../../nodes/DocumentView"; import { FormattedTextBox } from "../../nodes/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; import { CollectionSubView } from "../CollectionSubView"; @@ -35,6 +35,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); +import { documentSchema, positionSchema } from "../../DocComponent"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); @@ -677,13 +678,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { this.Document.fitH = this.contentBounds && (this.contentBounds.b - this.contentBounds.y); // if isAnnotationOverlay is set, then children will be stored in the extension document for the fieldKey. // otherwise, they are stored in fieldKey. All annotations to this document are stored in the extension document - return ( + return !this.extensionDoc ? (null) :

- + {!this.extensionDoc ? (null) : @@ -694,8 +694,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { {this.overlayViews} -
- ); +
; } } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 3eaad3ad7..e0bf4a2f0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -19,23 +19,24 @@ import { CollectionViewType } from "../CollectionBaseView"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; import "./MarqueeView.scss"; import React = require("react"); +import { SubCollectionViewProps } from "../CollectionSubView"; interface MarqueeViewProps { getContainerTransform: () => Transform; getTransform: () => Transform; - container: CollectionFreeFormView; addDocument: (doc: Doc) => boolean; activeDocuments: () => Doc[]; selectDocuments: (docs: Doc[]) => void; removeDocument: (doc: Doc) => boolean; addLiveTextDocument: (doc: Doc) => void; isSelected: () => boolean; + extensionDoc: Doc; isAnnotationOverlay?: boolean; setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; } @observer -export class MarqueeView extends React.Component +export class MarqueeView extends React.Component { private _mainCont = React.createRef(); @observable _lastX: number = 0; @@ -187,13 +188,13 @@ export class MarqueeView extends React.Component @action onPointerUp = (e: PointerEvent): void => { - if (!this.props.container.props.active()) this.props.selectDocuments([this.props.container.props.Document]); + if (!this.props.active()) this.props.selectDocuments([this.props.Document]); if (this._visible) { let mselect = this.marqueeSelect(); if (!e.shiftKey) { - SelectionManager.DeselectAll(mselect.length ? undefined : this.props.container.props.Document); + SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document); } - this.props.selectDocuments(mselect.length ? mselect : [this.props.container.props.Document]); + this.props.selectDocuments(mselect.length ? mselect : [this.props.Document]); } this.cleanupInteractions(true); @@ -246,12 +247,11 @@ export class MarqueeView extends React.Component } get ink() { // ink will be stored on the extension doc for the field (fieldKey) where the container's data is stored. - return this.props.container.extensionDoc && Cast(this.props.container.extensionDoc.ink, InkField); + return this.props.extensionDoc && Cast(this.props.extensionDoc.ink, InkField); } set ink(value: InkField | undefined) { - let cprops = this.props.container.props; - this.props.container.extensionDoc && (this.props.container.extensionDoc.ink = value); + this.props.extensionDoc && (this.props.extensionDoc.ink = value); } @undoBatch @@ -290,11 +290,11 @@ export class MarqueeView extends React.Component } let defaultPalette = ["rgb(114,229,239)", "rgb(255,246,209)", "rgb(255,188,156)", "rgb(247,220,96)", "rgb(122,176,238)", "rgb(209,150,226)", "rgb(127,235,144)", "rgb(252,188,189)", "rgb(247,175,81)",]; - let colorPalette = Cast(this.props.container.props.Document.colorPalette, listSpec("string")); - if (!colorPalette) this.props.container.props.Document.colorPalette = new List(defaultPalette); - let palette = Array.from(Cast(this.props.container.props.Document.colorPalette, listSpec("string")) as string[]); + let colorPalette = Cast(this.props.Document.colorPalette, listSpec("string")); + if (!colorPalette) this.props.Document.colorPalette = new List(defaultPalette); + let palette = Array.from(Cast(this.props.Document.colorPalette, listSpec("string")) as string[]); let usedPaletted = new Map(); - [...this.props.activeDocuments(), this.props.container.props.Document].map(child => { + [...this.props.activeDocuments(), this.props.Document].map(child => { let bg = StrCast(Doc.Layout(child).backgroundColor); if (palette.indexOf(bg) !== -1) { palette.splice(palette.indexOf(bg), 1); diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index 32ebe7c61..ef194624a 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -37,7 +37,7 @@ enum FollowOptions { @observer export class LinkFollowBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString(LinkFollowBox); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkFollowBox, fieldKey); } public static Instance: LinkFollowBox | undefined; @observable static linkDoc: Doc | undefined = undefined; @observable static destinationDoc: Doc | undefined = undefined; diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 3e5deb55b..4c1c3a465 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -4,18 +4,18 @@ import { observer } from "mobx-react"; import "./AudioBox.scss"; import { Cast } from "../../../new_fields/Types"; import { AudioField } from "../../../new_fields/URLField"; -import { DocStaticComponent } from "../DocComponent"; +import { DocExtendableComponent } from "../DocComponent"; import { makeInterface } from "../../../new_fields/Schema"; -import { documentSchema } from "./DocumentView"; +import { documentSchema } from "../../../new_fields/documentSchemas"; type AudioDocument = makeInterface<[typeof documentSchema]>; const AudioDocument = makeInterface(documentSchema); const defaultField: AudioField = new AudioField(new URL("http://techslides.com/demos/samples/sample.mp3")); @observer -export class AudioBox extends DocStaticComponent(AudioDocument) { +export class AudioBox extends DocExtendableComponent(AudioDocument) { - public static LayoutString() { return FieldView.LayoutString(AudioBox); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); } _ref = React.createRef(); componentDidMount() { diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index b4d33fb0f..1531d825b 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -3,11 +3,11 @@ import { faEdit } from '@fortawesome/free-regular-svg-icons'; import { action, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCastAsync, DocListCast } from '../../../new_fields/Doc'; +import { Doc, DocListCast } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { createSchema, makeInterface, listSpec } from '../../../new_fields/Schema'; import { ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, StrCast, Cast } from '../../../new_fields/Types'; +import { BoolCast, StrCast, Cast, FieldValue } from '../../../new_fields/Types'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { DocComponent } from '../DocComponent'; @@ -15,26 +15,28 @@ import './ButtonBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; import { ContextMenuProps } from '../ContextMenuItem'; import { ContextMenu } from '../ContextMenu'; +import { documentSchema } from '../../../new_fields/documentSchemas'; library.add(faEdit as any); const ButtonSchema = createSchema({ onClick: ScriptField, + buttonParams: listSpec("string"), text: "string" }); -type ButtonDocument = makeInterface<[typeof ButtonSchema]>; -const ButtonDocument = makeInterface(ButtonSchema); +type ButtonDocument = makeInterface<[typeof ButtonSchema, typeof documentSchema]>; +const ButtonDocument = makeInterface(ButtonSchema, documentSchema); @observer export class ButtonBox extends DocComponent(ButtonDocument) { - public static LayoutString() { return FieldView.LayoutString(ButtonBox); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ButtonBox, fieldKey); } private dropDisposer?: DragManager.DragDropDisposer; @computed get dataDoc() { return this.props.DataDoc && - (BoolCast(this.props.Document.isTemplateField) || BoolCast(this.props.DataDoc.isTemplateField) || + (this.Document.isTemplateField || BoolCast(this.props.DataDoc.isTemplateField) || this.props.DataDoc.layout === this.props.Document) ? this.props.DataDoc : Doc.GetProto(this.props.Document); } @@ -52,7 +54,7 @@ export class ButtonBox extends DocComponent(Butt let funcs: ContextMenuProps[] = []; funcs.push({ description: "Clear Script Params", event: () => { - let params = Cast(this.props.Document.buttonParams, listSpec("string")); + let params = FieldValue(this.Document.buttonParams); params && params.map(p => this.props.Document[p] = undefined); }, icon: "trash" }); @@ -70,13 +72,13 @@ export class ButtonBox extends DocComponent(Butt } // (!missingParams || !missingParams.length ? "" : "(" + missingParams.map(m => m + ":").join(" ") + ")") render() { - let params = Cast(this.props.Document.buttonParams, listSpec("string")); + let params = this.Document.buttonParams; let missingParams = params && params.filter(p => this.props.Document[p] === undefined); params && params.map(p => DocListCast(this.props.Document[p])); // bcz: really hacky form of prefetching ... return (
-
+
{(this.Document.text || this.Document.title)}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index e3ca02fa4..58cb831f8 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -2,14 +2,15 @@ import { random } from "animejs"; import { computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; -import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; -import { Cast, FieldValue, NumCast, StrCast } from "../../../new_fields/Types"; +import { listSpec } from "../../../new_fields/Schema"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { percent2frac } from "../../../Utils"; import { Transform } from "../../util/Transform"; import { DocComponent } from "../DocComponent"; import "./CollectionFreeFormDocumentView.scss"; -import { documentSchema, DocumentView, DocumentViewProps } from "./DocumentView"; +import { DocumentView, DocumentViewProps } from "./DocumentView"; import React = require("react"); +import { PositionDocument } from "../../../new_fields/documentSchemas"; export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { dataProvider?: (doc: Doc, dataDoc?: Doc) => { x: number, y: number, width: number, height: number, z: number, transition?: string } | undefined; @@ -20,15 +21,6 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { jitterRotation: number; transition?: string; } -export const positionSchema = createSchema({ - zIndex: "number", - x: "number", - y: "number", - z: "number", -}); - -export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>; -export const PositionDocument = makeInterface(documentSchema, positionSchema); @observer export class CollectionFreeFormDocumentView extends DocComponent(PositionDocument) { @@ -92,8 +84,6 @@ export class CollectionFreeFormDocumentView extends DocComponent this.clusterColor; - get layoutDoc() { return Doc.Layout(this.props.Document); } - @observable _animPos: number[] | undefined = undefined; finalPanelWidth = () => this.dataProvider ? this.dataProvider.width : this.panelWidth(); diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index fdcedb3a5..fda6d64f4 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -4,20 +4,20 @@ import { SketchPicker } from 'react-color'; import { FieldView, FieldViewProps } from './FieldView'; import "./ColorBox.scss"; import { InkingControl } from "../InkingControl"; -import { DocStaticComponent } from "../DocComponent"; -import { documentSchema } from "./DocumentView"; +import { DocExtendableComponent } from "../DocComponent"; import { makeInterface } from "../../../new_fields/Schema"; -import { trace, reaction, observable, action, IReactionDisposer } from "mobx"; +import { reaction, observable, action, IReactionDisposer } from "mobx"; import { SelectionManager } from "../../util/SelectionManager"; import { StrCast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; +import { documentSchema } from "../../../new_fields/documentSchemas"; type ColorDocument = makeInterface<[typeof documentSchema]>; const ColorDocument = makeInterface(documentSchema); @observer -export class ColorBox extends DocStaticComponent(ColorDocument) { - public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(ColorBox, fieldKey); } +export class ColorBox extends DocExtendableComponent(ColorDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); } _selectedDisposer: IReactionDisposer | undefined; _penDisposer: IReactionDisposer | undefined; diff --git a/src/client/views/nodes/DocuLinkBox.tsx b/src/client/views/nodes/DocuLinkBox.tsx index 22e44948a..7119b0db0 100644 --- a/src/client/views/nodes/DocuLinkBox.tsx +++ b/src/client/views/nodes/DocuLinkBox.tsx @@ -7,11 +7,11 @@ import { Utils } from '../../../Utils'; import { DocumentManager } from "../../util/DocumentManager"; import { DragLinksAsDocuments } from "../../util/DragManager"; import { DocComponent } from "../DocComponent"; -import { documentSchema } from "./DocumentView"; import "./DocuLinkBox.scss"; import { FieldView, FieldViewProps } from "./FieldView"; import React = require("react"); import { DocumentType } from "../../documents/DocumentTypes"; +import { documentSchema } from "../../../new_fields/documentSchemas"; type DocLinkSchema = makeInterface<[typeof documentSchema]>; const DocLinkDocument = makeInterface(documentSchema); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 9e36f0811..f741dae9a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,29 +1,37 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import * as fa from '@fortawesome/free-solid-svg-icons'; -import { action, computed, runInAction, trace, observable } from "mobx"; +import { action, computed, runInAction } from "mobx"; import { observer } from "mobx-react"; import * as rp from "request-promise"; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../new_fields/Doc"; +import { Document } from '../../../new_fields/documentSchemas'; import { Id } from '../../../new_fields/FieldSymbols'; -import { createSchema, listSpec, makeInterface } from "../../../new_fields/Schema"; +import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from '../../../new_fields/ScriptField'; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast, FieldValue } from "../../../new_fields/Types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { ImageField } from '../../../new_fields/URLField'; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; -import { emptyFunction, returnTrue, Utils, returnTransparent, returnOne } from "../../../Utils"; +import { emptyFunction, returnTransparent, returnTrue, Utils } from "../../../Utils"; +import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from "../../documents/Documents"; +import { DocumentType } from '../../documents/DocumentTypes'; import { ClientUtils } from '../../util/ClientUtils'; import { DictationManager } from '../../util/DictationManager'; import { DocumentManager } from "../../util/DocumentManager"; import { DragManager, dropActionType } from "../../util/DragManager"; import { LinkManager } from '../../util/LinkManager'; +import { Scripting } from '../../util/Scripting'; import { SelectionManager } from "../../util/SelectionManager"; +import SharingManager from '../../util/SharingManager'; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { CollectionViewType } from '../collections/CollectionBaseView'; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; +import { DictationOverlay } from '../DictationOverlay'; import { DocComponent } from "../DocComponent"; import { EditableView } from '../EditableView'; import { OverlayView } from '../OverlayView'; @@ -33,13 +41,6 @@ import { DocumentContentsView } from "./DocumentContentsView"; import "./DocumentView.scss"; import { FormattedTextBox } from './FormattedTextBox'; import React = require("react"); -import { DocumentType } from '../../documents/DocumentTypes'; -import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; -import { ImageField } from '../../../new_fields/URLField'; -import SharingManager from '../../util/SharingManager'; -import { Scripting } from '../../util/Scripting'; -import { DictationOverlay } from '../DictationOverlay'; -import { CollectionViewType } from '../collections/CollectionBaseView'; library.add(fa.faEdit); library.add(fa.faTrash); @@ -67,7 +68,6 @@ library.add(fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointRight, fa.faCom export interface DocumentViewProps { ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; - fieldKey: string; Document: Doc; DataDoc?: Doc; fitToBox?: boolean; @@ -96,41 +96,6 @@ export interface DocumentViewProps { layoutKey?: string; } -export const documentSchema = createSchema({ - // layout: "string", // this should be a "string" or Doc, but can't do that in schemas, so best to leave it out - title: "string", // document title (can be on either data document or layout) - nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set - nativeHeight: "number", // " - width: "number", // width of document in its container's coordinate system - height: "number", // " - backgroundColor: "string", // background color of document - opacity: "number", // opacity of document - //links: listSpec(Doc), // computed (readonly) list of links associated with this document - dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy") - removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped - onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) - onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. - dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. - ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document - autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents - isTemplateField: "boolean", // whether this document acts as a template layout for describing how other documents should be displayed - isBackground: "boolean", // whether document is a background element and ignores input events (can only selet with marquee) - type: "string", // enumerated type of document - maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab) - lockedPosition: "boolean", // whether the document can be spatially manipulated - inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently - borderRounding: "string", // border radius rounding of document - searchFields: "string", // the search fields to display when this document matches a search in its metadata - heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc) - showCaption: "string", // whether editable caption text is overlayed at the bottom of the document - showTitle: "string", // whether an editable title banner is displayed at tht top of the document - isButton: "boolean", // whether document functions as a button (overiding native interactions of its content) - ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) -}); - - -type Document = makeInterface<[typeof documentSchema]>; -const Document = makeInterface(documentSchema); @observer export class DocumentView extends DocComponent(Document) { @@ -572,13 +537,6 @@ export class DocumentView extends DocComponent(Docu }); } - - // the document containing the view layout information - will be the Document itself unless the Document has - // a layout field. In that case, all layout information comes from there unless overriden by Document - get layoutDoc(): Document { - return Document(Doc.Layout(this.props.Document)); - } - // does Document set a layout prop setsLayoutProp = (prop: string) => this.props.Document[prop] !== this.props.Document["default" + prop[0].toUpperCase() + prop.slice(1)]; // get the a layout prop by first choosing the prop from Document, then falling back to the layout doc otherwise. @@ -599,7 +557,6 @@ export class DocumentView extends DocComponent(Docu return ((Docu } } -Scripting.addGlobal(function toggleDetail(doc: any) { - doc.layoutKey = StrCast(doc.layoutKey, "layout") === "layout" ? "layoutCustom" : "layout"; -}); \ No newline at end of file +Scripting.addGlobal(function toggleDetail(doc: any) { doc.layoutKey = StrCast(doc.layoutKey, "layout") === "layout" ? "layoutCustom" : "layout"; }); \ No newline at end of file diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 7d69b5b51..5108954bb 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -52,9 +52,8 @@ export interface FieldViewProps { @observer export class FieldView extends React.Component { - public static LayoutString(fieldType: { name: string }, fieldStr: string = "data") { - return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"}/>`; - //"" + public static LayoutString(fieldType: { name: string }, fieldStr: string) { + return `<${fieldType.name} {...props} fieldKey={"${fieldStr}"}/>`; //e.g., "" } @computed diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index fd6a475fb..ae9273639 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -17,7 +17,7 @@ type FontIconDocument = makeInterface<[typeof FontIconSchema]>; const FontIconDocument = makeInterface(FontIconSchema); @observer export class FontIconBox extends DocComponent(FontIconDocument) { - public static LayoutString() { return FieldView.LayoutString(FontIconBox); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); } @observable _foregroundColor = "white"; _ref: React.RefObject = React.createRef(); _backgroundReaction: IReactionDisposer | undefined; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 995fcee17..5c2d39d98 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -33,7 +33,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import { TooltipLinkingMenu } from "../../util/TooltipLinkingMenu"; import { TooltipTextMenu } from "../../util/TooltipTextMenu"; import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { DocComponent } from "../DocComponent"; +import { DocExtendableComponent } from "../DocComponent"; import { DocumentButtonBar } from '../DocumentButtonBar'; import { DocumentDecorations } from '../DocumentDecorations'; import { InkingControl } from "../InkingControl"; @@ -44,6 +44,7 @@ import React = require("react"); import { ContextMenuProps } from '../ContextMenuItem'; import { ContextMenu } from '../ContextMenu'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { documentSchema } from '../../../new_fields/documentSchemas'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -62,16 +63,14 @@ const richTextSchema = createSchema({ export const GoogleRef = "googleDocId"; -type RichTextDocument = makeInterface<[typeof richTextSchema]>; -const RichTextDocument = makeInterface(richTextSchema); +type RichTextDocument = makeInterface<[typeof richTextSchema, typeof documentSchema]>; +const RichTextDocument = makeInterface(richTextSchema, documentSchema); type PullHandler = (exportState: Opt, dataDoc: Doc) => void; @observer -export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) { - public static LayoutString(fieldStr: string = "data") { - return FieldView.LayoutString(FormattedTextBox, fieldStr); - } +export class FormattedTextBox extends DocExtendableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) { + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } public static blankState = () => EditorState.create(FormattedTextBox.Instance.config); public static Instance: FormattedTextBox; private static _toolTipTextMenu: TooltipTextMenu | undefined = undefined; @@ -135,17 +134,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe return true; } - constructor(props: FieldViewProps) { + constructor(props: any) { super(props); FormattedTextBox.Instance = this; } public get CurrentDiv(): HTMLDivElement { return this._ref.current!; } - // the document containing the view layout information - will be the Document itself unless the Document has - // a layout field. In that case, all layout information comes from there unless overriden by Document - @computed get layoutDoc(): Doc { return Doc.Layout(this.props.Document); } - linkOnDeselect: Map = new Map(); doLinkOnDeselect() { @@ -266,8 +261,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe newLayout = Doc.MakeDelegate(draggedDoc); newLayout.layout = StrCast(newLayout.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${this.props.fieldKey}"}`); } - this.props.Document.layoutCustom = newLayout; - this.props.Document.layoutKey = "layoutCustom"; + this.Document.layoutCustom = newLayout; + this.Document.layoutKey = "layoutCustom"; e.stopPropagation(); // embed document when dragging with a userDropAction or an embedDoc flag set } else if (de.data.userDropAction || de.data.embedDoc) { @@ -1015,7 +1010,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe let scrollHeight = this._ref.current ? this._ref.current.scrollHeight : 0; if (!this.layoutDoc.isAnimating && this.layoutDoc.autoHeight && scrollHeight !== 0 && getComputedStyle(this._ref.current!.parentElement!).top === "0px") { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation - let nh = this.props.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0); + let nh = this.Document.isTemplateField ? 0 : NumCast(this.dataDoc.nativeHeight, 0); let dh = NumCast(this.layoutDoc.height, 0); this.layoutDoc.height = Math.max(10, (nh ? dh / nh * scrollHeight : scrollHeight) + (this.props.ChromeHeight ? this.props.ChromeHeight() : 0)); this.dataDoc.nativeHeight = nh ? scrollHeight : undefined; @@ -1052,7 +1047,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onPointerEnter={action(() => this._entered = true)} onPointerLeave={action(() => this._entered = false)} > -
+
{ diff --git a/src/client/views/nodes/IconBox.tsx b/src/client/views/nodes/IconBox.tsx index 4971f61b7..60f547b1e 100644 --- a/src/client/views/nodes/IconBox.tsx +++ b/src/client/views/nodes/IconBox.tsx @@ -24,7 +24,7 @@ library.add(faFilm, faTag, faTextHeight); @observer export class IconBox extends React.Component { - public static LayoutString() { return FieldView.LayoutString(IconBox); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(IconBox, fieldKey); } @observable _panelWidth: number = 0; @observable _panelHeight: number = 0; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 4d623a04f..4c2f8e22d 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -20,12 +20,12 @@ import { ContextMenu } from "../../views/ContextMenu"; import { ContextMenuProps } from '../ContextMenuItem'; import { DocAnnotatableComponent } from '../DocComponent'; import { InkingControl } from '../InkingControl'; -import { documentSchema } from './DocumentView'; import FaceRectangles from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { documentSchema } from '../../../new_fields/documentSchemas'; var requestImageSize = require('../../util/request-image-size'); var path = require('path'); const { Howl } = require('howler'); @@ -38,7 +38,9 @@ library.add(faFileAudio, faAsterisk); export const pageSchema = createSchema({ curPage: "number", fitWidth: "boolean", - rotation: "number" + rotation: "number", + googlePhotosUrl: "string", + googlePhotosTags: "string" }); interface Window { @@ -54,8 +56,8 @@ type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>; const ImageDocument = makeInterface(pageSchema, documentSchema); @observer -export class ImageBox extends DocAnnotatableComponent(ImageDocument, "annotations") { - public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(ImageBox, fieldKey); } +export class ImageBox extends DocAnnotatableComponent(ImageDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } private _imgRef: React.RefObject = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; @observable private _audioState = 0; @@ -251,7 +253,7 @@ export class ImageBox extends DocAnnotatableComponent this.recordAudioAnnotation(); considerGooglePhotosLink = () => { - const remoteUrl = StrCast(this.props.Document.googlePhotosUrl); + const remoteUrl = this.Document.googlePhotosUrl; return !remoteUrl ? (null) : ( { - const tags = StrCast(this.props.Document.googlePhotosTags); + const tags = this.Document.googlePhotosTags; return !tags ? (null) : (); } @@ -287,7 +289,7 @@ export class ImageBox extends DocAnnotatableComponent { + public static LayoutString(fieldStr: string) { return FieldView.LayoutString(KeyValueBox, fieldStr); } + private _mainCont = React.createRef(); private _keyHeader = React.createRef(); - @observable private rows: KeyValuePair[] = []; - public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(KeyValueBox, fieldStr); } + @observable private rows: KeyValuePair[] = []; @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index b1110daf4..396a5356a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -16,18 +16,18 @@ import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { DocAnnotatableComponent } from "../DocComponent"; import { PDFViewer } from "../pdf/PDFViewer"; -import { documentSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import { pageSchema } from "./ImageBox"; import "./PDFBox.scss"; import React = require("react"); +import { documentSchema } from '../../../new_fields/documentSchemas'; type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>; const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema); @observer -export class PDFBox extends DocAnnotatableComponent(PdfDocument, "annotations") { - public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(PDFBox, fieldKey); } +export class PDFBox extends DocAnnotatableComponent(PdfDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } private _keyValue: string = ""; private _valueValue: string = ""; private _scriptValue: string = ""; @@ -232,7 +232,7 @@ export class PDFBox extends DocAnnotatableComponent pinToPres={this.props.pinToPres} addDocument={this.addDocument} ScreenToLocalTransform={this.props.ScreenToLocalTransform} select={this.props.select} isSelected={this.props.isSelected} whenActiveChanged={this.whenActiveChanged} - fieldKey={this.props.fieldKey} extensionDoc={this.extensionDoc} startupLive={this._initialScale < 2.5 ? true : false} /> + fieldKey={this.props.fieldKey} startupLive={this._initialScale < 2.5 ? true : false} /> {this.settingsPanel()}
); } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 15fafb022..7ec5d0471 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -31,7 +31,7 @@ library.add(faEdit); @observer export class PresBox extends React.Component { - public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(PresBox, fieldKey); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } _docListChangedReaction: IReactionDisposer | undefined; componentDidMount() { this._docListChangedReaction = reaction(() => { diff --git a/src/client/views/nodes/QueryBox.tsx b/src/client/views/nodes/QueryBox.tsx index ced597b59..99b5810fc 100644 --- a/src/client/views/nodes/QueryBox.tsx +++ b/src/client/views/nodes/QueryBox.tsx @@ -18,7 +18,7 @@ library.add(faEdit); @observer export class QueryBox extends React.Component { - public static LayoutString(fieldKey?: string) { return FieldView.LayoutString(QueryBox, fieldKey); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(QueryBox, fieldKey); } _docListChangedReaction: IReactionDisposer | undefined; componentDidMount() { } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 64871ef41..48a699e58 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,33 +1,32 @@ import React = require("react"); -import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked, trace } from "mobx"; +import { library } from "@fortawesome/fontawesome-svg-core"; +import { faVideo } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, untracked } from "mobx"; import { observer } from "mobx-react"; import * as rp from 'request-promise'; +import { Doc } from "../../../new_fields/Doc"; import { InkTool } from "../../../new_fields/InkField"; -import { makeInterface, createSchema, listSpec } from "../../../new_fields/Schema"; -import { Cast, FieldValue, NumCast, BoolCast, StrCast } from "../../../new_fields/Types"; +import { createSchema, makeInterface } from "../../../new_fields/Schema"; +import { ScriptField } from "../../../new_fields/ScriptField"; +import { Cast, StrCast } from "../../../new_fields/Types"; import { VideoField } from "../../../new_fields/URLField"; import { RouteStore } from "../../../server/RouteStore"; -import { Utils, emptyFunction, returnOne } from "../../../Utils"; +import { emptyFunction, returnOne, Utils } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; +import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { DocAnnotatableComponent } from "../DocComponent"; import { DocumentDecorations } from "../DocumentDecorations"; import { InkingControl } from "../InkingControl"; -import { documentSchema } from "./DocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import "./VideoBox.scss"; -import { library } from "@fortawesome/fontawesome-svg-core"; -import { faVideo } from "@fortawesome/free-solid-svg-icons"; -import { Doc } from "../../../new_fields/Doc"; -import { ScriptField } from "../../../new_fields/ScriptField"; -import { positionSchema } from "./CollectionFreeFormDocumentView"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { documentSchema, positionSchema } from "../../../new_fields/documentSchemas"; var path = require('path'); export const timeSchema = createSchema({ - currentTimecode: "number", + currentTimecode: "number", // the current time of a video or other linear, time-based document. Note, should really get set on an extension field, but that's more complicated when it needs to be set since the extension doc needs to be found first }); type VideoDocument = makeInterface<[typeof documentSchema, typeof positionSchema, typeof timeSchema]>; const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema); @@ -35,7 +34,7 @@ const VideoDocument = makeInterface(documentSchema, positionSchema, timeSchema); library.add(faVideo); @observer -export class VideoBox extends DocAnnotatableComponent(VideoDocument, "annotations") { +export class VideoBox extends DocAnnotatableComponent(VideoDocument) { static _youtubeIframeCounter: number = 0; private _reactionDisposer?: IReactionDisposer; private _youtubeReactionDisposer?: IReactionDisposer; @@ -49,7 +48,7 @@ export class VideoBox extends DocAnnotatableComponent this._playing ? this.Pause() : this.Play() + onPlayDown = () => this._playing ? this.Pause() : this.Play(); - @action onFullDown = (e: React.PointerEvent) => { this.FullScreen(); e.stopPropagation(); e.preventDefault(); } - @action onSnapshot = (e: React.PointerEvent) => { this.Snapshot(); e.stopPropagation(); e.preventDefault(); } - @action onResetDown = (e: React.PointerEvent) => { this.Pause(); e.stopPropagation(); @@ -309,12 +304,12 @@ export class VideoBox extends DocAnnotatableComponent { this._isResetClick += Math.abs(e.movementX) + Math.abs(e.movementY); this.Seek(Math.max(0, (this.Document.currentTimecode || 0) + Math.sign(e.movementX) * 0.0333)); e.stopImmediatePropagation(); } + @action onResetUp = (e: PointerEvent) => { document.removeEventListener("pointermove", this.onResetMove, true); @@ -322,7 +317,6 @@ export class VideoBox extends DocAnnotatableComponent; const WebDocument = makeInterface(documentSchema); @observer -export class WebBox extends DocAnnotatableComponent(WebDocument, "annotations") { +export class WebBox extends DocAnnotatableComponent(WebDocument) { - public static LayoutString(fieldKey: string = "data") { return FieldView.LayoutString(WebBox, fieldKey); } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } @observable private collapsed: boolean = true; @observable private url: string = ""; - get layoutDoc() { return Doc.Layout(this.props.Document); } componentWillMount() { let field = Cast(this.props.Document[this.props.fieldKey], WebField); @@ -199,7 +198,7 @@ export class WebBox extends DocAnnotatableComponent ; - extensionDoc: Doc; PanelWidth: () => number; PanelHeight: () => number; ContentScaling: () => number; @@ -73,7 +72,7 @@ interface IViewerProps { * Handles rendering and virtualization of the pdf */ @observer -export class PDFViewer extends DocAnnotatableComponent(PdfDocument, "annotations") { +export class PDFViewer extends DocAnnotatableComponent(PdfDocument) { static _annotationStyle: any = addStyleSheet(); @observable private _pageSizes: { width: number, height: number }[] = []; @observable private _annotations: Doc[] = []; @@ -109,8 +108,8 @@ export class PDFViewer extends DocAnnotatableComponent this._script.run({ this: anno }, console.log, true).result); + return this.extensionDoc ? DocListCast(this.extensionDoc.annotations).filter( + anno => this._script.run({ this: anno }, console.log, true).result) : []; } @computed get nonDocAnnotations() { @@ -200,7 +199,7 @@ export class PDFViewer extends DocAnnotatableComponent this.props.extensionDoc && DocListCast(this.props.extensionDoc.annotations), + () => this.extensionDoc && DocListCast(this.extensionDoc.annotations), annotations => annotations && annotations.length && this.renderAnnotations(annotations, true), { fireImmediately: true }); @@ -629,10 +628,10 @@ export class PDFViewer extends DocAnnotatableComponent {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map((anno, index) => - )} + )}
(this.Document.scrollHeight || this.Document.nativeHeight || 0)} PanelWidth={() => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document.nativeWidth || 0)} @@ -673,7 +672,7 @@ export class PDFViewer extends DocAnnotatableComponent this._marqueeing; visibleHeight = () => this.props.PanelHeight() / this.props.ContentScaling() * 72 / 96; render() { - return (
{this.pdfViewerDiv} {this.annotationLayer} diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 0824808eb..5f758f496 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -5,18 +5,19 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { documentSchema } from '../../../new_fields/documentSchemas'; import { Id } from "../../../new_fields/FieldSymbols"; -import { BoolCast, NumCast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, returnEmptyString, returnFalse, returnOne } from "../../../Utils"; +import { createSchema, makeInterface } from '../../../new_fields/Schema'; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction, returnFalse } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { Transform } from "../../util/Transform"; import { CollectionViewType } from '../collections/CollectionBaseView'; -import { DocumentView } from "../nodes/DocumentView"; +import { CollectionSchemaPreview } from '../collections/CollectionSchemaView'; +import { DocComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import "./PresElementBox.scss"; import React = require("react"); -import { CollectionSchemaPreview } from '../collections/CollectionSchemaView'; - library.add(faArrowUp); library.add(fileSolid); @@ -24,34 +25,34 @@ library.add(faLocationArrow); library.add(fileRegular as any); library.add(faSearch); library.add(faArrowDown); + +export const presSchema = createSchema({ + presentationTargetDoc: Doc, + presBox: Doc, + presBoxKey: "string", + showButton: "boolean", + navButton: "boolean", + hideTillShownButton: "boolean", + fadeButton: "boolean", + hideAfterButton: "boolean", + groupButton: "boolean", + embedOpen: "boolean" +}); + +type PresDocument = makeInterface<[typeof presSchema, typeof documentSchema]>; +const PresDocument = makeInterface(presSchema, documentSchema); /** * This class models the view a document added to presentation will have in the presentation. * It involves some functionality for its buttons and options. */ @observer -export class PresElementBox extends React.Component { +export class PresElementBox extends DocComponent(PresDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); } - public static LayoutString() { return FieldView.LayoutString(PresElementBox); } - - @computed get myIndex() { return DocListCast(this.presentationDoc[this.presentationFieldKey]).indexOf(this.props.Document); } - @computed get presentationDoc() { return this.props.Document.presBox as Doc; } - @computed get presentationFieldKey() { return StrCast(this.props.Document.presBoxKey); } + @computed get indexInPres() { return DocListCast(this.presentationDoc[this.Document.presBoxKey || ""]).indexOf(this.props.Document); } + @computed get presentationDoc() { return Cast(this.Document.presBox, Doc) as Doc; } + @computed get targetDoc() { return this.Document.presentationTargetDoc as Doc } @computed get currentIndex() { return NumCast(this.presentationDoc.selectedDoc); } - @computed get showButton() { return BoolCast(this.props.Document.showButton); } - @computed get navButton() { return BoolCast(this.props.Document.navButton); } - @computed get hideTillShownButton() { return BoolCast(this.props.Document.hideTillShownButton); } - @computed get fadeButton() { return BoolCast(this.props.Document.fadeButton); } - @computed get hideAfterButton() { return BoolCast(this.props.Document.hideAfterButton); } - @computed get groupButton() { return BoolCast(this.props.Document.groupButton); } - @computed get embedOpen() { return BoolCast(this.props.Document.embedOpen); } - - set embedOpen(value: boolean) { this.props.Document.embedOpen = value; } - set showButton(val: boolean) { this.props.Document.showButton = val; } - set navButton(val: boolean) { this.props.Document.navButton = val; } - set hideTillShownButton(val: boolean) { this.props.Document.hideTillShownButton = val; } - set fadeButton(val: boolean) { this.props.Document.fadeButton = val; } - set hideAfterButton(val: boolean) { this.props.Document.hideAfterButton = val; } - set groupButton(val: boolean) { this.props.Document.groupButton = val; } /** * The function that is called on click to turn Hiding document till press option on/off. @@ -60,14 +61,14 @@ export class PresElementBox extends React.Component { @action onHideDocumentUntilPressClick = (e: React.MouseEvent) => { e.stopPropagation(); - this.hideTillShownButton = !this.hideTillShownButton; - if (!this.hideTillShownButton) { - if (this.myIndex >= this.currentIndex) { - (this.props.Document.presentationTargetDoc as Doc).opacity = 1; + this.Document.hideTillShownButton = !this.Document.hideTillShownButton; + if (!this.Document.hideTillShownButton) { + if (this.indexInPres >= this.currentIndex && this.targetDoc) { + this.targetDoc.opacity = 1; } } else { - if (this.presentationDoc.presStatus && this.myIndex > this.currentIndex) { - (this.props.Document.presentationTargetDoc as Doc).opacity = 0; + if (this.presentationDoc.presStatus && this.indexInPres > this.currentIndex && this.targetDoc) { + this.targetDoc.opacity = 0; } } } @@ -80,15 +81,15 @@ export class PresElementBox extends React.Component { @action onHideDocumentAfterPresentedClick = (e: React.MouseEvent) => { e.stopPropagation(); - this.hideAfterButton = !this.hideAfterButton; - if (!this.hideAfterButton) { - if (this.myIndex <= this.currentIndex) { - (this.props.Document.presentationTargetDoc as Doc).opacity = 1; + this.Document.hideAfterButton = !this.Document.hideAfterButton; + if (!this.Document.hideAfterButton) { + if (this.indexInPres <= this.currentIndex && this.targetDoc) { + this.targetDoc.opacity = 1; } } else { - if (this.fadeButton) this.fadeButton = false; - if (this.presentationDoc.presStatus && this.myIndex < this.currentIndex) { - (this.props.Document.presentationTargetDoc as Doc).opacity = 0; + if (this.Document.fadeButton) this.Document.fadeButton = false; + if (this.presentationDoc.presStatus && this.indexInPres < this.currentIndex && this.targetDoc) { + this.targetDoc.opacity = 0; } } } @@ -101,15 +102,15 @@ export class PresElementBox extends React.Component { @action onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => { e.stopPropagation(); - this.fadeButton = !this.fadeButton; - if (!this.fadeButton) { - if (this.myIndex <= this.currentIndex) { - (this.props.Document.presentationTargetDoc as Doc).opacity = 1; + this.Document.fadeButton = !this.Document.fadeButton; + if (!this.Document.fadeButton) { + if (this.indexInPres <= this.currentIndex && this.targetDoc) { + this.targetDoc.opacity = 1; } } else { - this.hideAfterButton = false; - if (this.presentationDoc.presStatus && (this.myIndex < this.currentIndex)) { - (this.props.Document.presentationTargetDoc as Doc).opacity = 0.5; + this.Document.hideAfterButton = false; + if (this.presentationDoc.presStatus && (this.indexInPres < this.currentIndex) && this.targetDoc) { + this.targetDoc.opacity = 0.5; } } } @@ -120,10 +121,10 @@ export class PresElementBox extends React.Component { @action onNavigateDocumentClick = (e: React.MouseEvent) => { e.stopPropagation(); - this.navButton = !this.navButton; - if (this.navButton) { - this.showButton = false; - if (this.currentIndex === this.myIndex) { + this.Document.navButton = !this.Document.navButton; + if (this.Document.navButton) { + this.Document.showButton = false; + if (this.currentIndex === this.indexInPres) { this.props.focus(this.props.Document); } } @@ -136,12 +137,12 @@ export class PresElementBox extends React.Component { onZoomDocumentClick = (e: React.MouseEvent) => { e.stopPropagation(); - this.showButton = !this.showButton; - if (!this.showButton) { + this.Document.showButton = !this.Document.showButton; + if (!this.Document.showButton) { this.props.Document.viewScale = 1; } else { - this.navButton = false; - if (this.currentIndex === this.myIndex) { + this.Document.navButton = false; + if (this.currentIndex === this.indexInPres) { this.props.focus(this.props.Document); } } @@ -151,13 +152,12 @@ export class PresElementBox extends React.Component { */ ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord]; - get layoutDoc() { return Doc.Layout(this.props.Document); } /** * The function that is responsible for rendering the a preview or not for this * presentation element. */ renderEmbeddedInline = () => { - if (!this.embedOpen || !(this.props.Document.presentationTargetDoc instanceof Doc)) { + if (!this.Document.embedOpen || !this.targetDoc) { return (null); } @@ -170,8 +170,9 @@ export class PresElementBox extends React.Component { width: propDocWidth === 0 ? "auto" : propDocWidth * scale(), }}> { } render() { - let p = this.props; - let treecontainer = this.props.ContainingCollectionDoc && this.props.ContainingCollectionDoc.viewType === CollectionViewType.Tree; - let className = "presElementBox-item" + (this.currentIndex === this.myIndex ? " presElementBox-selected" : ""); + let className = "presElementBox-item" + (this.currentIndex === this.indexInPres ? " presElementBox-selected" : ""); let pbi = "presElementBox-interaction"; return ( -
{ p.focus(p.Document); e.stopPropagation(); }}> +
{ this.props.focus(this.props.Document); e.stopPropagation(); }}> {treecontainer ? (null) : <> - {`${this.myIndex + 1}. ${p.Document.title}`} + {`${this.indexInPres + 1}. ${this.Document.title}`} - +
- - } - - - - - - - + } + + + + + + +
{this.renderEmbeddedInline()} diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index c6f654c33..08cb66d5f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -645,7 +645,8 @@ export namespace Doc { @observable BrushedDoc: ObservableMap = new ObservableMap(); } - // returns the active layout document for 'doc'. + // the document containing the view layout information - will be the Document itself unless the Document has + // a layout field. In that case, all layout information comes from there unless overriden by Document export function Layout(doc: Doc) { return Doc.LayoutField(doc) instanceof Doc ? doc[StrCast(doc.layoutKey, "layout")] as Doc : doc; } export function LayoutField(doc: Doc) { return doc[StrCast(doc.layoutKey, "layout")]; } const manager = new DocData(); diff --git a/src/new_fields/documentSchemas.ts b/src/new_fields/documentSchemas.ts new file mode 100644 index 000000000..8c3b62067 --- /dev/null +++ b/src/new_fields/documentSchemas.ts @@ -0,0 +1,51 @@ +import { makeInterface, createSchema, listSpec } from "./Schema"; +import { ScriptField } from "./ScriptField"; +import { Doc } from "./Doc"; + +export const documentSchema = createSchema({ + // layout: "string", // this should be a "string" or Doc, but can't do that in schemas, so best to leave it out + layoutKey: "string", // holds the field key for the field that actually holds the current lyoat + layoutCustom: Doc, // used to hold a custom layout (there's nothing special about this field .. any field could hold a custom layout that can be selected by setting 'layoutKey') + title: "string", // document title (can be on either data document or layout) + nativeWidth: "number", // native width of document which determines how much document contents are scaled when the document's width is set + nativeHeight: "number", // " + width: "number", // width of document in its container's coordinate system + height: "number", // " + color: "string", // foreground color of document + backgroundColor: "string", // background color of document + opacity: "number", // opacity of document + //links: listSpec(Doc), // computed (readonly) list of links associated with this document + dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias" or "copy") + removeDropProperties: listSpec("string"), // properties that should be removed from the alias/copy/etc of this document when it is dropped + onClick: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) + onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. + dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document. + ignoreAspect: "boolean", // whether aspect ratio should be ignored when laying out or manipulating the document + autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents + isTemplateField: "boolean", // whether this document acts as a template layout for describing how other documents should be displayed + isBackground: "boolean", // whether document is a background element and ignores input events (can only selet with marquee) + type: "string", // enumerated type of document + maximizeLocation: "string", // flag for where to place content when following a click interaction (e.g., onRight, inPlace, inTab) + lockedPosition: "boolean", // whether the document can be spatially manipulated + inOverlay: "boolean", // whether the document is rendered in an OverlayView which handles selection/dragging differently + borderRounding: "string", // border radius rounding of document + searchFields: "string", // the search fields to display when this document matches a search in its metadata + heading: "number", // the logical layout 'heading' of this document (used by rule provider to stylize h1 header elements, from h2, etc) + showCaption: "string", // whether editable caption text is overlayed at the bottom of the document + showTitle: "string", // whether an editable title banner is displayed at tht top of the document + isButton: "boolean", // whether document functions as a button (overiding native interactions of its content) + ignoreClick: "boolean", // whether documents ignores input clicks (but does not ignore manipulation and other events) +}); + +export const positionSchema = createSchema({ + zIndex: "number", + x: "number", + y: "number", + z: "number", +}); + +export type Document = makeInterface<[typeof documentSchema]>; +export const Document = makeInterface(documentSchema); + +export type PositionDocument = makeInterface<[typeof documentSchema, typeof positionSchema]>; +export const PositionDocument = makeInterface(documentSchema, positionSchema); -- cgit v1.2.3-70-g09d2 From 3b16a81e21942684d63f175b60629f85070715e1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 21 Oct 2019 23:55:19 -0400 Subject: got rid of CollectionBaseView --- src/client/documents/Documents.ts | 2 +- src/client/util/DictationManager.ts | 2 +- src/client/util/DropConverter.ts | 2 +- src/client/views/CollectionLinearView.tsx | 2 +- src/client/views/MainView.tsx | 4 +- .../views/collections/CollectionBaseView.scss | 26 --- .../views/collections/CollectionBaseView.tsx | 180 -------------------- .../views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/CollectionView.scss | 26 +++ src/client/views/collections/CollectionView.tsx | 183 ++++++++++++++++----- .../views/collections/CollectionViewChromes.tsx | 2 +- .../views/collections/ParentDocumentSelector.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/linking/LinkFollowBox.tsx | 2 +- src/client/views/nodes/ButtonBox.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/PresBox.tsx | 2 +- .../views/presentationview/PresElementBox.tsx | 4 +- src/client/views/search/SearchItem.tsx | 2 +- 20 files changed, 186 insertions(+), 266 deletions(-) delete mode 100644 src/client/views/collections/CollectionBaseView.scss delete mode 100644 src/client/views/collections/CollectionBaseView.tsx create mode 100644 src/client/views/collections/CollectionView.scss (limited to 'src/client/views/nodes/PresBox.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a400e68a3..b1406d5e1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -2,7 +2,7 @@ import { HistogramField } from "../northstar/dash-fields/HistogramField"; import { HistogramBox } from "../northstar/dash-nodes/HistogramBox"; import { HistogramOperation } from "../northstar/operations/HistogramOperation"; import { CollectionView } from "../views/collections/CollectionView"; -import { CollectionViewType } from "../views/collections/CollectionBaseView"; +import { CollectionViewType } from "../views/collections/CollectionView"; import { AudioBox } from "../views/nodes/AudioBox"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; import { ImageBox } from "../views/nodes/ImageBox"; diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index ae991635f..6bbd3d0ed 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -6,7 +6,7 @@ import { DocumentType } from "../documents/DocumentTypes"; import { Doc, Opt } from "../../new_fields/Doc"; import { List } from "../../new_fields/List"; import { Docs } from "../documents/Documents"; -import { CollectionViewType } from "../views/collections/CollectionBaseView"; +import { CollectionViewType } from "../views/collections/CollectionView"; import { Cast, CastCtor } from "../../new_fields/Types"; import { listSpec } from "../../new_fields/Schema"; import { AudioField, ImageField } from "../../new_fields/URLField"; diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index eea3da1bc..6b53333d7 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -1,5 +1,5 @@ import { DragManager } from "./DragManager"; -import { CollectionViewType } from "../views/collections/CollectionBaseView"; +import { CollectionViewType } from "../views/collections/CollectionView"; import { Doc, DocListCast } from "../../new_fields/Doc"; import { DocumentType } from "../documents/DocumentTypes"; import { ObjectField } from "../../new_fields/ObjectField"; diff --git a/src/client/views/CollectionLinearView.tsx b/src/client/views/CollectionLinearView.tsx index 1f28ef35d..31a518a6c 100644 --- a/src/client/views/CollectionLinearView.tsx +++ b/src/client/views/CollectionLinearView.tsx @@ -8,7 +8,7 @@ import { emptyFunction, returnEmptyString, returnOne, returnTrue, Utils } from ' import { DragManager } from '../util/DragManager'; import { Transform } from '../util/Transform'; import "./CollectionLinearView.scss"; -import { CollectionViewType } from './collections/CollectionBaseView'; +import { CollectionViewType } from './collections/CollectionView'; import { CollectionSubView } from './collections/CollectionSubView'; import { DocumentView } from './nodes/DocumentView'; import { documentSchema } from '../../new_fields/documentSchemas'; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 55a61f098..26e70c5c7 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -24,7 +24,7 @@ import { HistoryUtil } from '../util/History'; import SharingManager from '../util/SharingManager'; import { Transform } from '../util/Transform'; import { CollectionLinearView } from './CollectionLinearView'; -import { CollectionBaseView, CollectionViewType } from './collections/CollectionBaseView'; +import { CollectionViewType, CollectionView } from './collections/CollectionView'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { ContextMenu } from './ContextMenu'; import { DictationOverlay } from './DictationOverlay'; @@ -228,7 +228,7 @@ export class MainView extends React.Component { if (!state.nro) { DocServer.Control.makeReadOnly(); } - CollectionBaseView.SetSafeMode(true); + CollectionView.SetSafeMode(true); } else if (state.nro || state.nro === null || state.readonly === false) { } else if (doc.readOnly) { DocServer.Control.makeReadOnly(); diff --git a/src/client/views/collections/CollectionBaseView.scss b/src/client/views/collections/CollectionBaseView.scss deleted file mode 100644 index aff965469..000000000 --- a/src/client/views/collections/CollectionBaseView.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import "../globalCssVariables"; - -#collectionBaseView { - border-width: 0; - border-color: $light-color-secondary; - border-style: solid; - border-radius: 0 0 $border-radius $border-radius; - box-sizing: border-box; - border-radius: inherit; - width: 100%; - height: 100%; - overflow: auto; -} - -#google-tags { - transition: all 0.5s ease 0s; - width: 30px; - height: 30px; - position: absolute; - bottom: 15px; - left: 15px; - border: 2px solid black; - border-radius: 50%; - padding: 3px; - background: white; -} \ No newline at end of file diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx deleted file mode 100644 index 9f3d33e6f..000000000 --- a/src/client/views/collections/CollectionBaseView.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { action, computed, observable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { DateField } from '../../../new_fields/DateField'; -import { Doc, DocListCast } from '../../../new_fields/Doc'; -import { listSpec } from '../../../new_fields/Schema'; -import { BoolCast, Cast, FieldValue, PromiseValue, StrCast } from '../../../new_fields/Types'; -import { ImageField } from '../../../new_fields/URLField'; -import { DocumentManager } from '../../util/DocumentManager'; -import { SelectionManager } from '../../util/SelectionManager'; -import { ContextMenu } from '../ContextMenu'; -import { FieldViewProps } from '../nodes/FieldView'; -import './CollectionBaseView.scss'; - -export enum CollectionViewType { - Invalid, - Freeform, - Schema, - Docking, - Tree, - Stacking, - Masonry, - Pivot, - Linear, -} - -export namespace CollectionViewType { - - const stringMapping = new Map([ - ["invalid", CollectionViewType.Invalid], - ["freeform", CollectionViewType.Freeform], - ["schema", CollectionViewType.Schema], - ["docking", CollectionViewType.Docking], - ["tree", CollectionViewType.Tree], - ["stacking", CollectionViewType.Stacking], - ["masonry", CollectionViewType.Masonry], - ["pivot", CollectionViewType.Pivot], - ["linear", CollectionViewType.Linear] - ]); - - export const valueOf = (value: string) => { - return stringMapping.get(value.toLowerCase()); - }; - -} - -export interface CollectionRenderProps { - addDocument: (document: Doc) => boolean; - removeDocument: (document: Doc) => boolean; - moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; - active: () => boolean; - whenActiveChanged: (isActive: boolean) => void; -} - -export interface CollectionViewProps extends FieldViewProps { - onContextMenu?: (e: React.MouseEvent) => void; - children: (type: CollectionViewType, props: CollectionRenderProps) => JSX.Element | JSX.Element[] | null | (JSX.Element | null)[]; - className?: string; - contentRef?: React.Ref; -} - -@observer -export class CollectionBaseView extends React.Component { - @observable private static _safeMode = false; - static InSafeMode() { return this._safeMode; } - static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } - get collectionViewType(): CollectionViewType | undefined { - let Document = this.props.Document; - let viewField = Cast(Document.viewType, "number"); - if (CollectionBaseView._safeMode) { - if (viewField === CollectionViewType.Freeform) { - return CollectionViewType.Tree; - } - if (viewField === CollectionViewType.Invalid) { - return CollectionViewType.Freeform; - } - } - if (viewField !== undefined) { - return viewField; - } else { - return CollectionViewType.Invalid; - } - } - - active = (): boolean => { - var isSelected = this.props.isSelected(); - return isSelected || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0; - } - - //TODO should this be observable? - private _isChildActive = false; - whenActiveChanged = (isActive: boolean) => { - this._isChildActive = isActive; - this.props.whenActiveChanged(isActive); - } - - - @action.bound - addDocument(doc: Doc): boolean { - let targetDataDoc = Doc.GetProto(this.props.Document); - let targetField = this.props.fieldKey; - Doc.AddDocToList(targetDataDoc, targetField, doc); - let extension = Doc.fieldExtensionDoc(targetDataDoc, targetField); // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field - extension && (extension.lastModified = new DateField(new Date(Date.now()))); - Doc.GetProto(doc).lastOpened = new DateField; - return true; - } - - @action.bound - removeDocument(doc: Doc): boolean { - let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView); - docView && SelectionManager.DeselectDoc(docView); - //TODO This won't create the field if it doesn't already exist - let targetDataDoc = this.props.Document; - let targetField = this.props.fieldKey; - let value = Cast(targetDataDoc[targetField], listSpec(Doc), []); - let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); - index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - - if (index !== -1) { - value.splice(index, 1); - - // SelectionManager.DeselectAll() - ContextMenu.Instance.clearItems(); - return true; - } - return false; - } - - // this is called with the document that was dragged and the collection to move it into. - // if the target collection is the same as this collection, then the move will be allowed. - // otherwise, the document being moved must be able to be removed from its container before - // moving it into the target. - @action.bound - moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { - if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { - return true; - } - return this.removeDocument(doc) ? addDocument(doc) : false; - } - - showIsTagged = () => { - const children = DocListCast(this.props.Document.data); - const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto); - const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags); - if (allTagged) { - return ( - - ); - } - return (null); - } - - render() { - const props: CollectionRenderProps = { - addDocument: this.addDocument, - removeDocument: this.removeDocument, - moveDocument: this.moveDocument, - active: this.active, - whenActiveChanged: this.whenActiveChanged, - }; - const viewtype = this.collectionViewType; - return ( -
- {this.showIsTagged()} - {viewtype !== undefined ? this.props.children(viewtype, props) : (null)} -
- ); - } - -} diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 2cad41acb..47c355fc8 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -23,7 +23,7 @@ import { EditableView } from "../EditableView"; import { MainView } from '../MainView'; import { KeyValueBox } from '../nodes/KeyValueBox'; import { Templates } from '../Templates'; -import { CollectionViewType } from './CollectionBaseView'; +import { CollectionViewType } from './CollectionView'; import { CollectionSchemaPreview } from './CollectionSchemaView'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss new file mode 100644 index 000000000..e4187e4d6 --- /dev/null +++ b/src/client/views/collections/CollectionView.scss @@ -0,0 +1,26 @@ +@import "../globalCssVariables"; + +.collectionView { + border-width: 0; + border-color: $light-color-secondary; + border-style: solid; + border-radius: 0 0 $border-radius $border-radius; + box-sizing: border-box; + border-radius: inherit; + width: 100%; + height: 100%; + overflow: auto; +} + +#google-tags { + transition: all 0.5s ease 0s; + width: 30px; + height: 30px; + position: absolute; + bottom: 15px; + left: 15px; + border: 2px solid black; + border-radius: 50%; + padding: 3px; + background: white; +} \ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index f8eb28ade..8d5694bf0 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -5,12 +5,9 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mo import { observer } from "mobx-react"; import * as React from 'react'; import { Id } from '../../../new_fields/FieldSymbols'; -import { StrCast, Cast } from '../../../new_fields/Types'; +import { StrCast, BoolCast, Cast } from '../../../new_fields/Types'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { ContextMenu } from "../ContextMenu"; -import { ContextMenuProps } from '../ContextMenuItem'; -import { FieldView, FieldViewProps } from '../nodes/FieldView'; -import { CollectionBaseView, CollectionRenderProps, CollectionViewType } from './CollectionBaseView'; import { CollectionDockingView } from "./CollectionDockingView"; import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; @@ -26,18 +23,75 @@ import { DocListCast } from '../../../new_fields/Doc'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app export const COLLECTION_BORDER_WIDTH = 2; - +import { DateField } from '../../../new_fields/DateField'; +import { Doc, } from '../../../new_fields/Doc'; +import { listSpec } from '../../../new_fields/Schema'; +import { DocumentManager } from '../../util/DocumentManager'; +import { SelectionManager } from '../../util/SelectionManager'; +import './CollectionView.scss'; +import { FieldViewProps, FieldView } from '../nodes/FieldView'; library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy); +export enum CollectionViewType { + Invalid, + Freeform, + Schema, + Docking, + Tree, + Stacking, + Masonry, + Pivot, + Linear, +} + +export namespace CollectionViewType { + const stringMapping = new Map([ + ["invalid", CollectionViewType.Invalid], + ["freeform", CollectionViewType.Freeform], + ["schema", CollectionViewType.Schema], + ["docking", CollectionViewType.Docking], + ["tree", CollectionViewType.Tree], + ["stacking", CollectionViewType.Stacking], + ["masonry", CollectionViewType.Masonry], + ["pivot", CollectionViewType.Pivot], + ["linear", CollectionViewType.Linear] + ]); + + export const valueOf = (value: string) => stringMapping.get(value.toLowerCase()); +} + +export interface CollectionRenderProps { + addDocument: (document: Doc) => boolean; + removeDocument: (document: Doc) => boolean; + moveDocument: (document: Doc, targetCollection: Doc, addDocument: (document: Doc) => boolean) => boolean; + active: () => boolean; + whenActiveChanged: (isActive: boolean) => void; +} + @observer export class CollectionView extends React.Component { - public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } private _reactionDisposer: IReactionDisposer | undefined; + private _isChildActive = false; //TODO should this be observable? @observable private _isLightboxOpen = false; @observable private _curLightboxImg = 0; @observable private _collapsed = true; + @observable private static _safeMode = false; + public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; } + + get collectionViewType(): CollectionViewType | undefined { + let viewField = Cast(this.props.Document.viewType, "number"); + if (CollectionView._safeMode) { + if (viewField === CollectionViewType.Freeform) { + return CollectionViewType.Tree; + } + if (viewField === CollectionViewType.Invalid) { + return CollectionViewType.Freeform; + } + } + return viewField === undefined ? CollectionViewType.Invalid : viewField; + } componentDidMount = () => { this._reactionDisposer = reaction(() => StrCast(this.props.Document.chromeStatus), @@ -51,32 +105,73 @@ export class CollectionView extends React.Component { }); } - componentWillUnmount = () => { - this._reactionDisposer && this._reactionDisposer(); + componentWillUnmount = () => this._reactionDisposer && this._reactionDisposer(); + + // bcz: Argh? What's the height of the collection chromes?? + chromeHeight = () => (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0); + + active = () => this.props.isSelected() || BoolCast(this.props.Document.forceActive) || this._isChildActive || this.props.renderDepth === 0; + + whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }; + + @action.bound + addDocument(doc: Doc): boolean { + let targetDataDoc = Doc.GetProto(this.props.Document); + Doc.AddDocToList(targetDataDoc, this.props.fieldKey, doc); + let extension = Doc.fieldExtensionDoc(targetDataDoc, this.props.fieldKey); // set metadata about the field being rendered (ie, the set of documents) on an extension field for that field + extension && (extension.lastModified = new DateField(new Date(Date.now()))); + Doc.GetProto(doc).lastOpened = new DateField; + return true; + } + + @action.bound + removeDocument(doc: Doc): boolean { + let docView = DocumentManager.Instance.getDocumentView(doc, this.props.ContainingCollectionView); + docView && SelectionManager.DeselectDoc(docView); + let value = Cast(this.props.Document[this.props.fieldKey], listSpec(Doc), []); + let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); + index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); + + ContextMenu.Instance.clearItems(); + if (index !== -1) { + value.splice(index, 1); + return true; + } + return false; + } + + // this is called with the document that was dragged and the collection to move it into. + // if the target collection is the same as this collection, then the move will be allowed. + // otherwise, the document being moved must be able to be removed from its container before + // moving it into the target. + @action.bound + moveDocument(doc: Doc, targetCollection: Doc, addDocument: (doc: Doc) => boolean): boolean { + if (Doc.AreProtosEqual(this.props.Document, targetCollection)) { + return true; + } + return this.removeDocument(doc) ? addDocument(doc) : false; } - // bcz: Argh? What's the height of the collection chomes?? - chromeHeight = () => { - return (this.props.ChromeHeight ? this.props.ChromeHeight() : 0) + (this.props.Document.chromeStatus === "enabled" ? -60 : 0); + showIsTagged = () => { + const children = DocListCast(this.props.Document[this.props.fieldKey]); + const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto); + const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags); + return !allTagged ? (null) : ; } private SubViewHelper = (type: CollectionViewType, renderProps: CollectionRenderProps) => { - let props = { ...this.props, ...renderProps }; + let props = { ...this.props, ...renderProps, chromeCollapsed: this._collapsed, ChromeHeight: this.chromeHeight, CollectionView: this, annotationsKey: "" }; switch (type) { - case CollectionViewType.Schema: return (); - // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip - case CollectionViewType.Docking: return (); - case CollectionViewType.Tree: return (); - case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } - case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } - case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (); } - case CollectionViewType.Linear: { return (); } + case CollectionViewType.Schema: return (); + case CollectionViewType.Docking: return (); + case CollectionViewType.Tree: return (); + case CollectionViewType.Linear: { return (); } + case CollectionViewType.Stacking: { this.props.Document.singleColumn = true; return (); } + case CollectionViewType.Masonry: { this.props.Document.singleColumn = false; return (); } + case CollectionViewType.Pivot: { this.props.Document.freeformLayoutEngine = "pivot"; return (); } case CollectionViewType.Freeform: - default: - this.props.Document.freeformLayoutEngine = undefined; - return (); + default: { this.props.Document.freeformLayoutEngine = undefined; return (); } } - return (null); } @action @@ -87,22 +182,18 @@ export class CollectionView extends React.Component { private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip - if (this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking) { - return [(null), this.SubViewHelper(type, renderProps)]; - } - return [ - , - this.SubViewHelper(type, renderProps) - ]; + let chrome = this.props.Document.chromeStatus === "disabled" || type === CollectionViewType.Docking ? (null) : + ; + return [chrome, this.SubViewHelper(type, renderProps)]; } onContextMenu = (e: React.MouseEvent): void => { if (!e.isPropagationStopped() && this.props.Document[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7 let existingVm = ContextMenu.Instance.findByDescription("View Modes..."); - let subItems: ContextMenuProps[] = existingVm && "subitems" in existingVm ? existingVm.subitems : []; + let subItems = existingVm && "subitems" in existingVm ? existingVm.subitems : []; subItems.push({ description: "Freeform", event: () => { this.props.Document.viewType = CollectionViewType.Freeform; }, icon: "signature" }); - if (CollectionBaseView.InSafeMode()) { + if (CollectionView._safeMode) { ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => this.props.Document.viewType = CollectionViewType.Invalid, icon: "project-diagram" }); } subItems.push({ description: "Schema", event: () => this.props.Document.viewType = CollectionViewType.Schema, icon: "th-list" }); @@ -126,7 +217,7 @@ export class CollectionView extends React.Component { !existingVm && ContextMenu.Instance.addItem({ description: "View Modes...", subitems: subItems, icon: "eye" }); let existing = ContextMenu.Instance.findByDescription("Layout..."); - let layoutItems: ContextMenuProps[] = existing && "subitems" in existing ? existing.subitems : []; + let layoutItems = existing && "subitems" in existing ? existing.subitems : []; layoutItems.push({ description: `${this.props.Document.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.props.Document.forceActive = !this.props.Document.forceActive, icon: "project-diagram" }); !existing && ContextMenu.Instance.addItem({ description: "Layout...", subitems: layoutItems, icon: "hand-point-right" }); ContextMenu.Instance.addItem({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.props.Document) }); @@ -142,15 +233,23 @@ export class CollectionView extends React.Component { onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)} onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />); } - render() { - return (<> - - {this.SubView} - - + const props: CollectionRenderProps = { + addDocument: this.addDocument, + removeDocument: this.removeDocument, + moveDocument: this.moveDocument, + active: this.active, + whenActiveChanged: this.whenActiveChanged, + }; + return (
+ {this.showIsTagged()} + {this.collectionViewType !== undefined ? this.SubView(this.collectionViewType, props) : (null)} {this.lightbox(DocListCast(this.props.Document[this.props.fieldKey]).filter(d => d.type === DocumentType.IMG).map(d => Cast(d.data, ImageField) ? Cast(d.data, ImageField)!.url.href : ""))} - - ); +
); } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index dd5e630e4..cfc6c2a3f 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -14,7 +14,7 @@ import { undoBatch } from "../../util/UndoManager"; import { EditableView } from "../EditableView"; import { COLLECTION_BORDER_WIDTH } from "../globalCssVariables.scss"; import { DocLike } from "../MetadataEntryMenu"; -import { CollectionViewType } from "./CollectionBaseView"; +import { CollectionViewType } from "./CollectionView"; import { CollectionView } from "./CollectionView"; import "./CollectionViewChromes.scss"; import * as Autosuggest from 'react-autosuggest'; diff --git a/src/client/views/collections/ParentDocumentSelector.tsx b/src/client/views/collections/ParentDocumentSelector.tsx index 7f2913214..8b6fa330c 100644 --- a/src/client/views/collections/ParentDocumentSelector.tsx +++ b/src/client/views/collections/ParentDocumentSelector.tsx @@ -7,7 +7,7 @@ import { Id } from "../../../new_fields/FieldSymbols"; import { SearchUtil } from "../../util/SearchUtil"; import { CollectionDockingView } from "./CollectionDockingView"; import { NumCast } from "../../../new_fields/Types"; -import { CollectionViewType } from "./CollectionBaseView"; +import { CollectionViewType } from "./CollectionView"; import { DocumentButtonBar } from "../DocumentButtonBar"; import { DocumentManager } from "../../util/DocumentManager"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0419bc3fa..d7350fe1a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -35,7 +35,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { documentSchema, positionSchema } from "../../DocComponent"; +import { documentSchema, positionSchema } from "../../../../new_fields/documentSchemas"; library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload, faBraille, faChalkboard, faFileUpload); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index e0bf4a2f0..4f86eeb2b 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -15,7 +15,7 @@ import { Transform } from "../../../util/Transform"; import { undoBatch } from "../../../util/UndoManager"; import { InkingCanvas } from "../../InkingCanvas"; import { PreviewCursor } from "../../PreviewCursor"; -import { CollectionViewType } from "../CollectionBaseView"; +import { CollectionViewType } from "../CollectionView"; import { CollectionFreeFormView } from "./CollectionFreeFormView"; import "./MarqueeView.scss"; import React = require("react"); diff --git a/src/client/views/linking/LinkFollowBox.tsx b/src/client/views/linking/LinkFollowBox.tsx index ef194624a..efe2c7f2a 100644 --- a/src/client/views/linking/LinkFollowBox.tsx +++ b/src/client/views/linking/LinkFollowBox.tsx @@ -5,7 +5,7 @@ import { FieldViewProps, FieldView } from "../nodes/FieldView"; import { Doc, DocListCastAsync, Opt } from "../../../new_fields/Doc"; import { undoBatch } from "../../util/UndoManager"; import { NumCast, FieldValue, Cast, StrCast } from "../../../new_fields/Types"; -import { CollectionViewType } from "../collections/CollectionBaseView"; +import { CollectionViewType } from "../collections/CollectionView"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { SelectionManager } from "../../util/SelectionManager"; import { DocumentManager } from "../../util/DocumentManager"; diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index 1531d825b..beb2b30fd 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -66,7 +66,8 @@ export class ButtonBox extends DocComponent(Butt @action drop = (e: Event, de: DragManager.DropEvent) => { if (de.data instanceof DragManager.DocumentDragData && e.target) { - this.props.Document[(e.target as any).textContent] = new List(de.data.droppedDocuments); + this.props.Document[(e.target as any).textContent] = new List(de.data.droppedDocuments.map((d, i) => + d.onDragStart ? de.data.draggedDocuments[i] : d)); e.stopPropagation(); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f741dae9a..05080b824 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -26,7 +26,7 @@ import { SelectionManager } from "../../util/SelectionManager"; import SharingManager from '../../util/SharingManager'; import { Transform } from "../../util/Transform"; import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { CollectionViewType } from '../collections/CollectionBaseView'; +import { CollectionViewType } from '../collections/CollectionView'; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 7ec5d0471..cbb83b511 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -10,7 +10,7 @@ import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; -import { CollectionViewType } from "../collections/CollectionBaseView"; +import { CollectionViewType } from "../collections/CollectionView"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionView } from "../collections/CollectionView"; import { ContextMenu } from "../ContextMenu"; diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx index 5f758f496..7a1b4f9fb 100644 --- a/src/client/views/presentationview/PresElementBox.tsx +++ b/src/client/views/presentationview/PresElementBox.tsx @@ -12,7 +12,7 @@ import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { emptyFunction, returnFalse } from "../../../Utils"; import { DocumentType } from "../../documents/DocumentTypes"; import { Transform } from "../../util/Transform"; -import { CollectionViewType } from '../collections/CollectionBaseView'; +import { CollectionViewType } from '../collections/CollectionView'; import { CollectionSchemaPreview } from '../collections/CollectionSchemaView'; import { DocComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; @@ -51,7 +51,7 @@ export class PresElementBox extends DocComponent(P @computed get indexInPres() { return DocListCast(this.presentationDoc[this.Document.presBoxKey || ""]).indexOf(this.props.Document); } @computed get presentationDoc() { return Cast(this.Document.presBox, Doc) as Doc; } - @computed get targetDoc() { return this.Document.presentationTargetDoc as Doc } + @computed get targetDoc() { return this.Document.presentationTargetDoc as Doc; } @computed get currentIndex() { return NumCast(this.presentationDoc.selectedDoc); } /** diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 60307065a..f1d825aa0 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -15,7 +15,7 @@ import { LinkManager } from "../../util/LinkManager"; import { SearchUtil } from "../../util/SearchUtil"; import { Transform } from "../../util/Transform"; import { SEARCH_THUMBNAIL_SIZE } from "../../views/globalCssVariables.scss"; -import { CollectionViewType } from "../collections/CollectionBaseView"; +import { CollectionViewType } from "../collections/CollectionView"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { ContextMenu } from "../ContextMenu"; import { DocumentView } from "../nodes/DocumentView"; -- cgit v1.2.3-70-g09d2