diff options
| author | Bob Zeleznik <zzzman@gmail.com> | 2019-09-30 17:12:28 -0400 |
|---|---|---|
| committer | Bob Zeleznik <zzzman@gmail.com> | 2019-09-30 17:12:28 -0400 |
| commit | 65d9b92bcadbecb6e7dd55930f96f228c6a2f4f7 (patch) | |
| tree | 75f1e94c0238fd21407206a1fdbe9cb94bd17cce /src/client/views/presentationview/PresElementBox.tsx | |
| parent | 2b0840202933ac554a9dc79304203ada38551c0f (diff) | |
simpliified presentations to be a PresBox with PresElementBoxes.
Diffstat (limited to 'src/client/views/presentationview/PresElementBox.tsx')
| -rw-r--r-- | src/client/views/presentationview/PresElementBox.tsx | 329 |
1 files changed, 329 insertions, 0 deletions
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<void>; +} + +/** + * 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<PresElementProps & FieldViewProps> { + + public static LayoutString() { return FieldView.LayoutString(PresElementBox); } + private header?: HTMLDivElement | undefined; + private listdropDisposer?: DragManager.DragDropDisposer; + private presElRef: React.RefObject<HTMLDivElement> = 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 ( + <div className="presElementBox-embedded" style={{ + height: propDocHeight === 0 ? 100 : propDocHeight * scale(), + width: propDocWidth === 0 ? "auto" : propDocWidth * scale(), + }}> + <DocumentView + fitToBox={StrCast(this.props.Document.type).indexOf(DocumentType.COL) !== -1} + Document={this.props.Document.target as Doc} + addDocument={returnFalse} + removeDocument={returnFalse} + ruleProvider={undefined} + ScreenToLocalTransform={Transform.Identity} + addDocTab={returnFalse} + pinToPres={returnFalse} + 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} + /> + <div className="presElementBox-embeddedMask" /> + </div> + ); + } + + 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 ( + <div className={className} key={p.Document[Id] + p.index} + ref={this.presElRef} + onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} + onPointerDown={onItemDown} + style={{ outlineWidth: Doc.IsBrushed(p.Document) ? `1px` : "0px", }} + onClick={e => p.gotoDocument(p.index, this.currentIndex)}> + <strong className="presElementBox-name"> + {`${p.index + 1}. ${p.Document.title}`} + </strong> + <button className="presElementBox-icon" onPointerDown={e => e.stopPropagation()} onClick={e => this.props.removeDocument && this.props.removeDocument(p.Document)}>X</button> + <br /> + <button title="Zoom" className={"presElementBox-interaction" + (this.showButton ? "-selected" : "")} onPointerDown={(e) => e.stopPropagation()} onClick={this.onZoomDocumentClick}><FontAwesomeIcon icon={"search"} /></button> + <button title="Navigate" className={"presElementBox-interaction" + (this.navButton ? "-selected" : "")} onPointerDown={(e) => e.stopPropagation()} onClick={this.onNavigateDocumentClick}><FontAwesomeIcon icon={"location-arrow"} /></button> + <button title="Hide Til Presented" className={"presElementBox-interaction" + (this.hideTillShownButton ? "-selected" : "")} onPointerDown={(e) => e.stopPropagation()} onClick={this.onHideDocumentUntilPressClick}><FontAwesomeIcon icon={fileSolid} /></button> + <button title="Fade After Presented" className={"presElementBox-interaction" + (this.fadeButton ? "-selected" : "")} onPointerDown={(e) => e.stopPropagation()} onClick={this.onFadeDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} /></button> + <button title="Hide After Presented" className={"presElementBox-interaction" + (this.hideAfterButton ? "-selected" : "")} onPointerDown={(e) => e.stopPropagation()} onClick={this.onHideDocumentAfterPresentedClick}><FontAwesomeIcon icon={faFileDownload} /></button> + <button title="Group With Up" className={"presElementBox-interaction" + (this.groupButton ? "-selected" : "")} onPointerDown={(e) => e.stopPropagation()} onClick={action(() => this.groupButton = !this.groupButton)}> <FontAwesomeIcon icon={"arrow-up"} /> </button> + <button title="Expand Inline" className={"presElementBox-interaction" + (this.embedInline ? "-selected" : "")} onPointerDown={(e) => e.stopPropagation()} onClick={action(() => this.embedInline = !this.embedInline)}><FontAwesomeIcon icon={"arrow-down"} /></button> + + <br /> + {this.renderEmbeddedInline()} + </div> + ); + } +}
\ No newline at end of file |
