diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/ContentFittingDocumentView.tsx | 8 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentLinksButton.scss | 6 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentLinksButton.tsx | 4 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 12 | ||||
| -rw-r--r-- | src/client/views/nodes/PresBox.scss | 3 | ||||
| -rw-r--r-- | src/client/views/nodes/PresBox.tsx | 789 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.scss | 3 | ||||
| -rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 14 | ||||
| -rw-r--r-- | src/client/views/nodes/WebBox.tsx | 20 |
9 files changed, 544 insertions, 315 deletions
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx index bc3926ff4..bc3ad5bce 100644 --- a/src/client/views/nodes/ContentFittingDocumentView.tsx +++ b/src/client/views/nodes/ContentFittingDocumentView.tsx @@ -38,7 +38,13 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp private PanelHeight = () => this.panelHeight; @computed get panelWidth() { return this.nativeWidth() && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); } - @computed get panelHeight() { return this.nativeHeight() && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); } + @computed get panelHeight() { + if (this.nativeHeight()) { + if (!this.props.Document._fitWidth) return this.nativeHeight() * this.contentScaling() + else return this.panelWidth / Doc.NativeAspect(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 1; + } + return this.props.PanelHeight(); + } @computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); } // this is intended to detect when a document is being rendered inside itself as part of a template, but not as a leaf node where nativeWidth & height would apply. private getTransform = () => this.props.dontCenter ? diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss index 9328fb96b..735aa669f 100644 --- a/src/client/views/nodes/DocumentLinksButton.scss +++ b/src/client/views/nodes/DocumentLinksButton.scss @@ -1,6 +1,11 @@ @import "../globalCssVariables.scss"; +.documentLinksButton-cont { + min-width: 20; + min-height: 20; + position: absolute; +} .documentLinksButton, .documentLinksButton-endLink, .documentLinksButton-startLink { @@ -29,6 +34,7 @@ .documentLinksButton { background-color: black; + font-weight: bold; &:hover { background: $main-accent; diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index ddfb3cc34..a167f2a23 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -243,13 +243,13 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp src={`/assets/${"link.png"}`} />; const linkButton = <div className="documentLinksButton-cont" ref={this._linkButton} style={{ - minWidth: 20, minHeight: 20, position: "absolute", left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}> <div className={"documentLinksButton"} style={{ backgroundColor: this.props.InMenu ? "" : "#add8e6", color: this.props.InMenu ? "white" : "black", - width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px", fontWeight: "bold" + width: this.props.InMenu ? "20px" : "30px", + height: this.props.InMenu ? "20px" : "30px", }} onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick} // onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d227d3a8e..0b27cd03c 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -423,7 +423,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu this._downX = touch.clientX; this._downY = touch.clientY; if (!e.nativeEvent.cancelBubble) { - if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) e.stopPropagation(); + if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation(); this.removeMoveListeners(); this.addMoveListeners(); this.removeEndListeners(); @@ -438,7 +438,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (e.cancelBubble && this.active) { this.removeMoveListeners(); } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { const touch = me.touchEvent.changedTouches.item(0); if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) { @@ -554,7 +554,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if ((this.active || this.layoutDoc.onDragStart) && !e.ctrlKey && (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) && - !this.layoutDoc.inOverlay) { + !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { e.stopPropagation(); if (SelectionManager.IsSelected(this, true) && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it } @@ -573,7 +573,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu if (e.cancelBubble && this.active) { document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView) } - else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) { + else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart) && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) { if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) { document.removeEventListener("pointermove", this.onPointerMove); @@ -889,7 +889,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu e?.stopPropagation(); // DocumentViews should stop propagation of this event } cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15); - !this.isSelected(true) && SelectionManager.SelectDoc(this, false); + !this.isSelected(true) && setTimeout(() => SelectionManager.SelectDoc(this, false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear. }); } @@ -920,7 +920,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; } childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling()); - @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, undefined]; } + @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, -20]; } @computed get contents() { TraceMobx(); return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any, borderRadius: "inherit", width: "100%", height: "100%" }}> diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss index 9a8b861ba..07cf63532 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/PresBox.scss @@ -56,11 +56,14 @@ $light-background: #ececec; letter-spacing: 0; display: flex; align-items: center; + justify-content: center; transition: 0.5s; } .toolbar-button.active { color: $light-blue; + background-color: white; + border-radius: 100%; } .toolbar-transitionButtons { diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index 01a7bed8c..0e92044e0 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -1,7 +1,7 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; -import { action, computed, observable, runInAction } from "mobx"; +import { action, computed, observable, runInAction, ObservableMap, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; import { Doc, DocCastAsync, DocListCast, DocListCastAsync } from "../../../fields/Doc"; @@ -19,7 +19,7 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DocumentManager } from "../../util/DocumentManager"; import { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; -import { undoBatch } from "../../util/UndoManager"; +import { undoBatch, UndoManager } from "../../util/UndoManager"; import { CollectionDockingView } from "../collections/CollectionDockingView"; import { CollectionView, CollectionViewType } from "../collections/CollectionView"; import { TabDocView } from "../collections/TabDocView"; @@ -28,7 +28,6 @@ import { AudioBox } from "./AudioBox"; import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; import { FieldView, FieldViewProps } from './FieldView'; import "./PresBox.scss"; -import { VideoBox } from "./VideoBox"; export enum PresMovement { Zoom = "zoom", @@ -38,12 +37,12 @@ export enum PresMovement { } export enum PresEffect { - Fade = "fade", - Flip = "flip", - Rotate = "rotate", - Bounce = "bounce", - Roll = "roll", - None = "none", + Fade = "Fade", + Flip = "Flip", + Rotate = "Rotate", + Bounce = "Bounce", + Roll = "Roll", + None = "None", } enum PresStatus { @@ -52,6 +51,13 @@ enum PresStatus { Edit = "edit" } +enum PresColors { + LightBlue = "#AEDDF8", + DarkBlue = "#5B9FDD", + LightBackground = "#ececec", + SlideBackground = "#d5dce2", +} + type PresBoxSchema = makeInterface<[typeof documentSchema]>; const PresBoxDocument = makeInterface(documentSchema); @@ -59,27 +65,37 @@ const PresBoxDocument = makeInterface(documentSchema); export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); } - public static Instance: PresBox; + @observable public static Instance: PresBox; @observable _isChildActive = false; @observable _moveOnFromAudio: boolean = true; @observable _presTimer!: NodeJS.Timeout; + @observable _presKeyEventsActive: boolean = false; - @observable _selectedArray: Doc[] = []; - @observable _sortedSelectedArray: Doc[] = []; + @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>(); @observable _eleArray: HTMLElement[] = []; @observable _dragArray: HTMLElement[] = []; + @observable _pathBoolean: boolean = false; + @observable _expandBoolean: boolean = false; + private _disposers: { [name: string]: IReactionDisposer } = {}; @observable private transitionTools: boolean = false; @observable private newDocumentTools: boolean = false; @observable private progressivizeTools: boolean = false; - @observable private presentTools: boolean = false; - @observable private pathBoolean: boolean = false; @observable private openMovementDropdown: boolean = false; @observable private openEffectDropdown: boolean = false; + @observable private presentTools: boolean = false; @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); } + @computed get tagDocs() { + const tagDocs: Doc[] = []; + for (const doc of this.childDocs) { + const tagDoc = Cast(doc.presentationTargetDoc, Doc, null); + tagDocs.push(tagDoc) + } + return tagDocs; + } @computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); } - @computed get activeItem() { return Cast(this.childDocs[this.itemIndex], Doc, null); } + @computed get activeItem() { return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null); } @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); } @computed get scrollable(): boolean { if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true; @@ -92,10 +108,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); } constructor(props: any) { super(props); - if (Doc.UserDoc().activePresentation = this.rootDoc) PresBox.Instance = this; + if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => 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: 0, isTemplateDoc: true, isTemplateForField: "data" + title: "pres element template", type: DocumentType.PRESELEMENT, 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 @@ -108,60 +124,73 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } @computed get selectedDocumentView() { if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0]; - if (PresBox.Instance && PresBox.Instance._selectedArray) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); - return undefined; + if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc); } @computed get isPres(): boolean { - document.removeEventListener("keydown", this.keyEvents, true); + document.removeEventListener("keydown", PresBox.keyEventsWrapper, true); if (this.selectedDoc?.type === DocumentType.PRES) { - document.addEventListener("keydown", this.keyEvents, true); + document.removeEventListener("keydown", PresBox.keyEventsWrapper, true); + document.addEventListener("keydown", PresBox.keyEventsWrapper, true); return true; } return false; } @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } + @action componentWillUnmount() { - document.removeEventListener("keydown", this.keyEvents, true); + document.removeEventListener("keydown", PresBox.keyEventsWrapper, true); + this._presKeyEventsActive = false; + this.resetPresentation(); // Turn of progressivize editors this.turnOffEdit(true); + Object.values(this._disposers).forEach(disposer => disposer?.()); } - componentDidMount = async () => { + @action + componentDidMount() { this.rootDoc.presBox = this.rootDoc; this.rootDoc._forceRenderEngine = "timeline"; this.rootDoc._replacedChrome = "replaced"; this.layoutDoc.presStatus = PresStatus.Edit; this.layoutDoc._gridGap = 0; this.layoutDoc._yMargin = 0; + document.removeEventListener("keydown", PresBox.keyEventsWrapper, true); + document.addEventListener("keydown", PresBox.keyEventsWrapper, true); + this._presKeyEventsActive = true; this.turnOffEdit(true); DocListCastAsync((Doc.UserDoc().myPresentations as Doc).data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc)); + this._disposers.selection = reaction(() => SelectionManager.SelectedDocuments(), + views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation()); } - updateCurrentPresentation = () => { - Doc.UserDoc().activePresentation = this.rootDoc; + @action + updateCurrentPresentation = (pres?: Doc) => { + if (pres) Doc.UserDoc().activePresentation = pres; + else Doc.UserDoc().activePresentation = this.rootDoc; + document.removeEventListener("keydown", PresBox.keyEventsWrapper, true); + document.addEventListener("keydown", PresBox.keyEventsWrapper, true); PresBox.Instance = this; } /** * Called when the user moves to the next slide in the presentation trail. */ - @undoBatch @action next = () => { this.updateCurrentPresentation(); const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null); const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; - const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); - const currentFrame = Cast(targetDoc._currentFrame, "number", null); - const lastFrame = Cast(targetDoc.lastFrame, "number", null); - const curFrame = NumCast(targetDoc._currentFrame); + const currentFrame = Cast(targetDoc?._currentFrame, "number", null); + const lastFrame = Cast(targetDoc?.lastFrame, "number", null); + const curFrame = NumCast(targetDoc?._currentFrame); let internalFrames: boolean = false; if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.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) { + const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); targetDoc._viewTransition = "all 1s"; setTimeout(() => targetDoc._viewTransition = undefined, 1010); // targetDoc._currentFrame = curFrame + 1; @@ -176,7 +205,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> activeItem.playNow = false; // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide } else if (this.childDocs[this.itemIndex + 1] !== undefined) { - if (activeNext.presPinView) setTimeout(() => this.selectPres(), 0); + if (activeNext.presPinView || activeNext.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(), 0); else this.selectPres(); const nextSelected = this.itemIndex + 1; if (targetDoc.type === DocumentType.AUDIO) { if (AudioBox.Instance._ele) AudioBox.Instance.pause(); } @@ -188,9 +217,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> if (targetNext.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeNext.presStartTime)); // if (targetNext.type === DocumentType.VID) { VideoBox.Instance.Play() }; } else if (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) { activeNext.playNow = true; console.log('play next after it is navigated to'); } - this.gotoDocument(nextSelected, this.itemIndex); + this.gotoDocument(nextSelected); } else if (this.childDocs[this.itemIndex + 1] === undefined && this.layoutDoc.presLoop) { - this.gotoDocument(0, this.itemIndex); + this.gotoDocument(0); } } @@ -199,7 +228,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> * Design choice: If there are frames within the presentation, moving back will not * got back through the frames but instead directly to the next point in the presentation. */ - @undoBatch @action back = () => { this.updateCurrentPresentation(); @@ -209,22 +237,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null); const lastFrame = Cast(targetDoc.lastFrame, "number", null); const curFrame = NumCast(targetDoc._currentFrame); - if (prevItem.presPinView) setTimeout(() => this.selectPres(), 0); + if (prevItem.presPinView || prevTargetDoc === this.layoutDoc.presCollection) { setTimeout(() => this.updateCurrentPresentation(), 0); } else this.selectPres(); if (lastFrame !== undefined && curFrame >= 1) { this.prevKeyframe(targetDoc, activeItem); } else if (activeItem) { let prevSelected = this.itemIndex; prevSelected = Math.max(0, prevSelected - 1); - this.gotoDocument(prevSelected, this.itemIndex); + this.gotoDocument(prevSelected); if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame); } } //The function that is called when a document is clicked or reached through next or back. //it'll also execute the necessary actions if presentation is playing. - public gotoDocument = action((index: number, fromDoc: number) => { - this.updateCurrentPresentation(); + public gotoDocument = action((index: number) => { Doc.UnBrushAllDocs(); if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; @@ -240,7 +267,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010); } }); - } else { + } else if (presTargetDoc) { presTargetDoc && runInAction(() => { if (activeItem.presMovement === PresMovement.Jump) presTargetDoc.focusSpeed = 0; else presTargetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500; @@ -250,7 +277,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> if (presTargetDoc?.lastFrame !== undefined) { presTargetDoc._currentFrame = 0; } - this._selectedArray = [this.childDocs[index]]; //Update selected array + this._selectedArray.clear(); + this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array //Handles movement to element if (this.layoutDoc._viewType === "stacking") this.navigateToElement(this.childDocs[index]); this.onHideDocument(); //Handles hide after/before @@ -268,7 +296,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> navigateToElement = async (curDoc: Doc) => { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; - const srcContext = await DocCastAsync(targetDoc.context); + const srcContext = await DocCastAsync(targetDoc?.context); const presCollection = Cast(this.layoutDoc.presCollection, Doc, null); const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined; this.turnOffEdit(); @@ -277,40 +305,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> 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"); - // } - // } - this.updateCurrentPresentation(); const docToJump = curDoc; const willZoom = false; + const presStatus = this.rootDoc.presStatus; + const selViewCache = Array.from(this._selectedArray.keys()); + const dragViewCache = Array.from(this._dragArray); + const eleViewCache = Array.from(this._eleArray); + const self = this; + const resetSelection = action(() => { + const presDocView = DocumentManager.Instance.getDocumentView(self.rootDoc); + if (presDocView) SelectionManager.SelectDoc(presDocView, false); + self.rootDoc.presStatus = presStatus; + self._selectedArray.clear(); + selViewCache.forEach(doc => self._selectedArray.set(doc, undefined)); + self._dragArray.splice(0, self._dragArray.length, ...dragViewCache); + self._eleArray.splice(0, self._eleArray.length, ...eleViewCache); + }); const openInTab = () => { - collectionDocView ? collectionDocView.props.addDocTab(activeItem, "replace") : this.props.addDocTab(activeItem, "replace:left"); + collectionDocView ? collectionDocView.props.addDocTab(targetDoc, "") : this.props.addDocTab(targetDoc, ":left"); + this.layoutDoc.presCollection = targetDoc; + // this still needs some fixing + setTimeout(resetSelection, 500); }; // If openDocument is selected then it should open the document for the user if (activeItem.openDocument) { openInTab(); - } else + } else { //docToJump stayed same meaning, it was not in the group or was the last element in the group - if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== PresStatus.Edit) { - this.zoomProgressivizeNext(targetDoc); - } else if (docToJump === curDoc) { + // if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== PresStatus.Edit) { + // this.zoomProgressivizeNext(targetDoc); + // } else + if (docToJump === curDoc) { //checking if curDoc has navigation open if (curDoc.presMovement === PresMovement.Pan && targetDoc) { await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext); // documents open in new tab instead of on right } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) { //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext); // documents open in new tab instead of on right + await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, resetSelection); // documents open in new tab instead of on right } } else { //awaiting jump so that new scale can be found, since jumping is async targetDoc && await DocumentManager.Instance.jumpToDocument(targetDoc, willZoom, undefined, srcContext); } + } // After navigating to the document, if it is added as a presPinView then it will // adjust the pan and scale to that of the pinView when it was added. - // TODO: Add option to remove presPinView if (activeItem.presPinView) { // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document; @@ -319,6 +358,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> bestTarget._scrollY = activeItem.presPinViewScroll; } else if (bestTarget.type === DocumentType.COMPARISON) { bestTarget._clipWidth = activeItem.presPinClipWidth; + } else if (bestTarget.type === DocumentType.VID) { + bestTarget._currentTimecode = activeItem.presPinTimecode; } else { bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; bestTarget._panX = activeItem.presPinViewX; @@ -331,9 +372,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> // If website and has presWebsite data associated then on click it should // go back to that specific website // TODO: Add progressivize for navigating web (storing websites for given frames) - if (targetDoc.presWebsiteData) { - targetDoc.data = targetDoc.presWebsiteData; - } } /** @@ -381,7 +419,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } } - /** * For 'Hide Before' and 'Hide After' buttons making sure that * they are hidden each time the presentation is updated. @@ -392,17 +429,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null); if (tagDoc) tagDoc.opacity = 1; - if (curDoc.presHideTillShownButton) { + const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc); + const curInd: number = itemIndexes.indexOf(index); + if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) { } + else if (curDoc.presHideBefore) { if (index > this.itemIndex) { tagDoc.opacity = 0; - } else if (!curDoc.presHideAfterButton) { + } else if (!curDoc.presHideAfter) { tagDoc.opacity = 1; } } - if (curDoc.presHideAfterButton) { + if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== (itemIndexes.length - 1)) { } + else if (curDoc.presHideAfter) { if (index < this.itemIndex) { tagDoc.opacity = 0; - } else if (!curDoc.presHideTillShownButton) { + } else if (!curDoc.presHideBefore) { tagDoc.opacity = 1; } } @@ -412,7 +453,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc - @undoBatch @action startAutoPres = (startSlide: number) => { this.updateCurrentPresentation(); @@ -444,7 +484,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> }; this.layoutDoc.presStatus = PresStatus.Autoplay; this.startPresentation(startSlide); - this.gotoDocument(startSlide, this.itemIndex); + this.gotoDocument(startSlide); load(); } @@ -460,26 +500,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> //The function that resets the presentation by removing every action done by it. It also //stops the presentaton. resetPresentation = () => { - this.updateCurrentPresentation(); this.rootDoc._itemIndex = 0; + for (const doc of this.childDocs) Cast(doc.presentationTargetDoc, Doc, null).opacity = 1; } @action togglePath = (srcContext: Doc, off?: boolean) => { if (off) { - this.pathBoolean = false; + this._pathBoolean = false; srcContext.presPathView = false; } else { - this.pathBoolean = !this.pathBoolean; - srcContext.presPathView = this.pathBoolean; + runInAction(() => this._pathBoolean = !this._pathBoolean); + srcContext.presPathView = this._pathBoolean; } } - @undoBatch @action toggleExpandMode = () => { - this.rootDoc.expandBoolean = !this.rootDoc.expandBoolean; + runInAction(() => this._expandBoolean = !this._expandBoolean); + this.rootDoc.expandBoolean = this._expandBoolean; this.childDocs.forEach((doc) => { - if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true; - else if (!this.rootDoc.expandBoolean) doc.presExpandInlineButton = false; + doc.presExpandInlineButton = this._expandBoolean; }); } @@ -492,10 +531,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this.updateCurrentPresentation(); this.childDocs.map(doc => { const tagDoc = doc.presentationTargetDoc as Doc; - if (doc.presHideTillShownButton && this.childDocs.indexOf(doc) > startIndex) { + if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) { tagDoc.opacity = 0; } - if (doc.presHideAfterButton && this.childDocs.indexOf(doc) < startIndex) { + if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) { tagDoc.opacity = 0; } }); @@ -504,15 +543,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> /** * The method called to open the presentation as a minimized view */ - @undoBatch @action updateMinimize = () => { const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc); - if (this.layoutDoc.inOverlay) { + if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { this.layoutDoc.presStatus = PresStatus.Edit; Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc); CollectionDockingView.AddSplit(this.rootDoc, "right"); - this.layoutDoc.inOverlay = false; } else if (this.layoutDoc.context && docView) { this.layoutDoc.presStatus = PresStatus.Edit; clearTimeout(this._presTimer); @@ -540,7 +577,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> * Called when the user changes the view type * Either 'List' (stacking) or 'Slides' (carousel) */ - @undoBatch + // @undoBatch viewChanged = action((e: React.ChangeEvent) => { //@ts-ignore const viewType = e.target.selectedOptions[0].value as CollectionViewType; @@ -550,32 +587,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> if (viewType === CollectionViewType.Stacking) this.layoutDoc._gridGap = 0; }); - /** - * When the movement dropdown is changes - */ - @undoBatch - updateMovement = action((movement: any, activeItem: Doc, targetDoc: Doc) => { - switch (movement) { - case PresMovement.Zoom: //Pan and zoom - activeItem.presMovement = PresMovement.Zoom; - break; - case PresMovement.Pan: //Pan - activeItem.presMovement = PresMovement.Pan; - break; - case PresMovement.Jump: //Jump Cut - activeItem.presJump = true; - activeItem.presMovement = PresMovement.Jump; - break; - case PresMovement.None: default: - activeItem.presMovement = PresMovement.None; - break; - } - }); setMovementName = action((movement: any, activeItem: Doc): string => { let output: string = 'none'; switch (movement) { - case PresMovement.Zoom: output = 'Zoom'; break; //Pan and zoom + case PresMovement.Zoom: output = 'Pan & Zoom'; break; //Pan and zoom case PresMovement.Pan: output = 'Pan'; break; //Pan case PresMovement.Jump: output = 'Jump cut'; break; //Jump Cut case PresMovement.None: output = 'None'; break; //None @@ -589,23 +605,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> addDocumentFilter = (doc: Doc | Doc[]) => { const docs = doc instanceof Doc ? [doc] : doc; docs.forEach((doc, i) => { - if (this.childDocs.includes(doc)) { - if (docs.length === i + 1) return false; - } else if (doc.type === DocumentType.LABEL) { + if (doc.type === DocumentType.LABEL) { const audio = Cast(doc.annotationOn, Doc, null); if (audio) { - audio.aliasOf instanceof Doc; audio.presStartTime = NumCast(doc.audioStart); audio.presEndTime = NumCast(doc.audioEnd); audio.presDuration = NumCast(doc.audioEnd) - NumCast(doc.audioStart); TabDocView.PinDoc(audio, false, true); - setTimeout(() => this.removeDocument(doc), 1); + setTimeout(() => this.removeDocument(doc), 0); return false; } } else { - doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf); - !this.childDocs.includes(doc) && (doc.presMovement = PresMovement.Zoom); - if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true; + if (!doc.aliasOf) { + const original = Doc.MakeAlias(doc); + TabDocView.PinDoc(original); + setTimeout(() => this.removeDocument(doc), 0); + return false; + } else { + if (!doc.presentationTargetDoc) doc.title = doc.title + " - Slide"; + doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf); + doc.presMovement = PresMovement.Zoom; + if (this._expandBoolean) doc.presExpandInlineButton = true; + } } }); return true; @@ -622,41 +643,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> */ @action sortArray = (): Doc[] => { - return this.childDocs.filter(doc => this._selectedArray.includes(doc)); + return this.childDocs.filter(doc => this._selectedArray.has(doc)); } /** * Method to get the list of selected items in the order in which they have been selected */ @computed get listOfSelected() { - const list = this._selectedArray.map((doc: Doc, index: any) => { + const list = Array.from(this._selectedArray.keys()).map((doc: Doc, index: any) => { const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null); - if (tagDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>; + if (curDoc && curDoc === this.activeItem) return <div className="selectedList-items"><b>{index + 1}. {curDoc.title}</b></div> + else if (tagDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>; else if (curDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>; }); return list; } @action - selectPres = () => { - const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc)!; + selectPres = (pres?: Doc) => { + const presDocView = DocumentManager.Instance.getDocumentView(pres ? pres : this.rootDoc)!; SelectionManager.SelectDoc(presDocView, false); } //Regular click @action selectElement = (doc: Doc) => { - this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex)); - if (doc.presPinView) setTimeout(() => this.selectPres(), 0); - else this.selectPres(); + const context = Cast(doc.context, Doc, null); + this.gotoDocument(this.childDocs.indexOf(doc)); + if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0); + else this.updateCurrentPresentation(context); } //Command click @action multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { - if (!this._selectedArray.includes(doc)) { - this._selectedArray.push(doc); + if (!this._selectedArray.has(doc)) { + this._selectedArray.set(doc, undefined); this._eleArray.push(ref); this._dragArray.push(drag); } @@ -666,11 +689,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> //Shift click @action shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { - this._selectedArray = []; + this._selectedArray.clear(); // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); if (this.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._selectedArray.set(this.childDocs[i], undefined); this._eleArray.push(ref); this._dragArray.push(drag); } @@ -678,72 +701,102 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this.selectPres(); } + //regular click + @action + regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean) => { + this._selectedArray.clear(); + this._selectedArray.set(doc, undefined); + this._eleArray.splice(0, this._eleArray.length, ref); + this._dragArray.splice(0, this._dragArray.length, drag); + focus && this.selectElement(doc); + this.selectPres(); + } + + modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => { + if (cmdClick) this.multiSelect(doc, ref, drag); + else if (shiftClick) this.shiftSelect(doc, ref, drag); + else this.regularSelect(doc, ref, drag, focus); + } + + static keyEventsWrapper = (e: KeyboardEvent) => { + PresBox.Instance.keyEvents(e); + } + // Key for when the presentaiton is active - @undoBatch - keyEvents = action(async (e: KeyboardEvent) => { + @action + keyEvents = (e: KeyboardEvent) => { if (e.target instanceof HTMLInputElement) return; let handled = false; const anchorNode = document.activeElement as HTMLDivElement; if (anchorNode && anchorNode.className?.includes("lm_title")) return; - if (e.keyCode === 27) { // Escape key - if (this.layoutDoc.inOverlay) { this.updateMinimize(); } - else if (this.layoutDoc.presStatus === "edit") { this._selectedArray = []; this._eleArray = []; this._dragArray = []; } - else this.layoutDoc.presStatus = "edit"; - if (this._presTimer) clearTimeout(this._presTimer); - handled = true; - } if ((e.metaKey || e.altKey) && e.keyCode === 65) { // Ctrl-A to select all - if (this.layoutDoc.presStatus === "edit") { - this._selectedArray = this.childDocs; + switch (e.key) { + case "Backspace": + if (this.layoutDoc.presStatus === "edit") { + undoBatch(action(() => { + for (const doc of Array.from(this._selectedArray.keys())) { + this.removeDocument(doc); + } + this._selectedArray.clear(); + this._eleArray.length = 0; + this._dragArray.length = 0; + }))(); + handled = true; + } + break; + case "Escape": + if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); } + else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; } + else this.layoutDoc.presStatus = "edit"; + if (this._presTimer) clearTimeout(this._presTimer); handled = true; - } - } if (e.keyCode === 37 || e.keyCode === 38) { // left(37) / a(65) / up(38) to go back - this.back(); - if (this._presTimer) clearTimeout(this._presTimer); - handled = true; - } if (e.keyCode === 39 || e.keyCode === 40) { // right (39) / d(68) / down(40) to go to next - this.next(); - if (this._presTimer) clearTimeout(this._presTimer); - handled = true; - } if (e.keyCode === 32) { // spacebar to 'present' or autoplay - if (this.layoutDoc.presStatus === "manual") this.startAutoPres(this.itemIndex); - else if (this.layoutDoc.presStatus === "auto") if (this._presTimer) clearTimeout(this._presTimer); - handled = true; - } if (e.keyCode === 8) { // delete selected items - if (this.layoutDoc.presStatus === "edit") { - await Promise.all<boolean>(this._selectedArray.map((doc, i): boolean => { - const removed: boolean = this.removeDocument(doc); - console.log("Is removed? : " + i + " | " + removed); - return removed; - })); - action(() => this._selectedArray = []); - action(() => this._eleArray = []); - action(() => this._dragArray = []); + break; + case "Down": case "ArrowDown": + case "Right": case "ArrowRight": + if (this.itemIndex >= this.childDocs.length - 1) return; + if (e.shiftKey) { // TODO: update to work properly + this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1; + this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined); + } else { + this.next(); + if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + } handled = true; - } - } if (handled) { + break; + case "Up": case "ArrowUp": + case "Left": case "ArrowLeft": + if (this.itemIndex === 0) return; + if (e.shiftKey) { // TODO: update to work properly + this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1; + this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined); + } else { + this.back(); + if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + } + handled = true; + break; + case "Spacebar": case " ": + if (this.layoutDoc.presStatus === PresStatus.Manual) this.startAutoPres(this.itemIndex); + else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer); + handled = true; + break; + case "a": + if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === "edit") { + this._selectedArray.clear(); + this.childDocs.forEach(doc => this._selectedArray.set(doc, undefined)); + handled = true; + } + default: + break; + } + if (handled) { e.stopPropagation(); e.preventDefault(); - } if ((e.keyCode === 37 || e.keyCode === 38) && e.shiftKey) { // left(37) / a(65) / up(38) to go back - if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) { - const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]); - if ((index - 1) > 0) this._selectedArray.push(this.childDocs[index - 1]); - } - handled = true; - } if ((e.keyCode === 39 || e.keyCode === 40) && e.shiftKey) { // left(37) / a(65) / up(38) to go back - if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) { - const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]); - if ((index - 1) > 0) { - this._selectedArray.push(this.childDocs[index - 1]); - } - } - handled = true; } - }); + } /** * */ - @undoBatch @action viewPaths = () => { const srcContext = Cast(this.rootDoc.presCollection, Doc, null); @@ -752,26 +805,60 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } } + getAllIndexes = (arr: Doc[], val: Doc): number[] => { + var indexes = [], i; + for (i = 0; i < arr.length; i++) + if (arr[i] === val) + indexes.push(i); + return indexes; + } + // Adds the index in the pres path graphically @computed get order() { const order: JSX.Element[] = []; - this.childDocs.forEach((doc, index) => { + const docs: Doc[] = []; + this.childDocs.filter(doc => Cast(doc.presentationTargetDoc, Doc, null)).forEach((doc, index) => { const tagDoc = Cast(doc.presentationTargetDoc, Doc, null); - const srcContext = Cast(tagDoc?.context, Doc, null); + const srcContext = Cast(tagDoc.context, Doc, null); const width = NumCast(tagDoc._width) / 10; const height = Math.max(NumCast(tagDoc._height) / 10, 15); const edge = Math.max(width, height); const fontSize = edge * 0.8; - // Case A: Document is contained within the colleciton + const gap = 2; + // Case A: Document is contained within the collection if (this.rootDoc.presCollection === srcContext) { + if (docs.includes(tagDoc)) { + const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length; + docs.push(tagDoc); + order.push( + <div className="pathOrder" + key={tagDoc.id + 'pres' + index} + style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - (edge / 2)), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }} + onClick={() => this.selectElement(doc)}> + <div className="pathOrder-frame">{index + 1}</div> + </div>); + } else { + docs.push(tagDoc); + order.push( + <div className="pathOrder" + key={tagDoc.id + 'pres' + index} + style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }} + onClick={() => this.selectElement(doc)}> + <div className="pathOrder-frame">{index + 1}</div> + </div>); + } + // Case B: Document is presPinView and is presCollection + } else if (doc.pinWithView && this.layoutDoc.presCollection === tagDoc) { + docs.push(tagDoc); order.push( - <div className="pathOrder" style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}> + <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top: 0, left: 0 }}> <div className="pathOrder-frame">{index + 1}</div> </div>); - // Case B: Document is not inside of the collection + // Case C: Document is not contained within presCollection } else { + docs.push(tagDoc); order.push( - <div className="pathOrder" style={{ top: 0, left: 0 }}> + <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ position: 'absolute', top: 0, left: 0 }}> <div className="pathOrder-frame">{index + 1}</div> </div>); } @@ -818,49 +905,102 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> />); } - /** - * 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: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - 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; - } - } - } - // Converts seconds to ms and updates presTransition - @undoBatch setTransitionTime = (number: String, change?: number) => { let timeInMS = Number(number) * 1000; if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; if (timeInMS > 10000) timeInMS = 10000; - if (this.activeItem) this.activeItem.presTransition = timeInMS; + Array.from(this._selectedArray.keys()).forEach((doc) => doc.presTransition = timeInMS); } // Converts seconds to ms and updates presDuration - @undoBatch setDurationTime = (number: String, change?: number) => { let timeInMS = Number(number) * 1000; if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; if (timeInMS > 20000) timeInMS = 20000; - if (this.activeItem) this.activeItem.presDuration = timeInMS; + Array.from(this._selectedArray.keys()).forEach((doc) => doc.presDuration = timeInMS); + } + + /** + * When the movement dropdown is changes + */ + @undoBatch + updateMovement = action((movement: any, all?: boolean) => { + const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys()); + array.forEach((doc) => { + switch (movement) { + case PresMovement.Zoom: //Pan and zoom + doc.presMovement = PresMovement.Zoom; + break; + case PresMovement.Pan: //Pan + doc.presMovement = PresMovement.Pan; + break; + case PresMovement.Jump: //Jump Cut + doc.presJump = true; + doc.presMovement = PresMovement.Jump; + break; + case PresMovement.None: default: + doc.presMovement = PresMovement.None; + break; + } + }) + }); + + @undoBatch + @action + updateHideBefore = (activeItem: Doc) => { + activeItem.presHideBefore = !activeItem.presHideBefore; + Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideBefore = activeItem.presHideBefore); + } + + @undoBatch + @action + updateHideAfter = (activeItem: Doc) => { + activeItem.presHideAfter = !activeItem.presHideAfter; + Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideAfter = activeItem.presHideAfter); + } + + @undoBatch + @action + updateOpenDoc = (activeItem: Doc) => { + activeItem.openDocument = !activeItem.openDocument; + Array.from(this._selectedArray.keys()).forEach((doc) => { + doc.openDocument = activeItem.openDocument; + }); + } + + @undoBatch + @action + updateEffect = (effect: any, all?: boolean) => { + const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys()); + array.forEach((doc) => { + const tagDoc = Cast(doc.presentationTargetDoc, Doc, null); + switch (effect) { + case PresEffect.Bounce: + tagDoc.presEffect = PresEffect.Bounce; + break; + case PresEffect.Fade: + tagDoc.presEffect = PresEffect.Fade; + break; + case PresEffect.Flip: + tagDoc.presEffect = PresEffect.Flip; + break; + case PresEffect.Roll: + tagDoc.presEffect = PresEffect.Roll; + break; + case PresEffect.Rotate: + tagDoc.presEffect = PresEffect.Rotate; + break; + case PresEffect.None: default: + tagDoc.presEffect = PresEffect.None; + break; + } + }); } + _batch: UndoManager.Batch | undefined = undefined; @computed get transitionDropdown() { const activeItem: Doc = this.activeItem; @@ -879,10 +1019,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> {this.setMovementName(activeItem.presMovement, activeItem)} <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}> - <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None, activeItem, targetDoc)}>None</div> - <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom, activeItem, targetDoc)}>Pan and Zoom</div> - <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan, activeItem, targetDoc)}>Pan</div> - <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump, activeItem, targetDoc)}>Jump cut</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>Pan</div> + <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>Jump cut</div> </div> </div> <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}> @@ -893,15 +1033,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> onChange={action((e) => this.setTransitionTime(e.target.value))} /> s </div> <div className="ribbon-propertyUpDown"> - <div className="ribbon-propertyUpDownItem" onClick={() => this.setTransitionTime(String(transitionSpeed), 1000)}> + <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), 1000))}> <FontAwesomeIcon icon={"caret-up"} /> </div> - <div className="ribbon-propertyUpDownItem" onClick={() => this.setTransitionTime(String(transitionSpeed), -1000)}> + <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), -1000))}> <FontAwesomeIcon icon={"caret-down"} /> </div> </div> </div> - <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} /> + <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} + className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`} + id="toolbar-slider" + onPointerDown={() => this._batch = UndoManager.StartBatch("presTransition")} + onPointerUp={() => this._batch?.end()} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { + e.stopPropagation(); + this.setTransitionTime(e.target.value); + }} /> <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}> <div className="slider-text">Fast</div> <div className="slider-text">Medium</div> @@ -911,28 +1059,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="ribbon-box"> Visibility {"&"} Duration <div className="ribbon-doubleButton"> - <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideTillShownButton ? "active" : ""}`} onClick={undoBatch(() => activeItem.presHideTillShownButton = !activeItem.presHideTillShownButton)}>Hide before</div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfterButton ? "active" : ""}`} onClick={undoBatch(() => activeItem.presHideAfterButton = !activeItem.presHideAfterButton)}>Hide after</div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={undoBatch(() => activeItem.openDocument = !activeItem.openDocument)}>Open</div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Open</div></Tooltip> </div> <div className="ribbon-doubleButton" > <div className="presBox-subheading">Slide Duration</div> <div className="ribbon-property"> <input className="presBox-input" type="number" value={duration} - // onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)} onChange={action((e) => this.setDurationTime(e.target.value))} /> s </div> <div className="ribbon-propertyUpDown"> - <div className="ribbon-propertyUpDownItem" onClick={() => this.setDurationTime(String(duration), 1000)}> + <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}> <FontAwesomeIcon icon={"caret-up"} /> </div> - <div className="ribbon-propertyUpDownItem" onClick={() => this.setDurationTime(String(duration), -1000)}> + <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}> <FontAwesomeIcon icon={"caret-down"} /> </div> </div> </div> - <input type="range" step="0.1" min="0.1" max="20" value={duration} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }} className={"toolbar-slider"} id="duration-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }} /> + <input type="range" step="0.1" min="0.1" max="20" value={duration} + style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }} + className={"toolbar-slider"} id="duration-slider" + onPointerDown={() => { this._batch = UndoManager.StartBatch("presDuration"); }} + onPointerUp={() => { if (this._batch) this._batch.end(); }} + onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }} + /> <div className={"slider-headers"} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "grid" }}> <div className="slider-text">Short</div> <div className="slider-text">Medium</div> @@ -945,12 +1098,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> {effect} <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}> - <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'None')}>None</div> - <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Fade')}>Fade In</div> - <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Flip')}>Flip</div> - <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Rotate')}>Rotate</div> - <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Bounce')}>Bounce</div> - <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Roll')}>Roll</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.None)}>None</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>Fade In</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>Flip</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>Rotate</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>Bounce</div> + <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>Roll</div> </div> </div> <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? "none" : "inline-flex" }}> @@ -968,9 +1121,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </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> @@ -997,19 +1147,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> applyTo = (array: Doc[]) => { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; + this.updateMovement(activeItem.presMovement, true); + this.updateEffect(targetDoc.presEffect, true); array.forEach((doc) => { const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null); if (tagDoc && targetDoc) { curDoc.presTransition = activeItem.presTransition; curDoc.presDuration = activeItem.presDuration; - tagDoc.presEffect = targetDoc.presEffect; tagDoc.presEffectDirection = targetDoc.presEffectDirection; - tagDoc.presMovement = targetDoc.presMovement; - curDoc.presMovement = activeItem.presMovement; - this.updateMovement(activeItem.presMovement, curDoc, tagDoc); - curDoc.presHideTillShownButton = activeItem.presHideTillShownButton; - curDoc.presHideAfterButton = activeItem.presHideAfterButton; + curDoc.presHideBefore = activeItem.presHideBefore; + curDoc.presHideAfter = activeItem.presHideAfter; } }); } @@ -1022,12 +1170,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <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-doubleButton" style={{ display: targetDoc.type === DocumentType.AUDIO ? "inline-flex" : "none" }}> <div className="ribbon-toggle" style={{ backgroundColor: activeItem.playAuto ? "#aedef8" : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div> <div className="ribbon-toggle" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : "#aedef8" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div> </div> - {targetDoc.type === DocumentType.VID ? <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presVidFullScreen ? "#aedef8" : "" }} onClick={() => activeItem.presVidFullScreen = !activeItem.presVidFullScreen}>Full screen</div> : (null)} - {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> + {/* {targetDoc.type === DocumentType.VID ? <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presVidFullScreen ? "#aedef8" : "" }} onClick={() => activeItem.presVidFullScreen = !activeItem.presVidFullScreen}>Full screen</div> : (null)} */} + {targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> <div className="presBox-subheading">Start time</div> <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> <input className="presBox-input" @@ -1036,7 +1184,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })} /> </div> </div> : (null)} - {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> + {targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> <div className="presBox-subheading">End time</div> <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> <input className="presBox-input" @@ -1045,13 +1193,45 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presEndTime = Number(val); })} /> </div> </div> : (null)} - {targetDoc.type === DocumentType.COL ? 'Presentation Pin View' : (null)} - <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? "#aedef8" : "" }} + {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)} + <div className="ribbon-doubleButton"> + <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? PresColors.LightBlue : "" }} onClick={() => { activeItem.presPinView = !activeItem.presPinView; targetDoc.presPinView = activeItem.presPinView; if (activeItem.presPinView) { + if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) { + const scroll = targetDoc._scrollTop; + activeItem.presPinView = true; + activeItem.presPinViewScroll = scroll; + } else if (targetDoc.type === DocumentType.VID) { + activeItem.presPinTimecode = targetDoc._currentTimecode; + } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) { + const x = targetDoc._panX; + const y = targetDoc._panY; + const scale = targetDoc._viewScale; + activeItem.presPinView = true; + activeItem.presPinViewX = x; + activeItem.presPinViewY = y; + activeItem.presPinViewScale = scale; + } else if (targetDoc.type === DocumentType.COMPARISON) { + const width = targetDoc._clipWidth; + activeItem.presPinClipWidth = width; + activeItem.presPinView = true; + } + } + }}>{presPinWithViewIcon}</div></Tooltip> + {activeItem.presPinView ? <Tooltip title={<><div className="dash-tooltip">{"Update the pinned view with the view of the selected document"}</div></>}><div className="ribbon-button" + onClick={() => { + if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) { + const scroll = targetDoc._scrollTop; + activeItem.presPinViewScroll = scroll; + } else if (targetDoc.type === DocumentType.VID) { + activeItem.presPinTimecode = targetDoc._currentTimecode; + } else if (targetDoc.type === DocumentType.COMPARISON) { + const clipWidth = targetDoc._clipWidth; + activeItem.presPinClipWidth = clipWidth; + } else { const x = targetDoc._panX; const y = targetDoc._panY; const scale = targetDoc._viewScale; @@ -1059,18 +1239,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> activeItem.presPinViewY = y; activeItem.presPinViewScale = scale; } - }}>{presPinWithViewIcon}</div> - {activeItem.presPinView ? <div className="ribbon-button" - onClick={() => { - const x = targetDoc._panX; - const y = targetDoc._panY; - const scale = targetDoc._viewScale; - activeItem.presPinViewX = x; - activeItem.presPinViewY = y; - activeItem.presPinViewScale = scale; - }}>Update</div> : (null)} + }}>Update</div></Tooltip> : (null)} </div> - <div style={{ display: activeItem.presPinView ? "block" : "none" }}> + {this.panable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}> <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> <div className="presBox-subheading">Pan X</div> <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> @@ -1098,7 +1269,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} /> </div> </div> - </div> + </div> : (null)} + {this.scrollable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}> + <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> + <div className="presBox-subheading">Scroll</div> + <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}> + <input className="presBox-input" + style={{ textAlign: 'left', width: 50 }} + type="number" value={NumCast(activeItem.presPinViewScroll)} + onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScroll = Number(val); })} /> + </div> + </div> + </div> : (null)} {/* <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}> <div className="ribbon-toggle" onClick={this.progressivizeText}>Store original website</div> </div> */} @@ -1205,7 +1387,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> if (data && presData) { data.push(doc); TabDocView.PinDoc(doc, false); - this.gotoDocument(this.childDocs.length, this.itemIndex); + this.gotoDocument(this.childDocs.length); } else { this.props.addDocTab(doc, "add:right"); } @@ -1255,10 +1437,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @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={(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}> + <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}> Minimize </div> - <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}> + <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}> Sidebar view </div> </div> @@ -1266,7 +1448,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> } // Case in which the document has keyframes to navigate to next key frame - @undoBatch @action nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => { const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]); @@ -1282,7 +1463,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame)); } - @undoBatch @action prevKeyframe = (tagDoc: Doc, actItem: Doc): void => { const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]); @@ -1604,7 +1784,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> return tags; } - @undoBatch @action nextAppearFrame = (doc: Doc, i: number): void => { // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); @@ -1617,7 +1796,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> 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); @@ -1678,30 +1856,43 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left"; const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel"; const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; + const isMini: boolean = this.toolbarWidth <= 100; + const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation); return (mode === CollectionViewType.Carousel3D) ? (null) : ( - <div id="toolbarContainer" className={'presBox-toolbar'} style={{ display: this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }}> + <div id="toolbarContainer" className={'presBox-toolbar'}> {/* <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> */} <Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}> - <div style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3 }} className={`toolbar-button ${this.pathBoolean ? "active" : ""}`} onClick={this.childDocs.length > 1 ? this.viewPaths : undefined}> + <div style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3, color: this._pathBoolean ? PresColors.DarkBlue : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 ? this.viewPaths : undefined}> <FontAwesomeIcon icon={"exchange-alt"} /> </div> </Tooltip> - <div className="toolbar-divider" /> - <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.expandBoolean ? "Minimize all" : "Expand all"}</div></>}> - <div className={`toolbar-button ${this.rootDoc.expandBoolean ? "active" : ""}`} onClick={this.toggleExpandMode}> - {/* <FontAwesomeIcon icon={this.rootDoc.expandBoolean ? "eye-slash" : "eye"} /> */} - <FontAwesomeIcon icon={"eye"} /> - </div> - </Tooltip> - <div className="toolbar-divider" /> - <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}> - <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}> - <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? '#AEDDF8' : 'white' }} /> - </div> - </Tooltip> + {isMini ? (null) : + <> + <div className="toolbar-divider" /> + <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}> + <div className={"toolbar-button"} + style={{ color: this._expandBoolean ? PresColors.DarkBlue : 'white' }} + onClick={this.toggleExpandMode}> + {/* <FontAwesomeIcon icon={this.rootDoc.expandBoolean ? "eye-slash" : "eye"} /> */} + <FontAwesomeIcon icon={"eye"} /> + </div> + </Tooltip> + <div className="toolbar-divider" /> + <Tooltip title={<><div className="dash-tooltip">{presKeyEvents ? "Keys are active" : "Keys are not active - click anywhere on the presentation trail to activate keys"}</div></>}> + <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? PresColors.DarkBlue : 'white' }} /> + </div> + </Tooltip> + <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}> + <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? PresColors.DarkBlue : 'white' }} /> + </div> + </Tooltip> + </> + } </div> ); } @@ -1713,25 +1904,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> */ @computed get topPanel() { const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; + const isMini: boolean = this.toolbarWidth <= 100; return ( <div className="presBox-buttons" style={{ display: this.rootDoc._chromeStatus === "disabled" ? "none" : undefined }}> - <select className="presBox-viewPicker" + {isMini ? (null) : <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === "edit" ? "block" : "none" }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}> <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option> <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option> - </select> - <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length > 0 ? 1 : 0.3 }}> + </select>} + <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}> <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}> - <div className="presBox-button-left" onClick={() => (this.childDocs.length > 0) && (this.layoutDoc.presStatus = "manual")}> + <div className="presBox-button-left" onClick={undoBatch(() => (this.childDocs.length) && (this.layoutDoc.presStatus = "manual"))}> <FontAwesomeIcon icon={"play-circle"} /> <div style={{ display: this.props.PanelWidth() > 200 ? "inline-flex" : "none" }}> Present</div> </div> - {(mode === CollectionViewType.Carousel3D) ? (null) : <div className={`presBox-button-right ${this.presentTools ? "active" : ""}`} + {(mode === CollectionViewType.Carousel3D || isMini) ? (null) : <div className={`presBox-button-right ${this.presentTools ? "active" : ""}`} onClick={(action(() => { - if (this.childDocs.length > 0) this.presentTools = !this.presentTools; + if (this.childDocs.length) this.presentTools = !this.presentTools; }))}> <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"angle-down"} /> {this.presentDropdown} @@ -1825,19 +2017,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @computed get playButtons() { // Case 1: There are still other frames and should go through all frames before going to next slide return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}> - <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColors.DarkBlue : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> <div className="presPanel-divider"></div> - <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div> - <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip> - <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div> + <div className="presPanel-button" onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div> + <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? "pause" : "play"} /></div></Tooltip> + <div className="presPanel-button" onClick={() => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-right"} /></div> <div className="presPanel-divider"></div> <div className="presPanel-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}> Slide {this.itemIndex + 1} / {this.childDocs.length} {this.playButtonFrames} </div> <div className="presPanel-divider"></div> - {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div> - : <div className="presPanel-button" onClick={() => this.layoutDoc.presStatus = "edit"}> + {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }))}>EXIT</div> + : <div className="presPanel-button" onClick={undoBatch(action(() => this.layoutDoc.presStatus = "edit"))}> <FontAwesomeIcon icon={"times"} /> </div>} </div>); @@ -1845,7 +2037,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> @action startOrPause = () => { - if (this.layoutDoc.presStatus === "manual" || this.layoutDoc.presStatus === "edit") this.startAutoPres(this.itemIndex); + if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex); else this.pauseAutoPres(); } @@ -1855,25 +2047,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> // needed to ensure that the childDocs are loaded for looking up fields this.childDocs.slice(); const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; - return this.layoutDoc.inOverlay ? + const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation); + return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ? <div className="miniPres"> - <div className="presPanelOverlay" style={{ display: "inline-flex", height: 35, background: '#323232', top: 0, zIndex: 3000000 }}> - <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> + <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + PresColors.DarkBlue : undefined }}> + <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColors.DarkBlue : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> <div className="presPanel-divider"></div> - <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div> - <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip> - <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div> + <div className="presPanel-button" onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div> + <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip> + <div className="presPanel-button" onClick={() => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-right"} /></div> <div className="presPanel-divider"></div> <div className="presPanel-button-text"> Slide {this.itemIndex + 1} / {this.childDocs.length} {this.playButtonFrames} </div> <div className="presPanel-divider"></div> - <div className="presPanel-button-text" onClick={() => { this.updateMinimize(); this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div> + <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.updateMinimize(); this.layoutDoc.presStatus = PresStatus.Edit; clearTimeout(this._presTimer); }))}>EXIT</div> </div> </div> : - <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} > + <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.OverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} > {this.topPanel} {this.toolbar} {this.newDocumentToolbarDropdown} @@ -1900,7 +2093,7 @@ 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 ? 30 : 26; + if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 35 : 31; if (field === 'presStatus') return container.presStatus; if (field === '_itemIndex') return container._itemIndex; if (field === 'presBox') return container; diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index 0c0854ac2..05714f665 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,5 +1,8 @@ .videoBox { transform-origin: top left; + width: 100%; + height: 100%; + position: relative; .videoBox-viewer { opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 168c87764..250595db5 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -20,6 +20,7 @@ import "./VideoBox.scss"; import { documentSchema } from "../../../fields/documentSchemas"; import { Networking } from "../../Network"; import { SnappingManager } from "../../util/SnappingManager"; +import { SelectionManager } from "../../util/SelectionManager"; const path = require('path'); export const timeSchema = createSchema({ @@ -75,6 +76,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD update && this.player?.pause(); update && this._youtubePlayer?.pauseVideo(); this._youtubePlayer && this._playTimer && clearInterval(this._playTimer); + this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true); + this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused. this._playTimer = undefined; this.updateTimecode(); } @@ -346,6 +349,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : ""); const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0))); return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`} + onPointerLeave={this.updateTimecode} onLoad={this.youtubeIframeLoaded} className={`${style}`} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390} src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />; } @@ -358,12 +362,20 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD return this.addDocument(doc); } + @computed get contentScaling() { return this.props.ContentScaling(); } contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content]; render() { return (<div className="videoBox" onContextMenu={this.specificContextMenu} - style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} > + style={{ + transform: this.props.PanelWidth() ? undefined : `scale(${this.contentScaling})`, + width: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`, + height: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`, + pointerEvents: this.layoutDoc._isBackground ? "none" : undefined, + borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.contentScaling}px` + }} > <div className="videoBox-viewer" > <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + forceScaling={true} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth} annotationsKey={this.annotationKey} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index eb9e08e20..de5546fa9 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -72,14 +72,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum constructor(props: any) { super(props); - Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850); - Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850); + if (this.dataDoc[this.fieldKey] instanceof WebField) { + Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850); + Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850); + } } iframeLoaded = action((e: any) => { const iframe = this._iframeRef.current; if (iframe && iframe.contentDocument) { iframe.setAttribute("enable-annotation", "true"); + iframe.contentDocument.addEventListener("click", undoBatch(action(e => { + const href = e.target?.href; + if (href) { + this._url = href.replace(Utils.prepend(""), Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.origin); + this.submitURL(); + } + }))); iframe.contentDocument.addEventListener('pointerdown', this.iframedown, false); iframe.contentDocument.addEventListener('scroll', this.iframeScrolled, false); this.layoutDoc.scrollHeight = iframe.contentDocument.children?.[0].scrollHeight || 1000; @@ -178,14 +187,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum this._iframeRef.current?.contentDocument?.removeEventListener('scroll', this.iframeScrolled); } - @action - onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => { - this._url = e.target.value; - } - onUrlDragover = (e: React.DragEvent) => { e.preventDefault(); } + + @undoBatch @action onUrlDrop = (e: React.DragEvent) => { const { dataTransfer } = e; |
