diff options
Diffstat (limited to 'src/client/views/nodes/PresBox.tsx')
-rw-r--r-- | src/client/views/nodes/PresBox.tsx | 307 |
1 files changed, 137 insertions, 170 deletions
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 428e9aa7b..a39c337ca 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -1,29 +1,26 @@ 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 { faArrowLeft, faArrowRight, faEdit, faMinus, faPlay, faPlus, faStop, faHandPointLeft, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, reaction, IReactionDisposer } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, DocListCastAsync } from "../../../new_fields/Doc"; -import { listSpec } from "../../../new_fields/Schema"; -import { Cast, FieldValue, NumCast } from "../../../new_fields/Types"; -import { CurrentUserUtils } from "../../../server/authentication/models/current_user_utils"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { InkTool } from "../../../new_fields/InkField"; +import { BoolCast, Cast, FieldValue, NumCast } from "../../../new_fields/Types"; +import { returnFalse } from "../../../Utils"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; -import { CollectionViewType } from "../collections/CollectionView"; import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { CollectionView } from "../collections/CollectionView"; -import { ContextMenu } from "../ContextMenu"; +import { CollectionView, CollectionViewType } from "../collections/CollectionView"; +import { InkingControl } from "../InkingControl"; import { FieldView, FieldViewProps } from './FieldView'; 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); library.add(faPlay); library.add(faStop); +library.add(faHandPointLeft); library.add(faPlus); library.add(faTimes); library.add(faMinus); @@ -32,95 +29,71 @@ library.add(faEdit); @observer export class PresBox extends React.Component<FieldViewProps> { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } - _docListChangedReaction: IReactionDisposer | undefined; + _childReaction: IReactionDisposer | undefined; + @observable _isChildActive = false; 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) { - const pinDoc = Docs.Create.PresElementBoxDocument({ backgroundColor: "transparent" }); - Doc.GetProto(pinDoc).presentationTargetDoc = item; - Doc.GetProto(pinDoc).title = ComputedField.MakeFunction('this.presentationTargetDoc?.title?.toString()'); - value.splice(i, 1, pinDoc); - } - }); - } - }); + this.props.Document._forceRenderEngine = "timeline"; + this.props.Document._replacedChrome = "replaced"; + this._childReaction = reaction(() => this.childDocs.slice(), (children) => children.forEach((child, i) => child.presentationIndex = i), { fireImmediately: true }); } - componentWillUnmount() { - this._docListChangedReaction && this._docListChangedReaction(); + this._childReaction?.(); } @computed get childDocs() { return DocListCast(this.props.Document[this.props.fieldKey]); } + @computed get currentIndex() { return NumCast(this.props.Document._itemIndex); } + + updateCurrentPresentation = action(() => Doc.UserDoc().curPresentation = this.props.Document); - next = async () => { - const current = NumCast(this.props.Document.selectedDoc); - //asking to get document at current index - const docAtCurrentNext = await this.getDocAtIndex(current + 1); - if (docAtCurrentNext !== undefined) { - const presDocs = DocListCast(this.props.Document[this.props.fieldKey]); - let nextSelected = current + 1; + next = () => { + this.updateCurrentPresentation(); + if (this.childDocs[this.currentIndex + 1] !== undefined) { + let nextSelected = this.currentIndex + 1; - for (; nextSelected < presDocs.length - 1; nextSelected++) { - if (!presDocs[nextSelected + 1].groupButton) { + for (; nextSelected < this.childDocs.length - 1; nextSelected++) { + if (!this.childDocs[nextSelected + 1].groupButton) { break; } } - this.gotoDocument(nextSelected, current); + this.gotoDocument(nextSelected, this.currentIndex); } } - back = async () => { - const current = NumCast(this.props.Document.selectedDoc); - //requesting for the doc at current index - const docAtCurrent = await this.getDocAtIndex(current); - if (docAtCurrent !== undefined) { - - //asking for its presentation id. - let prevSelected = current; - let zoomOut: boolean = false; - - const presDocs = await DocListCastAsync(this.props.Document[this.props.fieldKey]); - const currentsArray: Doc[] = []; - for (; presDocs && prevSelected > 0 && presDocs[prevSelected].groupButton; prevSelected--) { - currentsArray.push(presDocs[prevSelected]); + back = () => { + this.updateCurrentPresentation(); + const docAtCurrent = this.childDocs[this.currentIndex]; + if (docAtCurrent) { + //check if any of the group members had used zooming in including the current document + //If so making sure to zoom out, which goes back to state before zooming action + let prevSelected = this.currentIndex; + let didZoom = docAtCurrent.zoomButton; + for (; !didZoom && prevSelected > 0 && this.childDocs[prevSelected].groupButton; prevSelected--) { + didZoom = this.childDocs[prevSelected].zoomButton; } prevSelected = Math.max(0, prevSelected - 1); - //checking if any of the group members had used zooming in - currentsArray.forEach((doc: Doc) => { - if (doc.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 || docAtCurrent.showButton) { - const prevScale = NumCast(this.childDocs[prevSelected].viewScale, null); - const curScale = DocumentManager.Instance.getScaleOfDocView(this.childDocs[current]); - if (prevScale !== undefined && prevScale !== curScale) { - DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); - } + if (this.currentIndex > 0 && didZoom) { + const prevScale = NumCast(this.childDocs[prevSelected].viewScale); + const curScale = DocumentManager.Instance.getScaleOfDocView(docAtCurrent); + if (prevScale && prevScale !== curScale) { + DocumentManager.Instance.zoomIntoScale(docAtCurrent, prevScale); } } - this.gotoDocument(prevSelected, current); + this.gotoDocument(prevSelected, this.currentIndex); } } + whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); + active = (outsideReaction?: boolean) => ((InkingControl.Instance.selectedTool === InkTool.None && !this.props.Document.isBackground) && + (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) + /** * This is the method that checks for the actions that need to be performed * after the document has been presented, which involves 3 button options: * Hide Until Presented, Hide After Presented, Fade After Presented */ showAfterPresented = (index: number) => { + this.updateCurrentPresentation(); this.childDocs.forEach((doc, ind) => { //the order of cases is aligned based on priority if (doc.hideTillShownButton && ind <= index) { @@ -141,6 +114,7 @@ export class PresBox extends React.Component<FieldViewProps> { * Hide Until Presented, Hide After Presented, Fade After Presented */ hideIfNotPresented = (index: number) => { + this.updateCurrentPresentation(); this.childDocs.forEach((key, ind) => { //the order of cases is aligned based on priority @@ -162,6 +136,7 @@ export class PresBox extends React.Component<FieldViewProps> { * te option open, navigates to that element. */ navigateToElement = async (curDoc: Doc, fromDocIndex: number) => { + this.updateCurrentPresentation(); const fromDoc = this.childDocs[fromDocIndex].presentationTargetDoc as Doc; let docToJump = curDoc; let willZoom = false; @@ -181,22 +156,24 @@ export class PresBox extends React.Component<FieldViewProps> { docToJump = doc; willZoom = false; } - if (doc.showButton) { + if (doc.zoomButton) { docToJump = doc; willZoom = true; } }); //docToJump stayed same meaning, it was not in the group or was the last element in the group + const aliasOf = await Cast(docToJump.aliasOf, Doc); + const srcContext = aliasOf && await Cast(aliasOf.context, Doc); if (docToJump === curDoc) { //checking if curDoc has navigation open - const target = await curDoc.presentationTargetDoc as Doc; - if (curDoc.navButton) { - DocumentManager.Instance.jumpToDocument(target, false); - } else if (curDoc.showButton) { + const target = await Cast(curDoc.presentationTargetDoc, Doc); + if (curDoc.navButton && target) { + DocumentManager.Instance.jumpToDocument(target, false, undefined, srcContext); + } else if (curDoc.zoomButton && target) { const curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(target, true); + await DocumentManager.Instance.jumpToDocument(target, true, undefined, srcContext); curDoc.viewScale = DocumentManager.Instance.getScaleOfDocView(target); //saving the scale user was on before zooming in @@ -210,7 +187,8 @@ export class PresBox extends React.Component<FieldViewProps> { const curScale = DocumentManager.Instance.getScaleOfDocView(fromDoc); //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(await docToJump.presentationTargetDoc as Doc, willZoom); + const presTargetDoc = await docToJump.presentationTargetDoc as Doc; + await DocumentManager.Instance.jumpToDocument(presTargetDoc, willZoom, undefined, srcContext); const newScale = DocumentManager.Instance.getScaleOfDocView(await curDoc.presentationTargetDoc as Doc); curDoc.viewScale = newScale; //saving the scale that user was on @@ -220,86 +198,64 @@ 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.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 list[index]; - } - return undefined; - } - @undoBatch public removeDocument = (doc: Doc) => { - const value = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc))); - if (value) { - const indexOfDoc = value.indexOf(doc); - if (indexOfDoc !== - 1) { - value.splice(indexOfDoc, 1)[0]; - return true; - } - } - return false; + return Doc.RemoveDocFromList(this.props.Document, this.props.fieldKey, doc); } //The function that is called when a document is clicked or reached through next or back. //it'll also execute the necessary actions if presentation is playing. - @action - public gotoDocument = async (index: number, fromDoc: number) => { + public gotoDocument = (index: number, fromDoc: number) => { + this.updateCurrentPresentation(); Doc.UnBrushAllDocs(); - const list = FieldValue(Cast(this.props.Document[this.props.fieldKey], listSpec(Doc))); - if (list && index >= 0 && index < list.length) { - this.props.Document.selectedDoc = index; + if (index >= 0 && index < this.childDocs.length) { + this.props.Document._itemIndex = index; if (!this.props.Document.presStatus) { this.props.Document.presStatus = true; this.startPresentation(index); } - const doc = await list[index]; - if (this.props.Document.presStatus) { - this.navigateToElement(doc, fromDoc); - this.hideIfNotPresented(index); - this.showAfterPresented(index); - } + this.navigateToElement(this.childDocs[index], fromDoc); + this.hideIfNotPresented(index); + this.showAfterPresented(index); } } //The function that starts or resets presentaton functionally, depending on status flag. - @action startOrResetPres = () => { + this.updateCurrentPresentation(); if (this.props.Document.presStatus) { this.resetPresentation(); } else { this.props.Document.presStatus = true; this.startPresentation(0); - this.gotoDocument(0, NumCast(this.props.Document.selectedDoc)); + this.gotoDocument(0, this.currentIndex); } } + addDocument = (doc: Doc) => { + const newPinDoc = Doc.MakeAlias(doc); + newPinDoc.presentationTargetDoc = doc; + return Doc.AddDocToList(this.props.Document, this.props.fieldKey, newPinDoc); + } + + //The function that resets the presentation by removing every action done by it. It also //stops the presentaton. - @action resetPresentation = () => { - this.childDocs.forEach((doc: Doc) => { - doc.opacity = 1; - doc.viewScale = 1; - }); - this.props.Document.selectedDoc = 0; + this.updateCurrentPresentation(); + this.childDocs.forEach(doc => doc.opacity = doc.viewScale = 1); + this.props.Document._itemIndex = 0; this.props.Document.presStatus = false; - if (this.childDocs.length !== 0) { - DocumentManager.Instance.zoomIntoScale(this.childDocs[0], 1); - } + this.childDocs.length && 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.updateCurrentPresentation(); this.childDocs.map(doc => { if (doc.hideTillShownButton && this.childDocs.indexOf(doc) > startIndex) { doc.opacity = 0; @@ -313,70 +269,81 @@ export class PresBox extends React.Component<FieldViewProps> { }); } - toggleMinimize = undoBatch(action((e: React.PointerEvent) => { - 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.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), this.props.fieldKey, this.props.Document); + updateMinimize = undoBatch(action((e: React.ChangeEvent, mode: number) => { + if (BoolCast(this.props.Document.inOverlay) !== (mode === CollectionViewType.Invalid)) { + if (this.props.Document.inOverlay) { + Doc.RemoveDocFromList((Doc.UserDoc().overlays as Doc), undefined, this.props.Document); + CollectionDockingView.AddRightSplit(this.props.Document); + this.props.Document.inOverlay = false; + } else { + this.props.Document.x = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[0];// 500;//e.clientX + 25; + this.props.Document.y = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0)[1];////e.clientY - 25; + this.props.addDocTab?.(this.props.Document, "close"); + Doc.AddDocToList((Doc.UserDoc().overlays as Doc), undefined, this.props.Document); + } } })); - specificContextMenu = (e: React.MouseEvent): void => { - 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[], viewtype: number) => { - this.props.Document._chromeStatus = "disabled"; - const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 72; - docList.forEach((doc: Doc) => { - doc.presBox = this.props.Document; - doc.presBoxKey = this.props.fieldKey; - doc.collapsedHeight = hgt; - doc._height = ComputedField.MakeFunction("this.collapsedHeight + Number(this.embedOpen ? 100:0)"); - const curScale = NumCast(doc.viewScale, null); - if (curScale === undefined) { - doc.viewScale = 1; - } + const hgt = (viewtype === CollectionViewType.Tree) ? 50 : 46; + docList.forEach(doc => { + doc.presBox = this.props.Document; // give contained documents a reference to the presentation + doc.collapsedHeight = hgt; // set the collpased height for documents based on the type of view (Tree or Stack) they will be displaye din + !NumCast(doc.viewScale) && (doc.viewScale = 1); }); } - selectElement = (doc: Doc) => { - const index = DocListCast(this.props.Document[this.props.fieldKey]).indexOf(doc); - index !== -1 && this.gotoDocument(index, NumCast(this.props.Document.selectedDoc)); + this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.props.Document._itemIndex)); } getTransform = () => { - return this.props.ScreenToLocalTransform().translate(-10, -50);// listBox padding-left and pres-box-cont minHeight + return this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight } + panelHeight = () => { + return this.props.PanelHeight() - 20; + } + + @undoBatch + viewChanged = action((e: React.ChangeEvent) => { + //@ts-ignore + this.props.Document._viewType = Number(e.target.selectedOptions[0].value); + this.props.Document._viewType === CollectionViewType.Stacking && (this.props.Document._pivotField = undefined); // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here + this.updateMinimize(e, Number(this.props.Document._viewType)); + }); + + childLayoutTemplate = () => this.props.Document._viewType === CollectionViewType.Stacking ? Cast(Doc.UserDoc().presentationTemplate, Doc, null) : undefined; render() { - this.initializeScaleViews(this.childDocs, NumCast(this.props.Document._viewType)); - return ( - <div className="presBox-cont" onContextMenu={this.specificContextMenu}> - <div className="presBox-buttons"> - <button className="presBox-button" title="Back" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button> - <button className="presBox-button" title={"Reset Presentation" + this.props.Document.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}> - <FontAwesomeIcon icon={this.props.Document.presStatus ? "stop" : "play"} /> - </button> - <button className="presBox-button" title="Next" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button> - <button className="presBox-button" title={this.props.Document.inOverlay ? "Expand" : "Minimize"} onClick={this.toggleMinimize}><FontAwesomeIcon icon={"eye"} /></button> - </div> - {this.props.Document.inOverlay ? (null) : - <div className="presBox-listCont" > - <CollectionView {...this.props} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} /> - </div> + const mode = NumCast(this.props.Document._viewType, CollectionViewType.Invalid); + this.initializeScaleViews(this.childDocs, mode); + return <div className="presBox-cont" style={{ minWidth: this.props.Document.inOverlay ? 240 : undefined, pointerEvents: this.active() || this.props.Document.inOverlay ? "all" : "none" }} > + <div className="presBox-buttons" style={{ display: this.props.Document._chromeStatus === "disabled" ? "none" : undefined }}> + <select className="collectionViewBaseChrome-viewPicker" + onPointerDown={e => e.stopPropagation()} + onChange={this.viewChanged} + value={mode}> + <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Invalid}>Min</option> + <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option> + <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Time}>Time</option> + <option className="collectionViewBaseChrome-viewOption" onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel}>Slides</option> + </select> + <button className="presBox-button" title="Back" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></button> + <button className="presBox-button" title={"Reset Presentation" + this.props.Document.presStatus ? "" : " From Start"} onClick={this.startOrResetPres}> + <FontAwesomeIcon icon={this.props.Document.presStatus ? "stop" : "play"} /> + </button> + <button className="presBox-button" title="Next" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></button> + </div> + <div className="presBox-listCont" > + {mode !== CollectionViewType.Invalid ? + <CollectionView {...this.props} PanelHeight={this.panelHeight} moveDocument={returnFalse} childLayoutTemplate={this.childLayoutTemplate} + addDocument={this.addDocument} removeDocument={returnFalse} focus={this.selectElement} ScreenToLocalTransform={this.getTransform} /> + : (null) } </div> - ); + </div>; } }
\ No newline at end of file |