diff options
Diffstat (limited to 'src/client/views/nodes/PresBox.tsx')
-rw-r--r-- | src/client/views/nodes/PresBox.tsx | 1680 |
1 files changed, 1526 insertions, 154 deletions
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index a304ced18..ccf1fb8e3 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -1,25 +1,39 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast, DocCastAsync } from "../../../fields/Doc"; +import { Doc, DocListCast, DocCastAsync, WidthSym } from "../../../fields/Doc"; import { InkTool } from "../../../fields/InkField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; -import { returnFalse, returnOne } from "../../../Utils"; +import { BoolCast, Cast, NumCast, StrCast, ScriptCast } from "../../../fields/Types"; +import { returnFalse, returnOne, numberRange, returnTrue } from "../../../Utils"; import { documentSchema } from "../../../fields/documentSchemas"; import { DocumentManager } from "../../util/DocumentManager"; import { undoBatch } from "../../util/UndoManager"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; +import { CollectionDockingView, DockedFrameRenderer } from "../collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from "../collections/CollectionView"; import { FieldView, FieldViewProps } from './FieldView'; +import { DocumentType } from "../../documents/DocumentTypes"; import "./PresBox.scss"; import { ViewBoxBaseComponent } from "../DocComponent"; -import { makeInterface } from "../../../fields/Schema"; -import { Docs } from "../../documents/Documents"; +import { makeInterface, listSpec } from "../../../fields/Schema"; +import { Docs, DocUtils } from "../../documents/Documents"; import { PrefetchProxy } from "../../../fields/Proxy"; import { ScriptField } from "../../../fields/ScriptField"; import { Scripting } from "../../util/Scripting"; import { InkingStroke } from "../InkingStroke"; +import { HighlightSpanKind } from "typescript"; +import { SearchUtil } from "../../util/SearchUtil"; +import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; +import { child } from "serializr"; +import { List } from "../../../fields/List"; +import { Tooltip } from "@material-ui/core"; +import { CollectionFreeFormViewChrome } from "../collections/CollectionMenu"; +import { conformsTo } from "lodash"; +import { translate } from "googleapis/build/src/apis/translate"; +import { DragManager, dropActionType } from "../../util/DragManager"; +import { actionAsync } from "mobx-utils"; +import { SelectionManager } from "../../util/SelectionManager"; +import { AudioBox } from "./AudioBox"; type PresBoxSchema = makeInterface<[typeof documentSchema]>; const PresBoxDocument = makeInterface(documentSchema); @@ -27,56 +41,99 @@ const PresBoxDocument = makeInterface(documentSchema); @observer export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } + private treedropDisposer?: DragManager.DragDropDisposer; + static Instance: PresBox; @observable _isChildActive = false; @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } @computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); } @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); } constructor(props: any) { super(props); + PresBox.Instance = this; if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations. Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ - title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _height: 46, isTemplateDoc: true, isTemplateForField: "data" + title: "pres element template", backgroundColor: "transparent", _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data" })); // this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent // the preselement docs from being part of multiple presentations since they would all have the same field, or we'd have to keep per-presentation data - // stored on each pres element. + // stored on each pres element. (this.presElement as Doc).lookupField = ScriptField.MakeFunction("lookupPresBoxField(container, field, data)", { field: "string", data: Doc.name, container: Doc.name }); } this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox } + + componentDidMount() { this.rootDoc.presBox = this.rootDoc; this.rootDoc._forceRenderEngine = "timeline"; this.rootDoc._replacedChrome = "replaced"; + this.layoutDoc.presStatus = "edit"; + this.layoutDoc._gridGap = 5; + // document.addEventListener("keydown", this.keyEvents, false); + } + + componentWillUnmount() { + this.treedropDisposer?.(); + } + + onPointerOver = () => { + document.addEventListener("keydown", this.keyEvents, true); } - updateCurrentPresentation = () => Doc.UserDoc().activePresentation = this.rootDoc; + + onPointerLeave = () => { + document.removeEventListener("keydown", this.keyEvents, true); + } + + updateCurrentPresentation = () => { + Doc.UserDoc().activePresentation = this.rootDoc; + } + + @observable _moveOnFromAudio: boolean = false; @undoBatch @action next = () => { this.updateCurrentPresentation(); + const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null); const presTargetDoc = Cast(this.childDocs[this.itemIndex].presentationTargetDoc, Doc, null); + const childDocs = DocListCast(presTargetDoc[Doc.LayoutFieldKey(presTargetDoc)]); + const currentFrame = Cast(presTargetDoc.currentFrame, "number", null); const lastFrame = Cast(presTargetDoc.lastFrame, "number", null); const curFrame = NumCast(presTargetDoc.currentFrame); - if (lastFrame !== undefined && curFrame < lastFrame) { + let internalFrames: boolean = false; + if (presTargetDoc.presProgressivize || presTargetDoc.zoomProgressivize || presTargetDoc.scrollProgressivize) internalFrames = true; + // Case 1: There are still other frames and should go through all frames before going to next slide + if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) { presTargetDoc._viewTransition = "all 1s"; setTimeout(() => presTargetDoc._viewTransition = undefined, 1010); presTargetDoc.currentFrame = curFrame + 1; - } - else if (this.childDocs[this.itemIndex + 1] !== undefined) { - let nextSelected = this.itemIndex + 1; - this.gotoDocument(nextSelected, this.itemIndex); - - for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) { - if (!this.childDocs[nextSelected].groupButton) { - break; - } else { - this.gotoDocument(nextSelected, this.itemIndex); - } + if (presTargetDoc.scrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(presTargetDoc, currentFrame); + if (presTargetDoc.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0); + if (presTargetDoc.zoomProgressivize) this.zoomProgressivizeNext(presTargetDoc); + // Case 2: Audio or video therefore wait to play the audio or video before moving on + } else if ((presTargetDoc.type === DocumentType.VID || presTargetDoc.type === DocumentType.AUDIO) && !this._moveOnFromAudio) { + if (presTargetDoc.type === DocumentType.AUDIO) { + AudioBox.Instance.playFrom(0); + this._moveOnFromAudio = true; + } + if (presTargetDoc.type === DocumentType.VID) { + this._moveOnFromAudio = true; } + // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide + } else if (activeNext !== undefined) { + if (!presTargetDoc.presProgressivize) { + const nextTagDoc = Cast(this.childDocs[this.itemIndex + 1].presentationTargetDoc, Doc, null); + const nextChildDocs = DocListCast(nextTagDoc[Doc.LayoutFieldKey(presTargetDoc)]); + nextChildDocs.forEach((doc, i) => { + doc.opacity = 1; + }); + } + const nextSelected = this.itemIndex + 1; + this.gotoDocument(nextSelected, this.itemIndex); + this._moveOnFromAudio = false; } } @@ -86,59 +143,72 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this.updateCurrentPresentation(); const docAtCurrent = this.childDocs[this.itemIndex]; 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.itemIndex; - let didZoom = docAtCurrent.zoomButton; - for (; !didZoom && prevSelected > 0 && this.childDocs[prevSelected].groupButton; prevSelected--) { - didZoom = this.childDocs[prevSelected].zoomButton; - } prevSelected = Math.max(0, prevSelected - 1); - this.gotoDocument(prevSelected, this.itemIndex); } } - /** - * 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) => { - const presTargetDoc = doc.presentationTargetDoc as Doc; - //the order of cases is aligned based on priority - if (doc.presHideTillShownButton && ind <= index) { - presTargetDoc.opacity = 1; - } - if (doc.presHideAfterButton && ind < index) { - presTargetDoc.opacity = 0; - } - if (doc.presFadeButton && ind < index) { - presTargetDoc.opacity = 0.5; + zoomProgressivizeNext = (presTargetDoc: Doc) => { + const srcContext = Cast(presTargetDoc.context, Doc, null); + const docView = DocumentManager.Instance.getDocumentView(presTargetDoc); + const vfLeft: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-left-indexed"]); + const vfWidth: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-width-indexed"]); + const vfTop: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-top-indexed"]); + const vfHeight: number = this.checkList(presTargetDoc, presTargetDoc["viewfinder-height-indexed"]); + // Case 1: document that is not a Golden Layout tab + if (srcContext) { + const srcDocView = DocumentManager.Instance.getDocumentView(srcContext); + if (srcDocView) { + const layoutdoc = Doc.Layout(presTargetDoc); + const panelWidth: number = srcDocView.props.PanelWidth(); + const panelHeight: number = srcDocView.props.PanelHeight(); + const newPanX = NumCast(presTargetDoc.x) + NumCast(layoutdoc._width) / 2; + const newPanY = NumCast(presTargetDoc.y) + NumCast(layoutdoc._height) / 2; + const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight); + srcContext._panX = newPanX + (vfLeft + (vfWidth / 2)); + srcContext._panY = newPanY + (vfTop + (vfHeight / 2)); + srcContext._viewScale = newScale; } - }); + } + // Case 2: document is the containing collection + if (docView && !srcContext) { + const panelWidth: number = docView.props.PanelWidth(); + const panelHeight: number = docView.props.PanelHeight(); + const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight); + presTargetDoc._panX = vfLeft + (vfWidth / 2); + presTargetDoc._panY = vfTop + (vfWidth / 2); + presTargetDoc._viewScale = newScale; + } + const resize = document.getElementById('resizable'); + if (resize) { + resize.style.width = vfWidth + 'px'; + resize.style.height = vfHeight + 'px'; + resize.style.top = vfTop + 'px'; + resize.style.left = vfLeft + 'px'; + } } - /** - * This is the method that checks for the actions that need to be performed - * before the document has been presented, which involves 3 button options: - * 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 - const presTargetDoc = key.presentationTargetDoc as Doc; - if (key.hideAfterButton && ind >= index) { - presTargetDoc.opacity = 1; - } - if (key.fadeButton && ind >= index) { - presTargetDoc.opacity = 1; + + @action + onHideDocument = () => { + this.childDocs.forEach((doc, index) => { + const curDoc = Cast(doc, Doc, null); + const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null); + if (tagDoc) tagDoc.opacity = 1; + if (curDoc.presHideTillShownButton) { + if (index > this.itemIndex) { + tagDoc.opacity = 0; + } else if (!curDoc.presHideAfterButton) { + tagDoc.opacity = 1; + } } - if (key.hideTillShownButton && ind > index) { - presTargetDoc.opacity = 0; + if (curDoc.presHideAfterButton) { + if (index < this.itemIndex) { + tagDoc.opacity = 0; + } else if (!curDoc.presHideTillShownButton) { + tagDoc.opacity = 1; + } } }); } @@ -146,50 +216,63 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> /** * This method makes sure that cursor navigates to the element that * has the option open and last in the group. If not in the group, and it has - * te option open, navigates to that element. + * the option open, navigates to that element. */ - navigateToElement = async (curDoc: Doc, fromDocIndex: number) => { - this.updateCurrentPresentation(); - let docToJump = curDoc; - let willZoom = false; - - const presDocs = DocListCast(this.dataDoc[this.props.fieldKey]); - let nextSelected = presDocs.indexOf(curDoc); - const currentDocGroups: Doc[] = []; - for (; nextSelected < presDocs.length - 1; nextSelected++) { - if (!presDocs[nextSelected + 1].groupButton) { - break; - } - currentDocGroups.push(presDocs[nextSelected]); - } + navigateToElement = async (curDoc: Doc) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + const srcContext = await DocCastAsync(targetDoc.context); + const presCollection = Cast(this.layoutDoc.presCollection, Doc, null); + const collectionDocView = presCollection ? await DocumentManager.Instance.getDocumentView(presCollection) : undefined; + this.turnOffEdit(); - currentDocGroups.forEach((doc: Doc, index: number) => { - if (doc.presNavButton) { - docToJump = doc; - willZoom = false; - } - if (doc.presZoomButton) { - docToJump = doc; - willZoom = true; + if (this.itemIndex >= 0) { + if (targetDoc) { + if (srcContext) this.layoutDoc.presCollection = srcContext; + } else if (targetDoc) this.layoutDoc.presCollection = targetDoc; + } + if (collectionDocView) { + if (srcContext && srcContext !== presCollection) { + // Case 1: new srcContext inside of current collection so add a new tab to the current pres collection + collectionDocView.props.addDocTab(srcContext, "inPlace"); } - }); + } + // else if (srcContext) { + // console.log("Case 2: srcContext - not open and collection containing this document exists, so open collection that contains it and then await zooming in on document"); + // this.props.addDocTab(srcContext, "onRight"); + // } else if (!srcContext) { + // console.log("Case 3: !srcContext - no collection containing this document, therefore open document itself on right"); + // this.props.addDocTab(targetDoc, "onRight"); + // } + this.updateCurrentPresentation(); + const docToJump = curDoc; + const willZoom = false; //docToJump stayed same meaning, it was not in the group or was the last element in the group - const aliasOf = await DocCastAsync(docToJump.aliasOf); - const srcContext = aliasOf && await DocCastAsync(aliasOf.context); - if (docToJump === curDoc) { + if (targetDoc.zoomProgressivize && this.rootDoc.presStatus !== 'edit') { + this.zoomProgressivizeNext(targetDoc); + } else if (docToJump === curDoc) { //checking if curDoc has navigation open - const target = (await DocCastAsync(curDoc.presentationTargetDoc)) || curDoc; - if (curDoc.presNavButton && target) { - DocumentManager.Instance.jumpToDocument(target, false, undefined, srcContext); - } else if (curDoc.presZoomButton && target) { + if (curDoc.presNavButton && targetDoc) { + await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext); + } else if (curDoc.presZoomButton && targetDoc) { //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(target, true, undefined, srcContext); + await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext); } } else { //awaiting jump so that new scale can be found, since jumping is async - const presTargetDoc = await DocCastAsync(docToJump.presentationTargetDoc); - presTargetDoc && await DocumentManager.Instance.jumpToDocument(presTargetDoc, willZoom, undefined, srcContext); + targetDoc && await DocumentManager.Instance.jumpToDocument(targetDoc, willZoom, undefined, srcContext); + } + if (activeItem.presPinView) { + targetDoc._panX = activeItem.presPinViewX; + targetDoc._panY = activeItem.presPinViewY; + targetDoc._viewScale = activeItem.presPinViewScale; + } + if (collectionDocView && activeItem.openDocument) { + collectionDocView.props.addDocTab(activeItem, "inPlace"); + } + if (targetDoc.presWebsiteData) { + targetDoc.data = targetDoc.presWebsiteData; } } @@ -201,30 +284,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; const presTargetDoc = Cast(this.childDocs[this.itemIndex].presentationTargetDoc, Doc, null); - if (presTargetDoc.lastFrame !== undefined) { + if (presTargetDoc?.lastFrame !== undefined) { presTargetDoc.currentFrame = 0; } - - if (!this.layoutDoc.presStatus) { - this.layoutDoc.presStatus = true; - this.startPresentation(index); - } - - this.navigateToElement(this.childDocs[index], fromDoc); - this.hideIfNotPresented(index); - this.showAfterPresented(index); + this.navigateToElement(this.childDocs[index]); + this._selectedArray = [this.childDocs[index]]; + // this.hideIfNotPresented(index); + // this.showAfterPresented(index); + // this.hideDocumentInPres(); + this.onHideDocument(); + // this.onHideAfterPresClick(); } }); + + @observable _presTimer!: NodeJS.Timeout; + //The function that starts or resets presentaton functionally, depending on status flag. - startOrResetPres = () => { + @undoBatch + @action + startOrResetPres = (startSlide: number) => { this.updateCurrentPresentation(); - if (this.layoutDoc.presStatus) { - this.resetPresentation(); + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + if (this.layoutDoc.presStatus === "auto") { + if (this._presTimer) clearInterval(this._presTimer); + this.layoutDoc.presStatus = "manual"; } else { - this.layoutDoc.presStatus = true; - this.startPresentation(0); - this.gotoDocument(0, this.itemIndex); + this.layoutDoc.presStatus = "auto"; + this.startPresentation(startSlide); + this.gotoDocument(startSlide, this.itemIndex); + this._presTimer = setInterval(() => { + if (this.itemIndex + 1 < this.childDocs.length) this.next(); + else { + clearInterval(this._presTimer); + this.layoutDoc.presStatus = "manual"; + } + }, targetDoc.presDuration ? NumCast(targetDoc.presDuration) + NumCast(targetDoc.presTransition) : 2000); } } @@ -234,7 +330,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this.updateCurrentPresentation(); this.childDocs.forEach(doc => (doc.presentationTargetDoc as Doc).opacity = 1); this.rootDoc._itemIndex = 0; - this.layoutDoc.presStatus = false; + // this.layoutDoc.presStatus = false; } //The function that starts the presentation, also checking if actions should be applied @@ -249,82 +345,1358 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> if (doc.presHideAfterButton && this.childDocs.indexOf(doc) < startIndex) { presTargetDoc.opacity = 0; } - if (doc.presFadeButton && this.childDocs.indexOf(doc) < startIndex) { - presTargetDoc.opacity = 0.5; - } }); } - updateMinimize = action((e: React.ChangeEvent, mode: CollectionViewType) => { - if (BoolCast(this.layoutDoc.inOverlay) !== (mode === CollectionViewType.Invalid)) { - if (this.layoutDoc.inOverlay) { - Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc); + updateMinimize = () => { + const srcContext = Cast(this.rootDoc.presCollection, Doc, null); + this.turnOffEdit(); + if (srcContext) { + if (srcContext.miniPres) { + document.removeEventListener("keydown", this.minimizeEvents, false); + srcContext.miniPres = false; CollectionDockingView.AddRightSplit(this.rootDoc); - this.layoutDoc.inOverlay = false; } else { - const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - this.rootDoc.x = pt[0];// 500;//e.clientX + 25; - this.rootDoc.y = pt[1];////e.clientY - 25; + document.addEventListener("keydown", this.minimizeEvents, false); + srcContext.miniPres = true; this.props.addDocTab?.(this.rootDoc, "close"); - Doc.AddDocToList((Doc.UserDoc().myOverlayDocuments as Doc), undefined, this.rootDoc); } } - }); + } @undoBatch viewChanged = action((e: React.ChangeEvent) => { //@ts-ignore const viewType = e.target.selectedOptions[0].value as CollectionViewType; viewType === CollectionViewType.Stacking && (this.rootDoc._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, this.rootDoc._viewType = viewType); + this.rootDoc._viewType = viewType; + if (viewType === CollectionViewType.Stacking) this.layoutDoc._gridGap = 5; + }); + + @undoBatch + movementChanged = action((movement: string) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + if (movement === 'zoom') { + activeItem.presZoomButton = !activeItem.presZoomButton; + if (activeItem.presZoomButton) activeItem.presMovement = 'Zoom'; + else activeItem.presMovement = 'None'; + activeItem.presNavButton = false; + } else if (movement === 'nav') { + activeItem.presZoomButton = false; + activeItem.presNavButton = !activeItem.presNavButton; + if (activeItem.presNavButton) activeItem.presMovement = 'Pan'; + else activeItem.presMovement = 'None'; + } else if (movement === 'switch') { + targetDoc.presTransition = 0; + activeItem.presSwitchButton = !activeItem.presSwitchButton; + if (activeItem.presSwitchButton) activeItem.presMovement = 'Jump cut'; + else activeItem.presMovement = 'None'; + } else { + activeItem.presMovement = 'None'; + activeItem.presZoomButton = false; + activeItem.presNavButton = false; + activeItem.presSwitchButton = false; + } }); whenActiveChanged = action((isActive: boolean) => this.props.whenActiveChanged(this._isChildActive = isActive)); + // For dragging documents into the presentation trail addDocumentFilter = (doc: Doc | Doc[]) => { const docs = doc instanceof Doc ? [doc] : doc; - docs.forEach(doc => { - doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf); - !this.childDocs.includes(doc) && (doc.presZoomButton = true); + docs.forEach((doc, i) => { + if (this.childDocs.includes(doc)) { + if (docs.length === i + 1) return false; + } else { + doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf); + !this.childDocs.includes(doc) && (doc.presZoomButton = true); + } }); return true; } childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement; removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); - selectElement = (doc: Doc) => this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex)); getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight - panelHeight = () => this.props.PanelHeight() - 20; + panelHeight = () => this.props.PanelHeight() - 40; active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground) && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false) + // KEYS + @observable _selectedArray: Doc[] = []; + @observable _sortedSelectedArray: Doc[] = []; + @observable _eleArray: HTMLElement[] = []; + @observable _dragArray: HTMLElement[] = []; + + //TODO: Update to radix / quick sort + @action + sortArray = (): Doc[] => { + const sort: Doc[] = this._selectedArray; + this.childDocs.forEach((doc, i) => { + if (this._selectedArray.includes(doc)) { + sort.push(doc); + } + }); + return sort; + } + + @computed get listOfSelected() { + const list = this._selectedArray.map((doc: Doc, index: any) => { + const activeItem = Cast(doc, Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc!, Doc, null); + return ( + <div className="selectedList-items">{index + 1}. {targetDoc.title}</div> + ); + }); + return list; + } + + //Regular click + @action + selectElement = (doc: Doc) => { + this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex)); + } + + //Command click + @action + multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { + this._selectedArray.push(this.childDocs[this.childDocs.indexOf(doc)]); + this._eleArray.push(ref); + this._dragArray.push(drag); + } + + //Shift click + @action + shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { + this._selectedArray = []; + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + if (activeItem) { + for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) { + this._selectedArray.push(this.childDocs[i]); + this._eleArray.push(ref); + this._dragArray.push(drag); + } + } + } + + @action + minimizeEvents = (e: KeyboardEvent) => { + e.stopPropagation(); + e.preventDefault(); + if (e.keyCode === 27) { // Escape key + this.layoutDoc.presStatus = "edit"; + } if (e.keyCode === 37) { // left(37) / a(65) / up(38) to go back + this.back(); + } if (e.keyCode === 39) { // right (39) / d(68) / down(40) to go to next + this.next(); + } + } + + //Esc click + @action + keyEvents = (e: KeyboardEvent) => { + e.stopPropagation(); + e.preventDefault(); + + if (e.keyCode === 27) { // Escape key + if (this.layoutDoc.presStatus === "edit") this._selectedArray = []; + else this.layoutDoc.presStatus = "edit"; + } if ((e.metaKey || e.altKey) && e.keyCode === 65) { // Ctrl-A to select all + if (this.layoutDoc.presStatus === "edit") this._selectedArray = this.childDocs; + } if (e.keyCode === 37) { // left(37) / a(65) / up(38) to go back + if (this.layoutDoc.presStatus !== "edit") this.back(); + } if (e.keyCode === 39) { // right (39) / d(68) / down(40) to go to next + if (this.layoutDoc.presStatus !== "edit") this.next(); + } if (e.keyCode === 32) { // spacebar to 'present' or autoplay + if (this.layoutDoc.presStatus !== "edit") this.startOrResetPres(0); + else this.layoutDoc.presStatus = "manual"; + } + if (e.keyCode === 8) { // delete selected items + if (this.layoutDoc.presStatus === "edit") { + this._selectedArray.forEach((doc, i) => { + this.removeDocument(doc); + }); + } + } + } + + @observable private transitionTools: boolean = false; + @observable private newDocumentTools: boolean = false; + @observable private progressivizeTools: boolean = false; + @observable private moreInfoTools: boolean = false; + @observable private playTools: boolean = false; + @observable private presentTools: boolean = false; + @observable private pathBoolean: boolean = false; + @observable private expandBoolean: boolean = false; + + @undoBatch + @action + viewPaths = async () => { + const srcContext = Cast(this.rootDoc.presCollection, Doc, null); + if (this.pathBoolean) { + if (srcContext) { + this.togglePath(); + srcContext._fitToBox = false; + srcContext._viewType = "freeform"; + srcContext.presPathView = false; + } + } else { + if (srcContext) { + this.togglePath(); + srcContext._fitToBox = true; + srcContext._viewType = "freeform"; + srcContext.presPathView = true; + } + } + const viewType = srcContext?._viewType; + const fit = srcContext?._fitToBox; + } + + @computed get order() { + const order: JSX.Element[] = []; + this.childDocs.forEach((doc, index) => { + const targetDoc = Cast(doc.presentationTargetDoc, Doc, null); + const srcContext = Cast(targetDoc.context, Doc, null); + if (this.rootDoc.presCollection === srcContext) { + order.push( + <div className="pathOrder" style={{ top: NumCast(targetDoc.y), left: NumCast(targetDoc.x) }}> + <div className="pathOrder-frame">{index + 1}</div> + </div>); + } else { + order.push( + <div className="pathOrder" style={{ top: 0, left: 0 }}> + <div className="pathOrder-frame">{index + 1}</div> + </div>); + } + }); + return order; + } + + @computed get paths() { + let pathPoints = ""; + this.childDocs.forEach((doc, index) => { + const targetDoc = Cast(doc.presentationTargetDoc, Doc, null); + const srcContext = Cast(targetDoc.context, Doc, null); + if (targetDoc && this.rootDoc.presCollection === srcContext) { + const n1x = NumCast(targetDoc.x) + (NumCast(targetDoc._width) / 2); + const n1y = NumCast(targetDoc.y) + (NumCast(targetDoc._height) / 2); + if (index = 0) pathPoints = n1x + "," + n1y; + else pathPoints = pathPoints + " " + n1x + "," + n1y; + } else { + if (index = 0) pathPoints = 0 + "," + 0; + else pathPoints = pathPoints + " " + 0 + "," + 0; + } + }); + return (<polyline + points={pathPoints} + style={{ + opacity: 1, + stroke: "#69a6db", + strokeWidth: 5, + strokeDasharray: '10 5', + }} + fill="none" + markerStart="url(#markerSquare)" + markerMid="url(#markerSquare)" + markerEnd="url(#markerArrow)" + />); + } + + @action togglePath = () => this.pathBoolean = !this.pathBoolean; + @action toggleExpand = () => this.expandBoolean = !this.expandBoolean; + + /** + * 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(); + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + activeItem.presFadeButton = !activeItem.presFadeButton; + if (!activeItem.presFadeButton) { + if (targetDoc) { + targetDoc.opacity = 1; + } + } else { + activeItem.presHideAfterButton = false; + if (this.rootDoc.presStatus !== "edit" && targetDoc) { + targetDoc.opacity = 0.5; + } + } + } + + // WHAT IS THIS? + @action + dropdownToggle = (menu: string) => { + const dropMenu = document.getElementById('presBox' + menu + 'Dropdown'); + if (dropMenu) dropMenu.style.display === 'none' ? dropMenu.style.display = 'block' : dropMenu.style.display = 'none'; + } + + setTransitionTime = (number: String) => { + const timeInMS = Number(number) * 1000; + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + if (targetDoc) targetDoc.presTransition = timeInMS; + } + + setDurationTime = (number: String) => { + const timeInMS = Number(number) * 1000; + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + if (targetDoc) targetDoc.presDuration = timeInMS; + } + + @computed get transitionDropdown() { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + if (activeItem && targetDoc) { + const transitionSpeed = targetDoc.presTransition ? String(Number(targetDoc.presTransition) / 1000) : 0.5; + const duration = targetDoc.presDuration ? String(Number(targetDoc.presDuration) / 1000) : 2; + const effect = targetDoc.presEffect ? targetDoc.presEffect : 'None'; + activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom'; + return ( + <div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> + <div className="ribbon-box"> + Movement + <div className="presBox-dropdown" + onPointerDown={e => e.stopPropagation()} + // onClick={() => this.dropdownToggle('Movement')} + > + {activeItem.presMovement} + <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2 }} icon={"angle-down"} /> + <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onClick={e => e.stopPropagation()}> + <div className={`presBox-dropdownOption ${activeItem.presMovement === 'None' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('none')}>None</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Zoom' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('zoom')}>Pan and Zoom</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Pan' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('nav')}>Pan</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === 'Jump cut' ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.movementChanged('switch')}>Jump cut</div> + </div> + </div> + <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "inline-flex" : "none" }}> + <div className="presBox-subheading" >Transition Speed</div> + <div className="ribbon-property"> {transitionSpeed} s </div> + </div> + <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} /> + <div className={`slider-headers ${activeItem.presMovement === 'Pan' || activeItem.presMovement === 'Zoom' ? "" : "none"}`}> + <div className="slider-text">Fast</div> + <div className="slider-text">Medium</div> + <div className="slider-text">Slow</div> + </div> + </div> + <div className="ribbon-box"> + Visibility {"&"} Duration + <div className="ribbon-doubleButton"> + <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-button ${activeItem.presHideTillShownButton ? "active" : ""}`} onClick={() => activeItem.presHideTillShownButton = !activeItem.presHideTillShownButton}>Hide before</div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-button ${activeItem.presHideAfterButton ? "active" : ""}`} onClick={() => activeItem.presHideAfterButton = !activeItem.presHideAfterButton}>Hide after</div></Tooltip> + </div> + <div className="ribbon-doubleButton" > + <div className="presBox-subheading">Slide Duration</div> + <div className="ribbon-property"> {duration} s </div> + </div> + <input type="range" step="0.1" min="0.1" max="10" value={duration} style={{ transform: 'rotate(0deg)' }} className={"toolbar-slider"} id="duration-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }} /> + <div className={"slider-headers"}> + <div className="slider-text">Short</div> + <div className="slider-text">Medium</div> + <div className="slider-text">Long</div> + </div> + </div> + <div className="ribbon-box"> + Effects + <div className="presBox-dropdown" + onPointerDown={e => e.stopPropagation()} + > + {effect} + <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2 }} icon={"angle-down"} /> + <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onClick={e => e.stopPropagation()}> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'None'}>None</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'Fade'}>Fade In</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'Flip'}>Flip</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'Rotate'}>Rotate</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'Bounce'}>Bounce</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => targetDoc.presEffect = 'Roll'}>Roll</div> + </div> + </div> + <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? "none" : "inline-flex" }}> + <div className="presBox-subheading" >Effect direction</div> + <div className="ribbon-property"> + {this.effectDirection} + </div> + </div> + <div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}> + <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "left" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'left'}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "right" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'right'}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === "top" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'top'}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === "bottom" ? "#5a9edd" : "black" }} onClick={() => targetDoc.presEffectDirection = 'bottom'}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection ? "solid 2px black" : "solid 2px #5a9edd", borderRadius: "100%" }} onClick={() => targetDoc.presEffectDirection = false}></div></Tooltip> + </div> + </div> + <div className="ribbon-final-box"> + <div className={this._selectedArray.length === 0 ? "ribbon-final-button" : "ribbon-final-button-hidden"} onClick={() => this.applyTo(this._selectedArray)}> + Apply to selected + </div> + <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}> + Apply to all + </div> + </div> + </div> + ); + } + } + + @computed get effectDirection(): string { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + let effect = ''; + switch (targetDoc.presEffectDirection) { + case 'left': effect = "Enter from left"; break; + case 'right': effect = "Enter from right"; break; + case 'top': effect = "Enter from top"; break; + case 'bottom': effect = "Enter from bottom"; break; + default: effect = "Enter from center"; break; + } + return effect; + } + + applyTo = (array: Doc[]) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + array.forEach((doc, index) => { + const curDoc = Cast(doc, Doc, null); + const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null); + if (tagDoc && targetDoc) { + tagDoc.presTransition = targetDoc.presTransition; + tagDoc.presDuration = targetDoc.presDuration; + tagDoc.presEffect = targetDoc.presEffect; + } + }); + } + + private inputRef = React.createRef<HTMLInputElement>(); + + @computed get optionsDropdown() { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + if (activeItem && targetDoc) { + return ( + <div> + <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> + <div className="ribbon-box"> + <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? "inline-flex" : "none" }}> + <div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Start automatically</div> + <div className="ribbon-button" style={{ display: "flex", backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Start manually</div> + </div> + <div className="ribbon-doubleButton" style={{ display: "flex" }}> + <div className="ribbon-button" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={() => activeItem.openDocument = !activeItem.openDocument}>Open document</div> + </div> + <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}> + <div className="ribbon-button" onClick={this.progressivizeText}>Store original website</div> + </div> + </div> + </div> + </div > + ); + } + } + + @computed get newDocumentToolbarDropdown() { + return ( + <div> + <div className={'presBox-toolbar-dropdown'} style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> + <div className="layout-container" style={{ height: 'max-content' }}> + <div className="layout" style={{ border: this.layout === 'blank' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} /> + <div className="layout" style={{ border: this.layout === 'title' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}> + <div className="title">Title</div> + <div className="subtitle">Subtitle</div> + </div> + <div className="layout" style={{ border: this.layout === 'header' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}> + <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div> + </div> + <div className="layout" style={{ border: this.layout === 'content' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}> + <div className="title" style={{ alignSelf: 'center' }}>Title</div> + <div className="content">Text goes here</div> + </div> + {/* <div className="layout" style={{ border: this.layout === 'twoColumns' ? 'solid 2px #5b9ddd' : '' }} onClick={() => runInAction(() => { this.layout = 'twoColumns'; this.createNewSlide(this.layout); })}> + <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div> + <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div> + <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div> + </div> */} + </div> + </div> + </div > + ); + } + + @observable openLayouts: boolean = false; + @observable addFreeform: boolean = true; + @observable layout: string = ""; + @observable title: string = ""; + + @computed get newDocumentDropdown() { + return ( + <div> + <div className={"presBox-ribbon"} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> + <div className="ribbon-box"> + Slide Title: <br></br> + {/* <div className="dropdown-textInput"> */} + <input className="ribbon-textInput" placeholder="..." type="text" name="fname" ref={this.inputRef} onChange={(e) => { + e.stopPropagation(); + runInAction(() => this.title = e.target.value); + }}></input> + {/* </div> */} + </div> + <div className="ribbon-box"> + Choose type: + <div className="ribbon-doubleButton"> + <div title="Text" className={'ribbon-button'} style={{ background: this.addFreeform ? "" : "#aedef8" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div> + <div title="Freeform" className={'ribbon-button'} style={{ background: this.addFreeform ? "#aedef8" : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div> + </div> + </div> + <div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}> + Preset layouts: + <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}> + <div className="layout" style={{ border: this.layout === 'blank' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'blank')} /> + <div className="layout" style={{ border: this.layout === 'title' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'title')}> + <div className="title">Title</div> + <div className="subtitle">Subtitle</div> + </div> + <div className="layout" style={{ border: this.layout === 'header' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'header')}> + <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div> + </div> + <div className="layout" style={{ border: this.layout === 'content' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'content')}> + <div className="title" style={{ alignSelf: 'center' }}>Title</div> + <div className="content">Text goes here</div> + </div> + <div className="layout" style={{ border: this.layout === 'twoColumns' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'twoColumns')}> + <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div> + <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div> + <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div> + </div> + </div> + <div className="open-layout" onClick={action(() => this.openLayouts = !this.openLayouts)}> + <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"caret-down"} size={"lg"} /> + </div> + </div> + <div className="ribbon-final-box"> + <div className={this.title !== "" && (this.addFreeform && this.layout !== "" || !this.addFreeform) ? "ribbon-final-button-hidden" : "ribbon-final-button"} onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}> + Create New Slide + </div> + </div> + </div> + </div > + ); + } + + createNewSlide = (layout?: string, title?: string, freeform?: boolean) => { + let doc = undefined; + if (layout) doc = this.createTemplate(layout); + if (freeform && layout) doc = this.createTemplate(layout, title); + if (!freeform && !layout) doc = Docs.Create.TextDocument("", { _nativeWidth: 400, _width: 225, title: title }); + const presCollection = Cast(this.layoutDoc.presCollection, Doc, null); + const data = Cast(presCollection.data, listSpec(Doc)); + const presData = Cast(this.rootDoc.data, listSpec(Doc)); + if (data && doc && presData) { + data.push(doc); + DockedFrameRenderer.PinDoc(doc, false); + this.gotoDocument(this.childDocs.length, this.itemIndex); + } + } + + createTemplate = (layout: string, input?: string) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + let x = 0; + let y = 0; + if (activeItem && targetDoc) { + x = NumCast(targetDoc.x); + y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20; + } + let doc = undefined; + const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24pt", }); + const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16pt" }); + const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20pt" }); + const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24pt" }); + const content = Docs.Create.TextDocument("Click to change texte", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14pt" }); + const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14pt" }); + const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14pt" }); + switch (layout) { + case 'blank': + doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y }); + break; + case 'title': + doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitToBox: true, x: x, y: y }); + break; + case 'header': + doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitToBox: true, x: x, y: y }); + break; + case 'content': + doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitToBox: true, x: x, y: y }); + break; + case 'twoColumns': + doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitToBox: true, x: x, y: y }); + break; + default: + break; + } + return doc; + } + + // Dropdown that appears for autoplay + @computed get playDropdown() { + return ( + <div className={`dropdown-play ${this.playTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> + <div className="dropdown-play-button" onClick={() => this.startOrResetPres(this.itemIndex)}> + Present from current slide + </div> + <div className="dropdown-play-button" onClick={() => this.startOrResetPres(0)}> + Present from first slide + </div> + </div> + ); + } + + // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view) + @computed get presentDropdown() { + return ( + <div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> + <div className="dropdown-play-button" onClick={this.updateMinimize}> + Minimize + </div> + <div className="dropdown-play-button" onClick={() => { this.startOrResetPres(0); this.turnOffEdit(); }}> + Sidebar view + </div> + </div> + ); + } + + // For toggling the options when the user wants to select play + @action togglePlay = () => { this.playTools = !this.playTools; }; + + // For toggling the options when the user wants to select play + @action togglePresent = () => { this.presentTools = !this.presentTools; }; + + // Case in which the document has keyframes to navigate to next key frame + @undoBatch + @action + nextKeyframe = (tagDoc: Doc): void => { + const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]); + const currentFrame = Cast(tagDoc.currentFrame, "number", null); + if (currentFrame === undefined) { + tagDoc.currentFrame = 0; + CollectionFreeFormDocumentView.setupScroll(tagDoc, 0); + CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); + } + // let lastFrame: number = 0; + // childDocs.forEach((doc) => { + // if (NumCast(doc.appearFrame) > lastFrame) lastFrame = NumCast(doc.appearFrame); + // }); + CollectionFreeFormDocumentView.updateScrollframe(tagDoc, currentFrame); + CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0); + tagDoc.currentFrame = Math.max(0, (currentFrame || 0) + 1); + tagDoc.lastFrame = Math.max(NumCast(tagDoc.currentFrame), NumCast(tagDoc.lastFrame)); + if (tagDoc.zoomProgressivize) { + const resize = document.getElementById('resizable'); + if (resize) { + resize.style.width = this.checkList(tagDoc, tagDoc["viewfinder-width-indexed"]) + 'px'; + resize.style.height = this.checkList(tagDoc, tagDoc["viewfinder-height-indexed"]) + 'px'; + resize.style.top = this.checkList(tagDoc, tagDoc["viewfinder-top-indexed"]) + 'px'; + resize.style.left = this.checkList(tagDoc, tagDoc["viewfinder-left-indexed"]) + 'px'; + } + } + } + + @undoBatch + @action + prevKeyframe = (tagDoc: Doc): void => { + const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]); + const currentFrame = Cast(tagDoc.currentFrame, "number", null); + if (currentFrame === undefined) { + tagDoc.currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); + } + CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice()); + tagDoc.currentFrame = Math.max(0, (currentFrame || 0) - 1); + if (tagDoc.zoomProgressivize) { + const resize = document.getElementById('resizable'); + if (resize) { + resize.style.width = this.checkList(tagDoc, tagDoc["viewfinder-width-indexed"]) + 'px'; + resize.style.height = this.checkList(tagDoc, tagDoc["viewfinder-height-indexed"]) + 'px'; + resize.style.top = this.checkList(tagDoc, tagDoc["viewfinder-top-indexed"]) + 'px'; + resize.style.left = this.checkList(tagDoc, tagDoc["viewfinder-left-indexed"]) + 'px'; + } + } + } + + @computed get stringType(): string { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + let type: string = ''; + if (activeItem) { + switch (targetDoc.type) { + case DocumentType.PDF: type = "PDF"; break; + case DocumentType.RTF: type = "Text node"; break; + case DocumentType.COL: type = "Collection"; break; + case DocumentType.AUDIO: type = "Audio"; break; + case DocumentType.VID: type = "Video"; break; + case DocumentType.IMG: type = "Image"; break; + default: type = "Other node"; break; + } + } + return type; + } + + @computed get progressivizeDropdown() { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + + if (activeItem && targetDoc) { + return ( + <div> + <div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> + <div className="ribbon-box"> + {this.stringType} selected + <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform' ? "inline-flex" : "none" }}> + <div className="ribbon-button" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Child documents</div> + <div className="ribbon-button" style={{ display: activeItem.presProgressivize ? "flex" : "none", backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div> + </div> + <div className="ribbon-doubleButton" style={{ display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}> + <div className="ribbon-button" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Internal zoom</div> + <div className="ribbon-button" style={{ display: activeItem.zoomProgressivize ? "flex" : "none", backgroundColor: targetDoc.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Viewfinder</div> + {/* <div className="ribbon-button" style={{ display: activeItem.zoomProgressivize ? "flex" : "none", backgroundColor: targetDoc.editSnapZoomProgressivize ? "#aedef8" : "" }} onClick={this.editSnapZoomProgressivize}>Snapshot</div> */} + </div> + <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform' ? "inline-flex" : "none" }}> + <div className="ribbon-button" onClick={this.progressivizeText}>Text progressivize</div> + <div className="ribbon-button" style={{ display: activeItem.textProgressivize ? "flex" : "none", backgroundColor: targetDoc.editTextProgressivize ? "#aedef8" : "" }} onClick={this.editTextProgressivize}>Edit</div> + </div> + <div className="ribbon-doubleButton" style={{ display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}> + <div className="ribbon-button" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll progressivize</div> + <div className="ribbon-button" style={{ display: activeItem.scrollProgressivize ? "flex" : "none", backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div> + </div> + </div> + <div className="ribbon-final-box" style={{ display: activeItem.zoomProgressivize || activeItem.scrollProgressivize || activeItem.presProgressivize || activeItem.textProgressivize ? "grid" : "none" }}> + Frames + <div className="ribbon-doubleButton"> + <div className="ribbon-frameSelector"> + <div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc); }}> + <FontAwesomeIcon icon={"caret-left"} size={"lg"} /> + </div> + <div key="num" title="toggle view all" className="numKeyframe" style={{ backgroundColor: targetDoc.editing ? "#5a9edd" : "#5a9edd" }} + onClick={action(() => targetDoc.editing = !targetDoc.editing)} > + {NumCast(targetDoc.currentFrame)} + </div> + <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={e => { e.stopPropagation(); this.nextKeyframe(targetDoc); }}> + <FontAwesomeIcon icon={"caret-right"} size={"lg"} /> + </div> + </div> + <Tooltip title={<><div className="dash-tooltip">{"Last frame"}</div></>}><div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div></Tooltip> + </div> + <div className="ribbon-button" style={{ height: 20, backgroundColor: "#5a9edd" }} onClick={() => console.log(" TODO: play frames")}>Play</div> + </div> + </div> + </div> + ); + } + } + + turnOffEdit = () => { + this.childDocs.forEach((doc, index) => { + const targetDoc = Cast(doc.presentationTargetDoc, Doc, null); + targetDoc.editSnapZoomProgressivize = false; + targetDoc.editZoomProgressivize = false; + targetDoc.editScrollProgressivize = false; + if (doc.type === DocumentType.WEB) { + doc.presWebsite = doc.data; + } + }); + } + + //Toggle whether the user edits or not + @action + editSnapZoomProgressivize = (e: React.MouseEvent) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + if (!targetDoc.editSnapZoomProgressivize) { + targetDoc.editSnapZoomProgressivize = true; + } else { + targetDoc.editSnapZoomProgressivize = false; + } + + } + + //Toggle whether the user edits or not + @action + editZoomProgressivize = (e: React.MouseEvent) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + if (!targetDoc.editZoomProgressivize) { + targetDoc.editZoomProgressivize = true; + } else { + targetDoc.editZoomProgressivize = false; + } + } + + //Toggle whether the user edits or not + @action + editScrollProgressivize = (e: React.MouseEvent) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + if (!targetDoc.editScrollProgressivize) { + targetDoc.editScrollProgressivize = true; + } else { + targetDoc.editScrollProgressivize = false; + } + } + + //Progressivize Zoom + @action + progressivizeScroll = (e: React.MouseEvent) => { + e.stopPropagation(); + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + activeItem.scrollProgressivize = !activeItem.scrollProgressivize; + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + targetDoc.scrollProgressivize = !targetDoc.zoomProgressivize; + CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc.currentFrame), true); + if (targetDoc.editScrollProgressivize) { + targetDoc.editScrollProgressivize = false; + targetDoc.currentFrame = 0; + targetDoc.lastFrame = 0; + } + } + + //Progressivize Zoom + @action + progressivizeZoom = (e: React.MouseEvent) => { + e.stopPropagation(); + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + activeItem.zoomProgressivize = !activeItem.zoomProgressivize; + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + targetDoc.zoomProgressivize = !targetDoc.zoomProgressivize; + CollectionFreeFormDocumentView.setupZoom(targetDoc, true); + if (targetDoc.editZoomProgressivize) { + targetDoc.editZoomProgressivize = false; + targetDoc.currentFrame = 0; + targetDoc.lastFrame = 0; + } + } + + //Progressivize Text nodes + @action + editTextProgressivize = (e: React.MouseEvent) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + targetDoc.currentFrame = targetDoc.lastFrame; + if (targetDoc?.editTextProgressivize) { + targetDoc.editTextProgressivize = false; + } else { + targetDoc.editTextProgressivize = true; + } + } + + @action + progressivizeText = (e: React.MouseEvent) => { + e.stopPropagation(); + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + activeItem.presProgressivize = !activeItem.presProgressivize; + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); + targetDoc.presProgressivize = !targetDoc.presProgressivize; + if (activeItem.presProgressivize) { + targetDoc.currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true); + targetDoc.lastFrame = docs.length - 1; + } + } + + //Progressivize Child Docs + @action + editProgressivize = (e: React.MouseEvent) => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + targetDoc.currentFrame = targetDoc.lastFrame; + if (targetDoc?.editProgressivize) { + targetDoc.editProgressivize = false; + } else { + targetDoc.editProgressivize = true; + } + } + + @action + progressivizeChild = (e: React.MouseEvent) => { + e.stopPropagation(); + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); + const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); + if (!activeItem.presProgressivize) { + activeItem.presProgressivize = true; + targetDoc.presProgressivize = true; + targetDoc.currentFrame = 0; + CollectionFreeFormDocumentView.setupKeyframes(docs, docs.length, true); + targetDoc.lastFrame = docs.length - 1; + } else { + targetDoc.editProgressivize = false; + activeItem.presProgressivize = false; + targetDoc.presProgressivize = false; + // docs.forEach((doc, index) => { + // doc.appearFrame = 0; + // }); + targetDoc.currentFrame = 0; + targetDoc.lastFrame = 0; + } + } + + @action + checkMovementLists = (doc: Doc, xlist: any, ylist: any) => { + const x: List<number> = xlist; + const y: List<number> = ylist; + const tags: JSX.Element[] = []; + let pathPoints = ""; //List of all of the pathpoints that need to be added + for (let i = 0; i < x.length - 1; i++) { + if (y[i] || x[i]) { + if (i === 0) pathPoints = (x[i] - 11) + "," + (y[i] + 33); + else pathPoints = pathPoints + " " + (x[i] - 11) + "," + (y[i] + 33); + tags.push(<div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}>{i}</div>); + } + } + tags.push(<svg style={{ overflow: 'visible', position: 'absolute' }}><polyline + points={pathPoints} + style={{ + position: 'absolute', + opacity: 1, + stroke: "#000000", + strokeWidth: 2, + strokeDasharray: '10 5', + }} + fill="none" + /></svg>); + return tags; + } + + @observable + toggleDisplayMovement = (doc: Doc) => { + if (doc.displayMovement) doc.displayMovement = false; + else doc.displayMovement = true; + } + + private _isDraggingTL = false; + private _isDraggingTR = false; + private _isDraggingBR = false; + private _isDraggingBL = false; + private _isDragging = false; + // private _drag = ""; + + // onPointerDown = (e: React.PointerEvent): void => { + // e.stopPropagation(); + // e.preventDefault(); + // if (e.button === 0) { + // this._drag = e.currentTarget.id; + // console.log(this._drag); + // } + // document.removeEventListener("pointermove", this.onPointerMove); + // document.addEventListener("pointermove", this.onPointerMove); + // document.removeEventListener("pointerup", this.onPointerUp); + // document.addEventListener("pointerup", this.onPointerUp); + // } + + + //Adds event listener so knows pointer is down and moving + onPointerMid = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isDragging = true; + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + //Adds event listener so knows pointer is down and moving + onPointerBR = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isDraggingBR = true; + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + //Adds event listener so knows pointer is down and moving + onPointerBL = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isDraggingBL = true; + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + //Adds event listener so knows pointer is down and moving + onPointerTR = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isDraggingTR = true; + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + //Adds event listener so knows pointer is down and moving + onPointerTL = (e: React.PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isDraggingTL = true; + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + document.addEventListener("pointerup", this.onPointerUp); + } + + //Removes all event listeners + onPointerUp = (e: PointerEvent): void => { + e.stopPropagation(); + e.preventDefault(); + this._isDraggingTL = false; + this._isDraggingTR = false; + this._isDraggingBL = false; + this._isDraggingBR = false; + this._isDragging = false; + document.removeEventListener("pointermove", this.onPointerMove); + document.removeEventListener("pointerup", this.onPointerUp); + } + + //Adjusts the value in NodeStore + onPointerMove = (e: PointerEvent): void => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + const tagDocView = DocumentManager.Instance.getDocumentView(targetDoc); + e.stopPropagation(); + e.preventDefault(); + const doc = document.getElementById('resizable'); + if (doc && tagDocView) { + + const scale = tagDocView.childScaling(); + const scale2 = tagDocView.props.ScreenToLocalTransform().Scale; + let height = doc.offsetHeight; + let width = doc.offsetWidth; + let top = doc.offsetTop; + let left = doc.offsetLeft; + // const newHeightB = height += (e.movementY * NumCast(targetDoc._viewScale)); + // const newHeightT = height -= (e.movementY * NumCast(targetDoc._viewScale)); + // const newWidthR = width += (e.movementX * NumCast(targetDoc._viewScale)); + // const newWidthL = width -= (e.movementX * NumCast(targetDoc._viewScale)); + // const newLeft = left += (e.movementX * NumCast(targetDoc._viewScale)); + // const newTop = top += (e.movementY * NumCast(targetDoc._viewScale)); + // switch (this._drag) { + // case "": break; + // case "resizer-br": + // doc.style.height = newHeightB + 'px'; + // doc.style.width = newWidthR + 'px'; + // break; + // case "resizer-bl": + // doc.style.height = newHeightB + 'px'; + // doc.style.width = newWidthL + 'px'; + // doc.style.left = newLeft + 'px'; + // break; + // case "resizer-tr": + // doc.style.width = newWidthR + 'px'; + // doc.style.height = newHeightT + 'px'; + // doc.style.top = newTop + 'px'; + // case "resizer-tl": + // doc.style.width = newWidthL + 'px'; + // doc.style.height = newHeightT + 'px'; + // doc.style.top = newTop + 'px'; + // doc.style.left = newLeft + 'px'; + // case "resizable": + // doc.style.top = newTop + 'px'; + // doc.style.left = newLeft + 'px'; + // } + //Bottom right + if (this._isDraggingBR) { + const newHeight = height += (e.movementY * scale2); + doc.style.height = newHeight + 'px'; + const newWidth = width += (e.movementX * scale2); + doc.style.width = newWidth + 'px'; + // Bottom left + } else if (this._isDraggingBL) { + const newHeight = height += (e.movementY * scale); + doc.style.height = newHeight + 'px'; + const newWidth = width -= (e.movementX * scale); + doc.style.width = newWidth + 'px'; + const newLeft = left += (e.movementX * scale); + doc.style.left = newLeft + 'px'; + // Top right + } else if (this._isDraggingTR) { + const newWidth = width += (e.movementX * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.width = newWidth + 'px'; + const newHeight = height -= (e.movementY * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.height = newHeight + 'px'; + const newTop = top += (e.movementY * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.top = newTop + 'px'; + // Top left + } else if (this._isDraggingTL) { + const newWidth = width -= (e.movementX * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.width = newWidth + 'px'; + const newHeight = height -= (e.movementY * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.height = newHeight + 'px'; + const newTop = top += (e.movementY * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.top = newTop + 'px'; + const newLeft = left += (e.movementX * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.left = newLeft + 'px'; + } else if (this._isDragging) { + const newTop = top += (e.movementY * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.top = newTop + 'px'; + const newLeft = left += (e.movementX * tagDocView.props.ScreenToLocalTransform().Scale); + doc.style.left = newLeft + 'px'; + } + this.updateList(targetDoc, targetDoc["viewfinder-width-indexed"], width); + this.updateList(targetDoc, targetDoc["viewfinder-height-indexed"], height); + this.updateList(targetDoc, targetDoc["viewfinder-top-indexed"], top); + this.updateList(targetDoc, targetDoc["viewfinder-left-indexed"], left); + } + } + + @action + checkList = (doc: Doc, list: any): number => { + const x: List<number> = list; + if (x && x.length >= NumCast(doc.currentFrame) + 1) { + return x[NumCast(doc.currentFrame)]; + } else { + x.length = NumCast(doc.currentFrame) + 1; + x[NumCast(doc.currentFrame)] = x[NumCast(doc.currentFrame) - 1]; + return x[NumCast(doc.currentFrame)]; + } + + } + + @action + updateList = (doc: Doc, list: any, val: number) => { + const x: List<number> = list; + if (x && x.length >= NumCast(doc.currentFrame) + 1) { + x[NumCast(doc.currentFrame)] = val; + list = x; + } else { + x.length = NumCast(doc.currentFrame) + 1; + x[NumCast(doc.currentFrame)] = val; + list = x; + } + } + + // scale: NumCast(targetDoc._viewScale), + @computed get zoomProgressivizeContainer() { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + if (targetDoc.editZoomProgressivize) { + return ( + <> + <div id="resizable" className="resizable" onPointerDown={this.onPointerMid} style={{ width: this.checkList(targetDoc, targetDoc["viewfinder-width-indexed"]), height: this.checkList(targetDoc, targetDoc["viewfinder-height-indexed"]), top: this.checkList(targetDoc, targetDoc["viewfinder-top-indexed"]), left: this.checkList(targetDoc, targetDoc["viewfinder-left-indexed"]), position: 'absolute' }}> + <div className='resizers'> + <div id="resizer-tl" className='resizer top-left' onPointerDown={this.onPointerTL}></div> + <div id="resizer-tr" className='resizer top-right' onPointerDown={this.onPointerTR}></div> + <div id="resizer-bl" className='resizer bottom-left' onPointerDown={this.onPointerBL}></div> + <div id="resizer-br" className='resizer bottom-right' onPointerDown={this.onPointerBR}></div> + </div> + </div> + </> + ); + } else return null; + } + + @computed get progressivizeChildDocs() { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); + const tags: JSX.Element[] = []; + docs.forEach((doc, index) => { + if (doc["x-indexed"] && doc["y-indexed"]) { + tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>); + } + tags.push( + <div className="progressivizeButton" onPointerLeave={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc.currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}> + <div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div> + <div className="progressivizeButton-frame">{doc.appearFrame}</div> + <div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div> + </div>); + }); + return tags; + } + + @undoBatch + @action + nextAppearFrame = (doc: Doc, i: number): void => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + const appearFrame = Cast(doc.appearFrame, "number", null); + if (appearFrame === undefined) { + doc.appearFrame = 0; + } + doc.appearFrame = appearFrame + 1; + this.updateOpacityList(doc["opacity-indexed"], NumCast(doc.appearFrame)); + } + + @undoBatch + @action + prevAppearFrame = (doc: Doc, i: number): void => { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); + const appearFrame = Cast(doc.appearFrame, "number", null); + if (appearFrame === undefined) { + doc.appearFrame = 0; + } + doc.appearFrame = Math.max(0, appearFrame - 1); + this.updateOpacityList(doc["opacity-indexed"], NumCast(doc.appearFrame)); + } + + @action + updateOpacityList = (list: any, frame: number) => { + const x: List<number> = list; + if (x && x.length >= frame) { + for (let i = 0; i < x.length; i++) { + if (i < frame) { + x[i] = 0; + } else if (i >= frame) { + x[i] = 1; + } + } + list = x; + } else { + x.length = frame + 1; + for (let i = 0; i < x.length; i++) { + if (i < frame) { + x[i] = 0; + } else if (i >= frame) { + x[i] = 1; + } + } + list = x; + } + } + + @computed get moreInfoDropdown() { + return (<div></div>); + } + + @computed + get toolbarWidth(): number { + const width = this.props.PanelWidth(); + return width; + } + + @computed get toolbar() { + const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); + if (activeItem) { + return ( + <> + <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}><FontAwesomeIcon icon={"plus"} /> + <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} /> + </div></Tooltip> + <div className="toolbar-divider" /> + <Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}><div className={`toolbar-button ${this.pathBoolean ? "active" : ""}`}> + <FontAwesomeIcon icon={"exchange-alt"} onClick={this.viewPaths} /> + </div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{this.expandBoolean ? "Minimize all" : "Expand all"}</div></>}> + <div className={`toolbar-button ${this.expandBoolean ? "active" : ""}`} onClick={() => { this.toggleExpand(); this.childDocs.forEach((doc, ind) => { if (this.expandBoolean) doc.presExpandInlineButton = true; else doc.presExpandInlineButton = false; }); }}> + <FontAwesomeIcon icon={"eye"} /> + </div> + </Tooltip> + {/* <div className="toolbar-button"><FontAwesomeIcon title={"Portal"} icon={"eye"} onClick={this.toolbarTest} /></div> */} + <div className="toolbar-divider" /> + {/* <Tooltip title={<><div className="dash-tooltip">{"Transitions"}</div></>}><div className={`toolbar-button ${this.transitionTools ? "active" : ""}`} onClick={this.toggleTransitionTools}> + <FontAwesomeIcon icon={"rocket"} /> + <div style={{ display: this.props.PanelWidth() > 430 ? "block" : "none" }} className="toolbar-buttonText"> Transitions</div> + <FontAwesomeIcon className={`dropdown ${this.transitionTools ? "active" : ""}`} icon={"angle-down"} /> + </div></Tooltip> + <div className="toolbar-divider" /> + <Tooltip title={<><div className="dash-tooltip">{"Progressivize"}</div></>}><div className={`toolbar-button ${this.progressivizeTools ? "active" : ""}`} onClick={this.toggleProgressivize}> + <FontAwesomeIcon icon={"tasks"} /> + <div style={{ display: this.props.PanelWidth() > 430 ? "block" : "none" }} className="toolbar-buttonText"> Progressivize</div> + <FontAwesomeIcon className={`dropdown ${this.progressivizeTools ? "active" : ""}`} icon={"angle-down"} /> + </div></Tooltip> + <div className="toolbar-divider" /> */} + {/* <div className="toolbar-button" style={{ position: 'absolute', right: 23, transform: 'rotate(45deg)', fontSize: 16 }}> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"thumbtack"} /> + </div> + <div className={`toolbar-button ${this.moreInfoTools ? "active" : ""}`} onClick={this.toggleMoreInfo}> + <div className={`toolbar-moreInfo ${this.moreInfoTools ? "active" : ""}`}> + <div className="toolbar-moreInfoBall" /> + <div className="toolbar-moreInfoBall" /> + <div className="toolbar-moreInfoBall" /> + </div> + </div> */} + </> + ); + } else { + return ( + <> + <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}> + <FontAwesomeIcon icon={"plus"} /> + <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} /> + </div></Tooltip> + {/* <div className="toolbar-button" style={{ position: 'absolute', right: 23, transform: 'rotate(45deg)', fontSize: 16 }}> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"thumbtack"} /> + </div> + <div className={`toolbar-button ${this.moreInfoTools ? "active" : ""}`} onClick={this.toggleMoreInfo}> + <div className={`toolbar-moreInfo ${this.moreInfoTools ? "active" : ""}`}> + <div className="toolbar-moreInfoBall" /> + <div className="toolbar-moreInfoBall" /> + <div className="toolbar-moreInfoBall" /> + </div> + </div> */} + </> + ); + } + } + render() { - // const presOrderedDocs = DocListCast(this.rootDoc.presOrderedDocs); - // if (presOrderedDocs.length != this.childDocs.length || presOrderedDocs.some((pd, i) => pd !== this.childDocs[i])) { - // this.rootDoc.presOrderedDocs = new List<Doc>(this.childDocs.slice()); - // } - this.childDocs.slice(); // needed to insure that the childDocs are loaded for looking up fields + this.childDocs.slice(); // needed to insure that the childDocs are loaded for looking up fields const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; - return <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} > + // const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => { + // return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false); + // }; + return <div onPointerOver={this.onPointerOver} onPointerLeave={this.onPointerLeave} className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} > <div className="presBox-buttons" style={{ display: this.rootDoc._chromeStatus === "disabled" ? "none" : undefined }}> <select className="presBox-viewPicker" onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}> - <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Invalid}>Min</option> <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option> - <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Time}>Time</option> <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel}>Slides</option> </select> - <div className="presBox-button" title="Back" style={{ gridColumn: 2 }} onClick={this.back}> - <FontAwesomeIcon icon={"arrow-left"} /> - </div> - <div className="presBox-button" title={"Reset Presentation" + this.layoutDoc.presStatus ? "" : " From Start"} style={{ gridColumn: 3 }} onClick={this.startOrResetPres}> - <FontAwesomeIcon icon={this.layoutDoc.presStatus ? "stop" : "play"} /> - </div> - <div className="presBox-button" title="Next" style={{ gridColumn: 4 }} onClick={this.next}> - <FontAwesomeIcon icon={"arrow-right"} /> + <div className="presBox-presentPanel"> + <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}> + <div className="presBox-button-left" onClick={() => this.layoutDoc.presStatus = "manual"}><FontAwesomeIcon icon={"play-circle"} /> Present </div> + <div className={`presBox-button-right ${this.presentTools ? "active" : ""}`} onClick={e => { e.stopPropagation; this.togglePresent(); }}> + <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"angle-down"} /> + {this.presentDropdown} + </div> + </span> + <div className="miniPresOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}> + {/* <span className={`presBox-button ${this.layoutDoc.presStatus !== "edit" ? "active" : ""}`}> + <div className="presBox-button-left" onClick={() => this.startOrResetPres(0)}> + <FontAwesomeIcon icon={"clock"} /> + <FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /> + </div> + <div className={`presBox-button-right ${this.playTools ? "active" : ""}`} onClick={e => { e.stopPropagation; this.togglePlay(); }}> + <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.playTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"angle-down"} /> + {this.playDropdown} + </div> + </span> */} + <div className="miniPres-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div> + <div className="miniPres-button" onClick={() => this.startOrResetPres(this.itemIndex)}><FontAwesomeIcon icon={PresBox.Instance.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div> + <div className="miniPres-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div> + <div className="miniPres-divider"></div> + <div className="miniPres-button-text">Slide {this.itemIndex + 1} / {this.childDocs.length}</div> + <div className="miniPres-divider"></div> + <div className="miniPres-button" onClick={() => this.layoutDoc.presStatus = "edit"}> + <FontAwesomeIcon icon={"times"} /> + </div> + </div> </div> </div> - <div className="presBox-listCont" > + <div id="toolbarContainer" className={`presBox-toolbar ${this.layoutDoc.presStatus === "edit" ? "active" : ""}`}> {this.toolbar} </div> + {this.newDocumentToolbarDropdown} + <div className="presBox-listCont"> {mode !== CollectionViewType.Invalid ? <CollectionView {...this.props} ContainingCollectionDoc={this.props.Document} @@ -346,9 +1718,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) { if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data); - if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 50 : 46; + if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 30 : 26; if (field === 'presStatus') return container.presStatus; if (field === '_itemIndex') return container._itemIndex; if (field === 'presBox') return container; return undefined; -}); +});
\ No newline at end of file |