diff options
Diffstat (limited to 'src/client/views/nodes/trails')
| -rw-r--r-- | src/client/views/nodes/trails/PresBox.scss | 26 | ||||
| -rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 1443 | ||||
| -rw-r--r-- | src/client/views/nodes/trails/PresElementBox.tsx | 17 | ||||
| -rw-r--r-- | src/client/views/nodes/trails/PresEnums.ts | 5 |
4 files changed, 558 insertions, 933 deletions
diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 4d60a02f1..fd202590e 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -938,14 +938,19 @@ min-width: 15px; max-width: 100px; left: 8px; + margin: auto; + margin-left: unset; + height: 100%; } .presBox-presentPanel { display: flex; justify-self: end; width: 100%; - max-width: 300px; - min-width: 150px; + margin: auto; + margin-right: unset; + height: 100%; + position: relative; } select { @@ -955,7 +960,7 @@ .presBox-button { cursor: pointer; - height: 25px; + //height: 100%; border-radius: 5px; display: none; justify-content: center; @@ -986,6 +991,9 @@ width: max-content; position: absolute; right: 10px; + margin: auto; + margin-right: unset; + height: 100%; .present-icon { margin-right: 7px; @@ -999,9 +1007,16 @@ position: relative; display: inline-block; left: 8px; + margin: auto; + margin-left: unset; } } +.presBox-buttons.inOverlay { + padding-top: unset; + padding-bottom: unset; +} + .presBox-backward, .presBox-forward { width: 25px; @@ -1052,6 +1067,8 @@ font-size: 30px; position: absolute; min-width: 50px; + margin: auto; + margin-left: unset; } } @@ -1087,13 +1104,12 @@ color: $white; border-radius: 5px; grid-template-rows: 100%; - height: 25; + height: 100%; width: max-content; min-width: max-content; justify-content: space-evenly; align-items: center; display: flex; - position: absolute; transition: all 0.2s; .presPanel-button-text { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 4e8aed8a6..855a7f171 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1,18 +1,18 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableSet, observe, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; -import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; -import { Doc, DocListCast, FieldResult, StrListCast } from '../../../../fields/Doc'; +import { AnimationSym, Doc, DocListCast, FieldResult, HighlightSym, Opt, StrListCast } from '../../../../fields/Doc'; import { Copy, Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; import { ObjectField } from '../../../../fields/ObjectField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents, StopEvent } from '../../../../Utils'; +import { AudioField } from '../../../../fields/URLField'; +import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -22,25 +22,40 @@ import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; -import { CollectionFreeFormView, MarqueeViewBounds } from '../../collections/collectionFreeForm'; +import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from '../../collections/collectionFreeForm'; import { CollectionView } from '../../collections/CollectionView'; import { TabDocView } from '../../collections/TabDocView'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView'; +import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; -import { PresEffect, PresMovement, PresStatus } from './PresEnums'; +import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; +import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline'; +const { Howl } = require('howler'); export interface PinProps { audioRange?: boolean; activeFrame?: number; + currentFrame?: number; hidePresBox?: boolean; pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected) pinDocLayout?: boolean; // pin layout info (width/height/x/y) pinDocContent?: boolean; // pin data info (scroll/pan/zoom/text) + pinAudioPlay?: boolean; // pin audio annotation + pinData?: { + scrollable?: boolean | undefined; + pannable?: boolean | undefined; + temporal?: boolean | undefined; + clippable?: boolean | undefined; + dataview?: boolean | undefined; + textview?: boolean | undefined; + poslayoutview?: boolean | undefined; + dataannos?: boolean | undefined; + }; } @observer @@ -49,39 +64,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { return FieldView.LayoutString(PresBox, fieldKey); } - /** - * returns an entrance animation effect function to wrap a JSX element - * @param presEffectDoc presentation effects document that specifies the animation effect parameters - * @returns a function that will wrap a JSX animation element wrapping any JSX element - */ - public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Doc) { - const effectProps = { - left: presEffectDoc.presEffectDirection === PresEffect.Left, - right: presEffectDoc.presEffectDirection === PresEffect.Right, - top: presEffectDoc.presEffectDirection === PresEffect.Top, - bottom: presEffectDoc.presEffectDirection === PresEffect.Bottom, - opposite: true, - delay: NumCast(presEffectDoc.presTransition), - }; - //prettier-ignore - switch (StrCast(presEffectDoc.presEffect)) { - default: - case PresEffect.None: return renderDoc; - case PresEffect.Zoom: return <Zoom {...effectProps}>{renderDoc}</Zoom>; - case PresEffect.Fade: return <Fade {...effectProps}>{renderDoc}</Fade>; - case PresEffect.Flip: return <Flip {...effectProps}>{renderDoc}</Flip>; - case PresEffect.Rotate: return <Rotate {...effectProps}>{renderDoc}</Rotate>; - case PresEffect.Bounce: return <Bounce {...effectProps}>{renderDoc}</Bounce>; - case PresEffect.Roll: return <Roll {...effectProps}>{renderDoc}</Roll>; - case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>; - } - } - private _disposers: { [name: string]: IReactionDisposer } = {}; public selectedArray = new ObservableSet<Doc>(); @observable public static Instance: PresBox; - @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView @observable _isChildActive = false; @observable _moveOnFromAudio: boolean = true; @@ -93,7 +79,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @observable _expandBoolean: boolean = false; @observable _transitionTools: boolean = false; @observable _newDocumentTools: boolean = false; - @observable _progressivizeTools: boolean = false; @observable _openMovementDropdown: boolean = false; @observable _openEffectDropdown: boolean = false; @observable _presentTools: boolean = false; @@ -125,7 +110,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); } @computed get scrollable() { - if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true; + if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._viewType === CollectionViewType.Stacking) return true; return false; } @computed get panable() { @@ -151,6 +136,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @action componentWillUnmount() { this._unmounting = true; + if (this._presTimer) clearTimeout(this._presTimer); document.removeEventListener('keydown', PresBox.keyEventsWrapper, true); this.resetPresentation(); // Turn of progressivize editors @@ -158,7 +144,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { Object.values(this._disposers).forEach(disposer => disposer?.()); } - @action componentDidMount() { this._disposers.keyboard = reaction( () => this.selectedDoc, @@ -182,10 +167,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { ); this.props.setContentView?.(this); this._unmounting = false; - this.rootDoc._forceRenderEngine = 'timeline'; - this.layoutDoc.presStatus = PresStatus.Edit; - this.layoutDoc._gridGap = 0; - this.layoutDoc._yMargin = 0; + if (this.props.renderDepth > 0) { + runInAction(() => { + this.rootDoc._forceRenderEngine = computeTimelineLayout.name; + this.layoutDoc._gridGap = 0; + this.layoutDoc._yMargin = 0; + }); + } this.turnOffEdit(true); this._disposers.selection = reaction( () => SelectionManager.Views(), @@ -199,17 +187,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { PresBox.Instance = this; }; - // There are still other internal frames and should go through all frames before going to next slide - nextInternalFrame = (targetDoc: Doc, activeItem: Doc) => { - const currentFrame = Cast(targetDoc?._currentFrame, 'number', null); - const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); - targetDoc._viewTransition = 'all 1s'; - setTimeout(() => (targetDoc._viewTransition = undefined), 1010); - this.nextKeyframe(targetDoc, activeItem); - if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc); - else targetDoc.keyFrameEditing = true; - }; - _mediaTimer!: [NodeJS.Timeout, Doc]; // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played startTempMedia = (targetDoc: Doc, activeItem: Doc) => { @@ -232,38 +209,53 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions // No more frames in current doc and next slide is defined, therefore move to next slide nextSlide = (slideNum?: number) => { - let nextSelected = slideNum ?? this.itemIndex + 1; - this.gotoDocument(nextSelected, this.activeItem); - for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) { - if (this.childDocs[nextSelected].groupWithUp) { - this.gotoDocument(nextSelected, this.activeItem, true); - } else { - break; + const nextSlideInd = slideNum ?? this.itemIndex + 1; + let curSlideInd = nextSlideInd; + const resetSelection = action(() => { + this.clearSelectedArray(); + for (let i = nextSlideInd; i <= curSlideInd; i++) { + this.addToSelectedArray(this.childDocs[i]); } - } + }); + CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.()); + this.clearSelectedArray(); + const doGroupWithUp = + (nextSelected: number, force = false) => + () => { + if (nextSelected < this.childDocs.length) { + if (force || this.childDocs[nextSelected].groupWithUp) { + const serial = nextSelected + 1 < this.childDocs.length && NumCast(this.childDocs[nextSelected + 1].groupWithUp) > 1; + if (serial) { + this.gotoDocument(nextSelected, this.activeItem, true, async () => { + const waitTime = NumCast(this.activeItem.presDuration) - NumCast(this.activeItem.presTransition); + await new Promise<void>(res => setTimeout(() => res(), Math.max(0, waitTime))); + doGroupWithUp(nextSelected + 1)(); + }); + } else { + this.gotoDocument(nextSelected, this.activeItem, undefined, resetSelection); + curSlideInd = this.itemIndex; + doGroupWithUp(nextSelected + 1)(); + } + } + } + }; + doGroupWithUp(curSlideInd, true)(); }; // Called when the user activates 'next' - to move to the next part of the pres. trail @action next = () => { - const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null); - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - 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; - if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) { - // Case 1: There are still other frames and should go through all frames before going to next slide - this.nextInternalFrame(targetDoc, activeItem); - } else if (this.childDocs[this.itemIndex + 1] !== undefined) { - // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide + if (this.childDocs[this.itemIndex + 1] !== undefined) { + // Case 1: No more frames in current doc and next slide is defined, therefore move to next slide const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]); const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex; - this.nextSlide(curLast + 1); - } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) { - // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode - this.nextSlide(0); + this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1); + } else { + if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) { + // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode + this.nextSlide(0); + } + return 0; } return this.itemIndex; }; @@ -272,29 +264,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @action back = () => { const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null); - const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null); - const lastFrame = Cast(targetDoc.lastFrame, 'number', null); - const curFrame = NumCast(targetDoc._currentFrame); let prevSelected = this.itemIndex; // Functionality for group with up let didZoom = activeItem.presMovement; for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].groupWithUp; prevSelected--) { didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presMovement : didZoom; } - if (lastFrame !== undefined && curFrame >= 1) { - // Case 1: There are still other frames and should go through all frames before going to previous slide - this.prevKeyframe(targetDoc, activeItem); - } else if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) { + if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) { // Case 2: There are no other frames so it should go to the previous slide prevSelected = Math.max(0, prevSelected - 1); this.nextSlide(prevSelected); this.rootDoc._itemIndex = prevSelected; - if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame); } else if (this.childDocs[this.itemIndex - 1] === undefined && this.layoutDoc.presLoop) { // Case 3: Pres loop is on so it should go to the last slide - this.gotoDocument(this.childDocs.length - 1, activeItem); + this.nextSlide(this.childDocs.length - 1); } return this.itemIndex; }; @@ -302,21 +286,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //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. @undoBatch - public gotoDocument = action((index: number, from?: Doc, group?: boolean) => { + public gotoDocument = action((index: number, from?: Doc, group?: boolean, finished?: () => void) => { Doc.UnBrushAllDocs(); - if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; - if (activeItem.presActiveFrame !== undefined) { + const activeFrame = activeItem.presActiveFrame ?? activeItem.presCurrentFrame; + if (activeFrame !== undefined) { const transTime = NumCast(activeItem.presTransition, 500); - const context = DocCast(DocCast(activeItem.presentationTargetDoc).context); + const acontext = activeItem.presActiveFrame !== undefined ? DocCast(DocCast(activeItem.presentationTargetDoc).context) : DocCast(activeItem.presentationTargetDoc); + const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext; if (context) { - const contextView = DocumentManager.Instance.getFirstDocumentView(context); - if (contextView?.ComponentView) { - CollectionFreeFormDocumentView.gotoKeyframe((contextView.ComponentView as CollectionFreeFormView).childDocs.slice(), transTime); - context._currentFrame = NumCast(activeItem.presActiveFrame); + const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.ComponentView as CollectionFreeFormView; + if (ffview?.childDocs) { + this._keyTimer = CollectionFreeFormDocumentView.gotoKeyframe(this._keyTimer, ffview.childDocs.slice(), transTime); + context._currentFrame = NumCast(activeFrame); } } } @@ -330,56 +315,91 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && activeItem.mediaStart === 'auto') { this.startTempMedia(targetDoc, activeItem); } - if (targetDoc) { - Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc); - targetDoc && runInAction(() => (targetDoc.focusSpeed = activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500))); - setTimeout(() => (targetDoc.focusSpeed = undefined), NumCast(targetDoc.focusSpeed) + 10); - } - if (targetDoc?.lastFrame !== undefined) { - targetDoc._currentFrame = 0; - } if (!group) this.clearSelectedArray(); this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array this.turnOffEdit(); - this.navigateToActiveItem(); //Handles movement to element only when presTrail is list + this.navigateToActiveItem(finished); //Handles movement to element only when presTrail is list this.onHideDocument(); //Handles hide after/before } }); - static pinDataTypes(target: Doc) { - const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(target.type as any) || target._viewType === CollectionViewType.Stacking; - const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform); - const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any); - const clippable = [DocumentType.COMPARISON].includes(target.type as any); - const dataview = [DocumentType.INK, DocumentType.COL].includes(target.type as any) && target.activeFrame === undefined; - const poslayoutview = [DocumentType.COL].includes(target.type as any) && target.activeFrame === undefined; - const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined; - return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview }; + static pinDataTypes(target?: Doc): { scrollable?: boolean; pannable?: boolean; temporal?: boolean; clippable?: boolean; dataview?: boolean; textview?: boolean; poslayoutview?: boolean; dataannos?: boolean } { + const targetType = target?.type as any; + const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetType) || target?._viewType === CollectionViewType.Stacking; + const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform); + const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType); + const clippable = [DocumentType.COMPARISON].includes(targetType); + const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG].includes(targetType) && target?.activeFrame === undefined; + const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined; + const textview = [DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined; + const dataannos = false; + return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview, dataannos }; } @action - static restoreTargetDocView(bestTarget: Doc, activeItem: Doc) { - const transTime = NumCast(activeItem.presTransition, 500); - const presTransitionTime = `all ${transTime}ms`; - const { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview } = this.pinDataTypes(bestTarget); - bestTarget._viewTransition = presTransitionTime; - if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth; - if (temporal) bestTarget._currentTimecode = activeItem.presStartTime; - if (scrollable) { - bestTarget._scrollTop = activeItem.presPinViewScroll; - const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); - if (contentBounds) { - const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView; - dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }); + playAnnotation = (anno: AudioField) => {}; + @action + static restoreTargetDocView(bestTargetView: Opt<DocumentView>, pinProps: PinProps | undefined, activeItem: Doc, transTime: number, pinDataTypes = this.pinDataTypes(bestTargetView?.rootDoc)) { + if (!bestTargetView) return; + const bestTarget = bestTargetView.rootDoc; + let changed = false; + if (pinProps?.pinDocLayout) { + if ( + bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) || + bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) || + bestTarget.rotation !== NumCast(activeItem.presRot, NumCast(bestTarget.rotation)) || + bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) || + bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height)) + ) { + bestTarget._dataTransition = `all ${transTime}ms`; + bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x)); + bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y)); + bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation)); + bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width)); + bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height)); + setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10); + changed = true; + } + } + if (pinDataTypes.clippable) { + if (bestTarget._clipWidth !== activeItem.presPinClipWidth) { + bestTarget._clipWidth = activeItem.presPinClipWidth; + changed = true; + } + } + if (pinDataTypes.temporal) { + if (bestTarget._currentTimecode !== activeItem.presStartTime) { + bestTarget._currentTimecode = activeItem.presStartTime; + changed = true; } } - if (dataview) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; - if (textview) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; - if (poslayoutview) { + if (pinDataTypes.scrollable) { + if (bestTarget._scrollTop !== activeItem.presPinViewScroll) { + bestTarget._scrollTop = activeItem.presPinViewScroll; + changed = true; + const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); + if (contentBounds) { + const dv = DocumentManager.Instance.getDocumentView(bestTarget)?.ComponentView; + dv?.brushView?.({ panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }); + } + } + } + if (pinDataTypes.dataannos) { + const fkey = Doc.LayoutFieldKey(bestTarget); + Doc.GetProto(bestTarget)[fkey + '-annotations'] = new List<Doc>([...DocListCast(bestTarget[fkey + '-annotations']).filter(doc => doc.unrendered), ...DocListCast(activeItem.presAnnotations)]); + } + if (pinDataTypes.dataview && activeItem.presData !== undefined) { + const fkey = Doc.LayoutFieldKey(bestTarget); + Doc.GetProto(bestTarget)[fkey] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; + bestTarget[fkey + '-useAlt'] = activeItem.presUseAlt; + } + if (pinDataTypes.textview && activeItem.presData !== undefined) Doc.GetProto(bestTarget)[Doc.LayoutFieldKey(bestTarget)] = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; + if (pinDataTypes.poslayoutview) { + changed = true; StrListCast(activeItem.presPinLayoutData) .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number }) .forEach(data => { const doc = DocServer.GetCachedRefField(data.id) as Doc; - doc._dataTransition = presTransitionTime; + doc._dataTransition = `all ${transTime}ms`; doc.x = data.x; doc.y = data.y; doc._width = data.w; @@ -389,16 +409,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { () => StrListCast(activeItem.presPinLayoutData) .map(str => JSON.parse(str) as { id: string; x: number; y: number; w: number; h: number }) - .forEach( - action(data => { - const doc = DocServer.GetCachedRefField(data.id) as Doc; - doc._dataTransition = undefined; - }) - ), + .forEach(action(data => ((DocServer.GetCachedRefField(data.id) as Doc)._dataTransition = undefined))), transTime + 10 ); } - if (pannable) { + if (pinDataTypes.pannable) { const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }; @@ -407,25 +422,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const dv = DocumentManager.Instance.getDocumentView(bestTarget); if (dv) { const computedScale = NumCast(activeItem.presZoom, 1) * Math.min(dv.props.PanelWidth() / viewport.width, dv.props.PanelHeight() / viewport.height); - activeItem.presMovement === 'zoom' && (bestTarget._viewScale = computedScale); + activeItem.presMovement === PresMovement.Zoom && (bestTarget._viewScale = computedScale); dv.ComponentView?.brushView?.(viewport); } } else { - bestTarget._panX = activeItem.presPinViewX; - bestTarget._panY = activeItem.presPinViewY; - activeItem.presMovement === 'zoom' && (bestTarget._viewScale = activeItem.presPinViewScale); + if (bestTarget._panX !== activeItem.presPinViewX || bestTarget._panY !== activeItem.presPinViewY || bestTarget._viewScale !== activeItem.presPinViewScale) { + bestTarget._panX = activeItem.presPinViewX; + bestTarget._panY = activeItem.presPinViewY; + bestTarget._viewScale = activeItem.presPinViewScale; + changed = true; + } } } - return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10); + if (changed) { + return bestTargetView.setViewTransition('all', transTime); + } } /// copies values from the targetDoc (which is the prototype of the pinDoc) to /// reserved fields on the pinDoc so that those values can be restored to the /// target doc when navigating to it. @action - static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined, targetDoc: Doc) { - const { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview } = this.pinDataTypes(pinDoc); - if (pinProps?.pinDocLayout) { + static pinDocView(pinDoc: Doc, pinProps: PinProps, targetDoc: Doc) { + if (pinProps.pinDocLayout) { pinDoc.presPinLayout = true; pinDoc.presX = NumCast(targetDoc.x); pinDoc.presY = NumCast(targetDoc.y); @@ -433,36 +452,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { pinDoc.presWidth = NumCast(targetDoc.width); pinDoc.presHeight = NumCast(targetDoc.height); } - if (pinProps?.pinDocContent) { - pinDoc.presPinData = scrollable || temporal || pannable || clippable || dataview || textview || poslayoutview || pinProps.activeFrame !== undefined; - if (dataview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.data; - if (textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text; - if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop; - if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth; - if (poslayoutview) pinDoc.presPinLayoutData = new List<string>(DocListCast(pinDoc.presData).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) }))); - if (pannable) { - pinDoc.presPinViewX = NumCast(pinDoc._panX); - pinDoc.presPinViewY = NumCast(pinDoc._panY); - pinDoc.presPinViewScale = NumCast(pinDoc._viewScale, 1); + if (pinProps.pinAudioPlay) pinDoc.followLinkAudio = true; + if (pinProps.pinData) { + pinDoc.presPinData = + pinProps.pinData.scrollable || + pinProps.pinData.temporal || + pinProps.pinData.pannable || + pinProps.pinData.clippable || + pinProps.pinData.dataview || + pinProps.pinData.textview || + pinProps.pinData.poslayoutview || + pinProps?.activeFrame !== undefined; + if (pinProps.pinData.dataview) { + const fkey = Doc.LayoutFieldKey(targetDoc); + pinDoc.presUseAlt = targetDoc[fkey + '-useAlt']; + pinDoc.presData = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data; + } + if (pinProps.pinData.dataannos) { + const fkey = Doc.LayoutFieldKey(targetDoc); + pinDoc.presAnnotations = new List<Doc>(DocListCast(Doc.GetProto(targetDoc)[fkey + '-annotations']).filter(doc => !doc.unrendered)); } - if (temporal) { - pinDoc.presStartTime = pinDoc._currentTimecode; - const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(pinDoc.presStartTime) + 0.1); - pinDoc.presEndTime = NumCast(pinDoc.clipEnd, duration); + if (pinProps.pinData.textview) pinDoc.presData = targetDoc[Doc.LayoutFieldKey(targetDoc)] instanceof ObjectField ? (targetDoc[Doc.LayoutFieldKey(targetDoc)] as ObjectField)[Copy]() : targetDoc.text; + if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop; + if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth; + if (pinProps.pinData.poslayoutview) pinDoc.presPinLayoutData = new List<string>(DocListCast(targetDoc.presData).map(d => JSON.stringify({ id: d[Id], x: NumCast(d.x), y: NumCast(d.y), w: NumCast(d._width), h: NumCast(d._height) }))); + if (pinProps.pinData.pannable) { + pinDoc.presPinViewX = NumCast(targetDoc._panX); + pinDoc.presPinViewY = NumCast(targetDoc._panY); + pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1); + } + if (pinProps.pinData.temporal) { + pinDoc.presStartTime = targetDoc._currentTimecode; + const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(targetDoc.presStartTime) + 0.1); + pinDoc.presEndTime = NumCast(targetDoc.clipEnd, duration); } } if (pinProps?.pinViewport) { // If pinWithView option set then update scale and x / y props of slide const bounds = pinProps.pinViewport; pinDoc.presPinView = true; - pinDoc.presPinViewScale = NumCast(pinDoc._viewScale, 1); + pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1); pinDoc.presPinViewX = bounds.left + bounds.width / 2; pinDoc.presPinViewY = bounds.top + bounds.height / 2; pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); } } - - static _navTimer: NodeJS.Timeout; /** * This method makes sure that cursor navigates to the element that * has the option open and last in the group. @@ -471,127 +505,106 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { * a new tab. If presCollection is undefined it will open the document * on the right. */ - navigateToActiveItem = () => { + navigateToActiveItem = (afterNav?: () => void) => { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; + const finished = () => { + afterNav?.(); + console.log('Finish Slide Nav: ' + targetDoc.title); + targetDoc[AnimationSym] = undefined; + }; const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null); const presCollection = Cast(this.layoutDoc.presCollection, Doc, null); const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined; - const includesDoc: boolean = DocListCast(presCollection?.data).includes(targetDoc); - const tab = CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext); + const includesDoc = () => (DocumentManager.Instance.getDocumentView(targetDoc) ? true : false); // DocListCast(presCollection?.data).includes(targetDoc); + const tabMap = CollectionDockingView.Instance?.tabMap; + const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc === srcContext || tab.DashDoc === targetDoc); // Handles the setting of presCollection - if (includesDoc) { + if (includesDoc()) { //Case 1: Pres collection should not change as it is already the same } else if (tab !== undefined) { // Case 2: Pres collection should update this.layoutDoc.presCollection = srcContext; } - const presStatus = this.rootDoc.presStatus; const selViewCache = Array.from(this.selectedArray); 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.SelectView(presDocView, false); - self.rootDoc.presStatus = presStatus; - self.clearSelectedArray(); - selViewCache.forEach(doc => self.addToSelectedArray(doc)); - self._dragArray.splice(0, self._dragArray.length, ...dragViewCache); - self._eleArray.splice(0, self._eleArray.length, ...eleViewCache); + if (!includesDoc()) { + const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc); + if (presDocView) SelectionManager.SelectView(presDocView, false); + this.clearSelectedArray(); + selViewCache.forEach(doc => this.addToSelectedArray(doc)); + this._dragArray.splice(0, this._dragArray.length, ...dragViewCache); + this._eleArray.splice(0, this._eleArray.length, ...eleViewCache); + } + finished(); }); - const openInTab = (doc: Doc, finished?: () => void) => { - (collectionDocView ?? this).props.addDocTab(doc, ''); + const createDocView = (doc: Doc, finished?: () => void) => { + DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.()); + (collectionDocView ?? this).props.addDocTab(doc, OpenWhere.lightbox); this.layoutDoc.presCollection = targetDoc; - // this still needs some fixing - setTimeout(resetSelection, 500); - if (doc !== targetDoc) { - setTimeout(finished ?? emptyFunction, 100); /// give it some time to create the targetDoc if we're opening up its context - } else { - finished?.(); - } }; - PresBox.NavigateToTarget(targetDoc, activeItem, openInTab, srcContext, includesDoc || tab ? undefined : resetSelection); + PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc() || tab ? finished : resetSelection); }; - static NavigateToTarget(targetDoc: Doc, activeItem: Doc, openInTab: any, srcContext: Doc, finished?: () => void) { - if ((activeItem.presPinLayout || activeItem.presPinView) && DocCast(targetDoc.context)?._currentFrame === undefined) { - const transTime = NumCast(activeItem.presTransition, 500); - const presTransitionTime = `all ${transTime}ms`; - targetDoc._dataTransition = presTransitionTime; - targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x)); - targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y)); - targetDoc.rotation = NumCast(activeItem.presRot, NumCast(targetDoc.rotation)); - targetDoc.width = NumCast(activeItem.presWidth, NumCast(targetDoc.width)); - targetDoc.height = NumCast(activeItem.presHeight, NumCast(targetDoc.height)); - setTimeout(() => (targetDoc._dataTransition = undefined), transTime + 10); + static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, srcContext: Doc, finished?: () => void) { + if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) { + (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); + return; } + const restoreLayout = () => { + // 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. + const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined; + if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) { + // targetDoc may or may not be displayed. so get the first available document (or alias) view that matches targetDoc and use it + PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500)); + } + }; // If openDocument is selected then it should open the document for the user if (activeItem.openDocument) { LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc); - } else if (targetDoc && activeItem.presMovement !== PresMovement.None) { - LightboxView.SetLightboxDoc(undefined); - const zooming = activeItem.presMovement !== PresMovement.Pan; - DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, finished, undefined, true, NumCast(activeItem.presZoom, 1)); - } else if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) { - (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); - } - // 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. - if (activeItem.presPinData || activeItem.presPinView) { - clearTimeout(PresBox._navTimer); - // targetDoc may or may not be displayed. this gets the first available document (or alias) view that matches targetDoc - const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document; - if (bestTarget) PresBox._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem); + setTimeout(restoreLayout); + } else { + if (targetDoc) { + const options: DocFocusOptions = { + willPan: activeItem.presMovement !== PresMovement.None, + willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center, + zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1), + zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500), + effect: activeItem, + noSelect: true, + originatingDoc: activeItem, + easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any, + zoomTextSelections: true, + }; + if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined; + var containerDocContext = srcContext ? [srcContext] : []; + while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) { + containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext]; + } + const testTarget = containerDocContext.length ? containerDocContext[0] : targetDoc; + if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(testTarget)) { + DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => { + if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(LightboxView.LightboxDoc)) { + DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished); + restoreLayout(); + } else { + LightboxView.SetLightboxDoc(undefined); + DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished); + restoreLayout(); + } + }); + return; + } + DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished); + restoreLayout(); + } else restoreLayout(); } } /** - * Uses the viewfinder to progressivize through the different views of a single collection. - * @param activeItem: document for which internal zoom is used - */ - zoomProgressivizeNext = (activeItem: Doc) => { - const targetDoc: Doc = this.targetDoc; - const srcContext = Cast(targetDoc?.context, Doc, null); - const docView = DocumentManager.Instance.getDocumentView(targetDoc); - const vfLeft = this.checkList(targetDoc, activeItem['viewfinder-left-indexed']); - const vfWidth = this.checkList(targetDoc, activeItem['viewfinder-width-indexed']); - const vfTop = this.checkList(targetDoc, activeItem['viewfinder-top-indexed']); - const vfHeight = this.checkList(targetDoc, activeItem['viewfinder-height-indexed']); - // Case 1: document that is not a Golden Layout tab - if (srcContext) { - const srcDocView = DocumentManager.Instance.getDocumentView(srcContext); - if (srcDocView) { - const layoutdoc = Doc.Layout(targetDoc); - const panelWidth: number = srcDocView.props.PanelWidth(); - const panelHeight: number = srcDocView.props.PanelHeight(); - const newPanX = NumCast(targetDoc.x) + NumCast(layoutdoc._width) / 2; - const newPanY = NumCast(targetDoc.y) + NumCast(layoutdoc._height) / 2; - const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight); - srcContext._panX = newPanX + (vfLeft + vfWidth / 2); - srcContext._panY = newPanY + (vfTop + vfHeight / 2); - srcContext._viewScale = newScale; - } - } - // Case 2: document is the containing collection - if (docView && !srcContext) { - const panelWidth: number = docView.props.PanelWidth(); - const panelHeight: number = docView.props.PanelHeight(); - const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight); - targetDoc._panX = vfLeft + vfWidth / 2; - targetDoc._panY = vfTop + vfWidth / 2; - targetDoc._viewScale = newScale; - } - const resize = document.getElementById('resizable'); - if (resize) { - resize.style.width = vfWidth + 'px'; - resize.style.height = vfHeight + 'px'; - resize.style.top = vfTop + 'px'; - resize.style.left = vfLeft + 'px'; - } - }; - - /** * For 'Hide Before' and 'Hide After' buttons making sure that * they are hidden each time the presentation is updated. */ @@ -600,54 +613,57 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this.childDocs.forEach((doc, index) => { const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null); - //if (tagDoc) tagDoc.opacity = 1; const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc); - const curInd: number = itemIndexes.indexOf(index); if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1; } else { - if (curDoc.presHideBefore) { - if (itemIndexes.length > 1 && curInd !== 0) { + if (curDoc.presHide) { + if (index !== this.itemIndex) { tagDoc.opacity = 1; - } else { - if (index > this.itemIndex) { - tagDoc.opacity = 0; - } else if (index === this.itemIndex || !curDoc.presHideAfter) { - tagDoc.opacity = 1; - } } } - if (curDoc.presHideAfter) { - if (itemIndexes.length > 1 && curInd !== itemIndexes.length - 1) { + const hidingIndBef = itemIndexes.find(item => item >= this.itemIndex); + if (curDoc.presHideBefore && index === hidingIndBef) { + if (index > this.itemIndex) { + tagDoc.opacity = 0; + } else if (index === this.itemIndex || !curDoc.presHideAfter) { tagDoc.opacity = 1; - } else { - if (index < this.itemIndex) { - tagDoc.opacity = 0; - } else if (index === this.itemIndex || !curDoc.presHideBefore) { - tagDoc.opacity = 1; - } + } + } + const hidingIndAft = itemIndexes + .slice() + .reverse() + .find(item => item < this.itemIndex); + if (curDoc.presHideAfter && index === hidingIndAft) { + if (index < this.itemIndex) { + tagDoc.opacity = 0; + } else if (index === this.itemIndex || !curDoc.presHideBefore) { + tagDoc.opacity = 1; + } + } + const hidingInd = itemIndexes.find(item => item === this.itemIndex); + if (curDoc.presHide && index === hidingInd) { + if (index === this.itemIndex) { + tagDoc.opacity = 0; } } } }); }; - //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc - @action - startAutoPres = (startSlide: number) => { - this.layoutDoc.presStatus = PresStatus.Autoplay; - this.startPresentation(startSlide); - clearTimeout(this._presTimer); - const func = (itemIndex: number) => { - if (itemIndex === this.next()) this.layoutDoc.presStatus = PresStatus.Manual; - else - this._presTimer = setTimeout( - () => this.layoutDoc.presStatus !== PresStatus.Manual && func(this.itemIndex), - NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition) - ); + _exitTrail: Opt<() => void>; + PlayTrail = (doc: Doc) => { + const savedState = { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) }; + this.startPresentation(0); + this._exitTrail = () => { + const { x, y, s, c } = savedState; + c._panX = x; + c._panY = y; + c._viewScale = s; + LightboxView.SetLightboxDoc(undefined); + Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); + return PresStatus.Edit; }; - - func(this.itemIndex); }; // The function pauses the auto presentation @@ -676,14 +692,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }; // The function allows for viewing the pres path on toggle - @action togglePath = (srcContext: Doc, off?: boolean) => { - if (off) { - this._pathBoolean = false; - srcContext.presPathView = false; - } else { - runInAction(() => (this._pathBoolean = !this._pathBoolean)); - srcContext.presPathView = this._pathBoolean; - } + @action togglePath = (off?: boolean) => { + this._pathBoolean = off ? false : !this._pathBoolean; + CollectionFreeFormView.ShowPresPaths = this._pathBoolean; }; // The function allows for expanding the view of pres on toggle @@ -701,40 +712,54 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { * @param startIndex: index that the presentation will start at */ startPresentation = (startIndex: number) => { - this.childDocs.forEach(doc => { - const tagDoc = doc.presentationTargetDoc as Doc; - if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) { - tagDoc.opacity = 0; - } - if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) { - tagDoc.opacity = 0; - } - }); - this.gotoDocument(startIndex, this.activeItem); + clearTimeout(this._presTimer); + if (this.childDocs.length) { + this.layoutDoc.presStatus = PresStatus.Autoplay; + this.childDocs.forEach(doc => { + const tagDoc = doc.presentationTargetDoc as Doc; + if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) tagDoc.opacity = 0; + if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) tagDoc.opacity = 0; + // if (doc.presHide && this.childDocs.indexOf(doc) === startIndex) tagDoc.opacity = 0; + }); + const func = () => { + const delay = NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition); + this._presTimer = setTimeout(() => { + if (!this.next()) this.layoutDoc.presStatus = this._exitTrail?.() ?? PresStatus.Manual; + this.layoutDoc.presStatus === PresStatus.Autoplay && func(); + }, delay); + }; + this.gotoDocument(startIndex, this.activeItem, undefined, func); + } }; /** * The method called to open the presentation as a minimized view */ @action - updateMinimize = async () => { + enterMinimize = () => { + clearTimeout(this._presTimer); + const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + this.props.removeDocument?.(this.layoutDoc); + return PresBox.OpenPresMinimized(this.rootDoc, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]); + }; + exitMinimize = () => { if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) { - this.layoutDoc.presStatus = PresStatus.Edit; Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); - CollectionDockingView.AddSplit(this.rootDoc, 'right'); - } else { - this.layoutDoc.presStatus = PresStatus.Edit; - clearTimeout(this._presTimer); - const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - this.rootDoc.overlayX = pt[0] + (this.props.PanelWidth() - 250); - this.rootDoc.overlayY = pt[1] + 10; - this.rootDoc._height = 30; - this.rootDoc._width = 248; - Doc.AddDocToList(Doc.MyOverlayDocs, undefined, this.rootDoc); - this.props.removeDocument?.(this.layoutDoc); + CollectionDockingView.AddSplit(this.rootDoc, OpenWhereMod.right); } + return PresStatus.Edit; }; + public static minimizedWidth = 198; + public static OpenPresMinimized(doc: Doc, pt: number[]) { + doc.overlayX = pt[0]; + doc.overlayY = pt[1]; + doc._height = 30; + doc._width = PresBox.minimizedWidth; + Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc); + return (doc.presStatus = PresStatus.Manual); + } + /** * Called when the user changes the view type * Either 'List' (stacking) or 'Slides' (carousel) @@ -766,20 +791,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (this.childDocs[stopDocIndex - 1].mediaStopTriggerList) { const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList); list.push(activeItem); - // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list; - console.log(list); + // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;\ } else { this.childDocs[stopDocIndex - 1].mediaStopTriggerList = new List<Doc>(); const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList); list.push(activeItem); // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list; - console.log(list); } }); movementName = action((activeItem: Doc) => { - if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) { - activeItem.presMovement = 'zoom'; + if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Center, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) { + return PresMovement.Zoom; } return StrCast(activeItem.presMovement); }); @@ -804,7 +827,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } else { if (!doc.aliasOf) { const original = Doc.MakeAlias(doc); - TabDocView.PinDoc(original); + TabDocView.PinDoc(original, {}); setTimeout(() => this.removeDocument(doc), 0); return false; } else { @@ -868,6 +891,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //Regular click @action selectElement = async (doc: Doc) => { + CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => DocumentManager.Instance.getDocumentView(clip)?.ComponentView?.Pause?.()); this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0); else this.updateCurrentPresentation(DocCast(doc.context)); @@ -920,7 +944,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { else this.regularSelect(doc, ref, drag, focus); }; - static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance.keyEvents(e); + static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance?.keyEvents(e); // Key for when the presentaiton is active @action @@ -947,11 +971,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { break; case 'Escape': if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) { - this.updateMinimize(); - } else if (this.layoutDoc.presStatus === 'edit') { + this.exitClicked(); + } else if (this.layoutDoc.presStatus === PresStatus.Edit) { this.clearSelectedArray(); this._eleArray.length = this._dragArray.length = 0; - } else this.layoutDoc.presStatus = 'edit'; + } else { + this.layoutDoc.presStatus = PresStatus.Edit; + } if (this._presTimer) clearTimeout(this._presTimer); handled = true; break; @@ -1010,17 +1036,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } }; - /** - * - */ - @action - viewPaths = () => { - const srcContext = Cast(this.rootDoc.presCollection, Doc, null); - if (srcContext) { - this.togglePath(srcContext); - } - }; - getAllIndexes = (arr: Doc[], val: Doc) => arr.map((doc, i) => (doc === val ? i : -1)).filter(i => i !== -1); // Adds the index in the pres path graphically @@ -1100,16 +1115,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { */ @computed get paths() { let pathPoints = ''; - const presCollection = Cast(this.rootDoc.presCollection, Doc, null); this.childDocs.forEach((doc, index) => { const tagDoc = Cast(doc.presentationTargetDoc, Doc, null); - const srcContext = Cast(tagDoc?.context, Doc, null); - if (tagDoc && presCollection === srcContext) { + if (tagDoc) { const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2; const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2; if ((index = 0)) pathPoints = n1x + ',' + n1y; else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; - } else if (doc.presPinView && presCollection === tagDoc) { + } else if (doc.presPinView) { const n1x = NumCast(doc.presPinViewX); const n1y = NumCast(doc.presPinViewY); if ((index = 0)) pathPoints = n1x + ',' + n1y; @@ -1133,14 +1146,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { /> ); } + getPaths = (collection: Doc) => this.paths; // needs to be smarter and figure out the paths to draw for this specific collection. or better yet, draw everything in an overlay layer instad of within a collection // Converts seconds to ms and updates presTransition - setTransitionTime = (number: String, change?: number) => { + public static SetTransitionTime = (number: String, setter: (timeInMS: number) => void, change?: number) => { let timeInMS = Number(number) * 1000; if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; - if (timeInMS > 10000) timeInMS = 10000; - this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)); + if (timeInMS > 100000) timeInMS = 100000; + setter(timeInMS); + }; + setTransitionTime = (number: String, change?: number) => { + PresBox.SetTransitionTime(number, (timeInMS: number) => this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)), change); }; // Converts seconds to ms and updates presTransition @@ -1176,6 +1193,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @undoBatch @action + updateHide = (activeItem: Doc) => { + activeItem.presHide = !activeItem.presHide; + this.selectedArray.forEach(doc => (doc.presHide = activeItem.presHide)); + }; + + @undoBatch + @action updateHideAfter = (activeItem: Doc) => { activeItem.presHideAfter = !activeItem.presHideAfter; this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter)); @@ -1187,10 +1211,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { activeItem.openDocument = !activeItem.openDocument; this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument)); }; + @undoBatch + @action + updateEaseFunc = (activeItem: Doc) => { + activeItem.presEaseFunc = activeItem.presEaseFunc === 'linear' ? 'ease' : 'linear'; + this.selectedArray.forEach(doc => (doc.presEaseFunc = activeItem.presEaseFunc)); + }; @undoBatch @action - updateEffectDirection = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect)); + updateEffectDirection = (effect: PresEffectDirection, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect)); @undoBatch @action @@ -1198,13 +1228,36 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { _batch: UndoManager.Batch | undefined = undefined; + public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => { + let batch: any; + return ( + <input + type="range" + step={step} + min={min} + max={max} + value={value} + style={{ marginLeft: hmargin, marginRight: hmargin, width: `calc(100% - ${2 * (hmargin ?? 0)}px)` }} + className={`toolbar-slider ${active ? '' : 'none'}`} + onPointerDown={e => { + batch = UndoManager.StartBatch('pres slider'); + e.stopPropagation(); + }} + onPointerUp={() => batch?.end()} + onChange={e => { + e.stopPropagation(); + change(e.target.value); + }} + /> + ); + }; @computed get transitionDropdown() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const isPresCollection: boolean = targetDoc === this.layoutDoc.presCollection; const isPinWithView: boolean = BoolCast(activeItem.presPinView); const presEffect = (effect: PresEffect) => ( - <div className={`presBox-dropdownOption ${this.activeItem.presEffect === effect || (effect === PresEffect.None && !this.activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}> + <div className={`presBox-dropdownOption ${activeItem.presEffect === effect || (effect === PresEffect.None && !activeItem.presEffect) ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateEffect(effect)}> {effect} </div> ); @@ -1213,47 +1266,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { {movement} </div> ); - const presDirection = (diretion: PresEffect, icon: string, gridColumn: number, gridRow: number, opts: object) => { - const color = this.activeItem.presEffectDirection === diretion || (diretion === PresEffect.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black'; + const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => { + const color = activeItem.presEffectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black'; return ( - <Tooltip title={<div className="dash-tooltip">{diretion}</div>}> + <Tooltip title={<div className="dash-tooltip">{direction}</div>}> <div - style={{ ...opts, border: diretion === PresEffect.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }} - onClick={() => this.updateEffectDirection(diretion)}> + style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }} + onClick={() => this.updateEffectDirection(direction)}> {icon ? <FontAwesomeIcon icon={icon as any} /> : null} </div> </Tooltip> ); }; - const inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void) => { - return ( - <input - type="range" - step={step} - min={min} - max={max} - value={value} - className={`toolbar-slider ${active ? '' : 'none'}`} - onPointerDown={e => { - this._batch = UndoManager.StartBatch('pres slider'); - e.stopPropagation(); - }} - onPointerUp={() => this._batch?.end()} - onChange={e => { - e.stopPropagation(); - change(e.target.value); - }} - /> - ); - }; if (activeItem && targetDoc) { const type = targetDoc.type; const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5; const zoom = NumCast(activeItem.presZoom, 1) * 100; - let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2; + let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 0; if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration); - const effect = this.activeItem.presEffect ? this.activeItem.presEffect : 'None'; - activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom'; + const effect = activeItem.presEffect ? activeItem.presEffect : PresMovement.None; + // activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : PresMovement.Zoom; return ( <div className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`} @@ -1277,6 +1309,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={StopEvent} style={{ display: this._openMovementDropdown ? 'grid' : 'none' }}> {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.None)} + {presMovement(PresMovement.Center)} {presMovement(PresMovement.Zoom)} {presMovement(PresMovement.Pan)} {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.Jump)} @@ -1296,9 +1329,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> </div> - {inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)} - <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}> - <div className="presBox-subheading">Movement Speed</div> + {PresBox.inputter('0', '1', '100', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)} + <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}> + <div className="presBox-subheading">Transition Speed</div> <div className="ribbon-property"> <input className="presBox-input" type="number" value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setTransitionTime(e.target.value))} /> s </div> @@ -1311,8 +1344,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> </div> - {inputter('0.1', '0.1', '10', transitionSpeed, [PresMovement.Pan, PresMovement.Zoom].includes(activeItem.presMovement as any), this.setTransitionTime)} - <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}> + {PresBox.inputter('0.1', '0.1', '100', transitionSpeed, true, this.setTransitionTime)} + <div className={'slider-headers'}> <div className="slider-text">Fast</div> <div className="slider-text">Medium</div> <div className="slider-text">Slow</div> @@ -1329,6 +1362,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </Tooltip> )} {isPresCollection ? null : ( + <Tooltip title={<div className="dash-tooltip">{'Hide while presented'}</div>}> + <div className={`ribbon-toggle ${activeItem.presHide ? 'active' : ''}`} onClick={() => this.updateHide(activeItem)}> + Hide + </div> + </Tooltip> + )} + {isPresCollection ? null : ( <Tooltip title={<div className="dash-tooltip">{'Hide after presented'}</div>}> <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}> Hide after @@ -1340,6 +1380,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { Lightbox </div> </Tooltip> + <Tooltip title={<div className="dash-tooltip">{'Transition movement style'}</div>}> + <div className="ribbon-toggle" onClick={() => this.updateEaseFunc(activeItem)}> + {`${StrCast(activeItem.presEaseFunc, 'ease')}`} + </div> + </Tooltip> </div> {type === DocumentType.AUDIO || type === DocumentType.VID ? null : ( <> @@ -1357,7 +1402,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> </div> - {inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)} + {PresBox.inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)} <div className={'slider-headers'} style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'grid' }}> <div className="slider-text">Short</div> <div className="slider-text">Medium</div> @@ -1369,6 +1414,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { {isPresCollection ? null : ( <div className="ribbon-box"> Effects + <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}> + <div className="presBox-subheading">Play Audio Annotation</div> + <input className="presBox-checkbox" style={{ margin: 10 }} type="checkbox" onChange={() => (activeItem.followLinkAudio = !BoolCast(activeItem.followLinkAudio))} checked={BoolCast(activeItem.followLinkAudio)} /> + </div> <div className="presBox-dropdown" onClick={action(e => { @@ -1376,7 +1425,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this._openEffectDropdown = !this._openEffectDropdown; })} style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> - {effect.toString()} + {effect?.toString()} <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this._openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}> {presEffect(PresEffect.None)} @@ -1387,16 +1436,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { {presEffect(PresEffect.Roll)} </div> </div> - <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? 'none' : 'inline-flex' }}> + <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : 'inline-flex' }}> <div className="presBox-subheading">Effect direction</div> <div className="ribbon-property">{StrCast(this.activeItem.presEffectDirection)}</div> </div> - <div className="effectDirection" style={{ display: effect === 'None' ? 'none' : 'grid', width: 40 }}> - {presDirection(PresEffect.Left, 'angle-right', 1, 2, {})} - {presDirection(PresEffect.Right, 'angle-left', 3, 2, {})} - {presDirection(PresEffect.Top, 'angle-down', 2, 1, {})} - {presDirection(PresEffect.Bottom, 'angle-up', 2, 3, {})} - {presDirection(PresEffect.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })} + <div className="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}> + {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})} + {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})} + {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})} + {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})} + {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })} </div> </div> )} @@ -1415,7 +1464,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { applyTo = (array: Doc[]) => { this.updateMovement(this.activeItem.presMovement as PresMovement, true); this.updateEffect(this.activeItem.presEffect as PresEffect, true); - this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffect, true); + this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffectDirection, true); const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem; array.forEach(curDoc => { curDoc.presTransition = presTransition; @@ -1745,10 +1794,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const presData = Cast(this.rootDoc.data, listSpec(Doc)); if (data && presData) { data.push(doc); - TabDocView.PinDoc(doc); + TabDocView.PinDoc(doc, {}); this.gotoDocument(this.childDocs.length, this.activeItem); } else { - this.props.addDocTab(doc, 'add:right'); + this.props.addDocTab(doc, OpenWhere.addRight); } } }; @@ -1781,7 +1830,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { className="dropdown-play-button" onClick={undoBatch( action(() => { - this.updateMinimize(); + this.enterMinimize(); this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }) @@ -1804,37 +1853,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } scrollFocus = () => { - this.startOrPause(false); + // this.gotoDocument(0); + // this.startOrPause(false); return undefined; }; - // Case in which the document has keyframes to navigate to next key frame - @action - nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => { - const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]); - const currentFrame = Cast(tagDoc._currentFrame, 'number', null); - if (currentFrame === undefined) { - tagDoc._currentFrame = 0; - // CollectionFreeFormDocumentView.setupScroll(tagDoc, 0); - // CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); - } - // if (tagDoc.editScrollProgressivize) CollectionFreeFormDocumentView.updateScrollframe(tagDoc, currentFrame); - CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc); - tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1); - tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame)); - }; - - @action - prevKeyframe = (tagDoc: Doc, actItem: Doc): void => { - const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]); - const currentFrame = Cast(tagDoc._currentFrame, 'number', null); - if (currentFrame === undefined) { - tagDoc._currentFrame = 0; - // CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0); - } - CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice()); - tagDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1); - }; + _keyTimer: NodeJS.Timeout | undefined; /** * Returns the collection type as a string for headers @@ -1860,123 +1884,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @observable private openActiveColorPicker: boolean = false; @observable private openViewedColorPicker: boolean = false; - @computed get progressivizeDropdown() { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - if (activeItem && targetDoc) { - const activeFontColor = targetDoc['pres-text-color'] ? StrCast(targetDoc['pres-text-color']) : 'Black'; - const viewedFontColor = targetDoc['pres-text-viewed-color'] ? StrCast(targetDoc['pres-text-viewed-color']) : 'Black'; - return ( - <div className={`presBox-ribbon ${this._progressivizeTools && this.layoutDoc.presStatus === 'edit' ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> - <div className="ribbon-box"> - {this.stringType} selected - <div - className="ribbon-doubleButton" - style={{ - borderTop: 'solid 1px darkgrey', - display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none', - }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeChild}> - Contents - </div> - <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editProgressivize}> - Edit - </div> - </div> - <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}> - <div className="presBox-subheading">Active text color</div> - <div - className="ribbon-colorBox" - style={{ backgroundColor: activeFontColor, height: 15, width: 15 }} - onClick={action(() => { - this.openActiveColorPicker = !this.openActiveColorPicker; - })}></div> - </div> - {this.activeColorPicker} - <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}> - <div className="presBox-subheading">Viewed font color</div> - <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => (this.openViewedColorPicker = !this.openViewedColorPicker))}></div> - </div> - {this.viewedColorPicker} - <div - className="ribbon-doubleButton" - style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? 'inline-flex' : 'none' }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeZoom}> - Zoom - </div> - <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editZoomProgressivize}> - Edit - </div> - </div> - <div - className="ribbon-doubleButton" - style={{ - borderTop: 'solid 1px darkgrey', - display: targetDoc._viewType === 'stacking' || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none', - }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeScroll}> - Scroll - </div> - <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editScrollProgressivize}> - Edit - </div> - </div> - </div> - <div className="ribbon-final-box"> - Frames - <div className="ribbon-doubleButton"> - <div className="ribbon-frameSelector"> - <div - key="back" - title="back frame" - className="backKeyframe" - onClick={e => { - e.stopPropagation(); - this.prevKeyframe(targetDoc, activeItem); - }}> - <FontAwesomeIcon icon={'caret-left'} size={'lg'} /> - </div> - <div - key="num" - title="toggle view all" - className="numKeyframe" - style={{ color: targetDoc.keyFrameEditing ? 'white' : 'black', backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }} - onClick={action(() => (targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}> - {NumCast(targetDoc._currentFrame)} - </div> - <div - key="fwd" - title="forward frame" - className="fwdKeyframe" - onClick={e => { - e.stopPropagation(); - this.nextKeyframe(targetDoc, activeItem); - }}> - <FontAwesomeIcon icon={'caret-right'} size={'lg'} /> - </div> - </div> - <Tooltip - title={ - <> - <div className="dash-tooltip">{'Last frame'}</div> - </> - }> - <div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div> - </Tooltip> - </div> - <div className="ribbon-frameList"> - {this.frameListHeader} - {this.frameList} - </div> - <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(' TODO: play frames')}> - Play - </div> - </div> - </div> - ); - } - } - @undoBatch @action switchActive = (color: ColorState) => { @@ -2011,262 +1918,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } @action - turnOffEdit = (paths?: boolean) => { - // Turn off paths - if (paths) { - const srcContext = Cast(this.rootDoc.presCollection, Doc, null); - if (srcContext) this.togglePath(srcContext, true); - } - // Turn off the progressivize editors for each document - this.childDocs.forEach(doc => { - doc.editSnapZoomProgressivize = false; - doc.editZoomProgressivize = false; - const targetDoc = Cast(doc.presentationTargetDoc, Doc, null); - if (targetDoc) { - targetDoc.editZoomProgressivize = false; - // targetDoc.editScrollProgressivize = false; - } - }); - }; - - //Toggle whether the user edits or not - @action - editZoomProgressivize = (e: React.MouseEvent) => { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - if (!targetDoc.editZoomProgressivize) { - if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true; - targetDoc.zoomProgressivize = true; - targetDoc.editZoomProgressivize = true; - activeItem.editZoomProgressivize = true; - } else { - targetDoc.editZoomProgressivize = false; - activeItem.editZoomProgressivize = false; - } - }; - - //Toggle whether the user edits or not - @action - editScrollProgressivize = (e: React.MouseEvent) => { - const targetDoc: Doc = this.targetDoc; - if (!targetDoc.editScrollProgressivize) { - if (!targetDoc.scrollProgressivize) { - targetDoc.scrollProgressivize = true; - this.activeItem.scrollProgressivize = true; - } - targetDoc.editScrollProgressivize = true; - } else { - targetDoc.editScrollProgressivize = false; - } - }; - - //Progressivize Zoom - @action - progressivizeScroll = (e: React.MouseEvent) => { - e.stopPropagation(); - this.activeItem.scrollProgressivize = !this.activeItem.scrollProgressivize; - const targetDoc: Doc = this.targetDoc; - targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize; - // CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame)); - if (targetDoc.editScrollProgressivize) { - targetDoc.editScrollProgressivize = false; - targetDoc._currentFrame = 0; - targetDoc.lastFrame = 0; - } - }; - - //Progressivize Zoom - @action - progressivizeZoom = (e: React.MouseEvent) => { - e.stopPropagation(); - const activeItem: Doc = this.activeItem; - activeItem.zoomProgressivize = !activeItem.zoomProgressivize; - const targetDoc: Doc = this.targetDoc; - targetDoc.zoomProgressivize = !targetDoc.zoomProgressivize; - CollectionFreeFormDocumentView.setupZoom(activeItem, targetDoc); - if (activeItem.editZoomProgressivize) { - activeItem.editZoomProgressivize = false; - targetDoc._currentFrame = 0; - targetDoc.lastFrame = 0; - } - }; - - //Progressivize Child Docs - @action - editProgressivize = (e: React.MouseEvent) => { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - targetDoc._currentFrame = targetDoc.lastFrame; - if (!targetDoc.editProgressivize) { - if (!activeItem.presProgressivize) { - activeItem.presProgressivize = true; - targetDoc.presProgressivize = true; - } - targetDoc.editProgressivize = true; - } else { - targetDoc.editProgressivize = false; - } - }; - - @action - progressivizeChild = (e: React.MouseEvent) => { - e.stopPropagation(); - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); - if (!activeItem.presProgressivize) { - targetDoc.keyFrameEditing = false; - activeItem.presProgressivize = true; - targetDoc.presProgressivize = true; - targetDoc._currentFrame = 0; - docs.forEach((doc, i) => CollectionFreeFormDocumentView.setupKeyframes([doc], i, true)); - targetDoc.lastFrame = targetDoc.lastFrame ? NumCast(targetDoc.lastFrame) : docs.length - 1; - } else { - // targetDoc.editProgressivize = false; - activeItem.presProgressivize = false; - targetDoc.presProgressivize = false; - targetDoc._currentFrame = 0; - targetDoc.keyFrameEditing = true; - } - }; - - @action - checkMovementLists = (doc: Doc, xlist: any, ylist: any) => { - const x: List<number> = xlist; - const y: List<number> = ylist; - const tags: JSX.Element[] = []; - let pathPoints = ''; //List of all of the pathpoints that need to be added - for (let i = 0; i < x.length - 1; i++) { - if (y[i] || x[i]) { - if (i === 0) pathPoints = x[i] - 11 + ',' + (y[i] + 33); - else pathPoints = pathPoints + ' ' + (x[i] - 11) + ',' + (y[i] + 33); - tags.push( - <div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}> - {i} - </div> - ); - } - } - tags.push( - <svg style={{ overflow: 'visible', position: 'absolute' }}> - <polyline - points={pathPoints} - style={{ - position: 'absolute', - opacity: 1, - stroke: '#000000', - strokeWidth: 2, - strokeDasharray: '10 5', - }} - fill="none" - /> - </svg> - ); - return tags; - }; - - @observable - toggleDisplayMovement = (doc: Doc) => (doc.displayMovement = !doc.displayMovement); - - @action - checkList = (doc: Doc, list: any): number => { - const x: List<number> = list; - if (x?.length >= NumCast(doc._currentFrame) + 1) { - return x[NumCast(doc._currentFrame)]; - } else if (x) { - x.length = NumCast(doc._currentFrame) + 1; - x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1]; - return x[NumCast(doc._currentFrame)]; - } - return 100; - }; - - @computed get progressivizeChildDocs() { - const targetDoc: Doc = this.targetDoc; - const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); - const tags: JSX.Element[] = []; - docs.forEach((doc, index) => { - if (doc['x-indexed'] && doc['y-indexed']) { - tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? 'block' : 'none' }}>{this.checkMovementLists(doc, doc['x-indexed'], doc['y-indexed'])}</div>); - } - tags.push( - <div - className="progressivizeButton" - key={index} - onPointerLeave={() => { - if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; - }} - onPointerOver={() => { - if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; - }} - onClick={e => { - this.toggleDisplayMovement(doc); - e.stopPropagation(); - }} - style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : '#c8c8c8', top: NumCast(doc.y), left: NumCast(doc.x) }}> - <div className="progressivizeButton-prev"> - <FontAwesomeIcon - icon={'caret-left'} - size={'lg'} - onClick={e => { - e.stopPropagation(); - this.prevAppearFrame(doc, index); - }} - /> - </div> - <div className="progressivizeButton-frame">{NumCast(doc.appearFrame)}</div> - <div className="progressivizeButton-next"> - <FontAwesomeIcon - icon={'caret-right'} - size={'lg'} - onClick={e => { - e.stopPropagation(); - this.nextAppearFrame(doc, index); - }} - /> - </div> - </div> - ); - }); - return tags; - } - - @action - nextAppearFrame = (doc: Doc, i: number) => { - doc.appearFrame = (Cast(doc.appearFrame, 'number', null) ?? 0) + 1; - this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame)); - }; - - @action - prevAppearFrame = (doc: Doc, i: number) => { - doc.appearFrame = Math.max(0, (Cast(doc.appearFrame, 'number', null) ?? 0) - 1); - this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame)); - }; - - @action - updateOpacityList = (list: any, frame: number) => { - const x: List<number> = list; - if (x && x.length >= frame) { - for (let i = 0; i < x.length; i++) { - if (i < frame) { - x[i] = 0; - } else if (i >= frame) { - x[i] = 1; - } - } - list = x; - } else { - x.length = frame + 1; - for (let i = 0; i < x.length; i++) { - if (i < frame) { - x[i] = 0; - } else if (i >= frame) { - x[i] = 1; - } - } - list = x; - } - }; + turnOffEdit = (paths?: boolean) => paths && this.togglePath(true); // Turn off paths @computed get toolbarWidth(): number { @@ -2289,11 +1941,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <FontAwesomeIcon icon={"plus"} /> <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} /> </div></Tooltip> */} - <Tooltip title={<div className="dash-tooltip">{'View paths'}</div>}> + <Tooltip title={<div className="dash-tooltip">View paths</div>}> <div - style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }} + style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }} className={'toolbar-button'} - onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}> + onClick={this.childDocs.length > 1 ? () => this.togglePath() : undefined}> <FontAwesomeIcon icon={'exchange-alt'} /> </div> </Tooltip> @@ -2332,8 +1984,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get topPanel() { const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); return ( - <div className="presBox-buttons" style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}> + <div className={`presBox-buttons${inOverlay ? ' inOverlay' : ''}`} style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}> {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={StopEvent} value={CollectionViewType.Stacking}> @@ -2350,7 +2003,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </select> )} <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}> - <span className={`presBox-button ${this.layoutDoc.presStatus === 'edit' ? 'present' : ''}`}> + <span className={`presBox-button ${this.layoutDoc.presStatus === PresStatus.Edit ? 'present' : ''}`}> <div className="presBox-button-left" onClick={undoBatch(() => { @@ -2379,89 +2032,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { ); } - @action - getList = (list: any): List<number> => list; - - @action - updateList = (list: any): List<number> => { - const targetDoc: Doc = this.targetDoc; - const x: List<number> = list; - x[x.length - 1] = NumCast(targetDoc._scrollY); - return x; - }; - - @action - newFrame = () => { - const activeItem: Doc = this.activeItem; - const type: string = StrCast(this.targetDoc.type); - if (!activeItem.frameList) activeItem.frameList = new List<number>(); - switch (type) { - case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB: - this.updateList(activeItem.frameList); - break; - } - }; - - @computed get frameListHeader() { - return ( - <div className="frameList-header"> - Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : null} - <div className={'frameList-headerButtons'}> - <Tooltip title={<div className="dash-tooltip">{'Add frame by example'}</div>}> - <div - className={'headerButton'} - onClick={e => { - e.stopPropagation(); - this.newFrame(); - }}> - <FontAwesomeIcon icon={'plus'} onPointerDown={e => e.stopPropagation()} /> - </div> - </Tooltip> - <Tooltip title={<div className="dash-tooltip">{'Edit in collection'}</div>}> - <div - className={'headerButton'} - onClick={e => { - e.stopPropagation(); - console.log('New frame'); - }}> - <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} /> - </div> - </Tooltip> - </div> - </div> - ); - } - - @computed get frameList() { - const frameList: List<number> = this.getList(this.activeItem.frameList); - return !frameList ? null : ( - <div className="frameList-container"> - {frameList.map(value => ( - <div className="framList-item" /> - ))} - </div> - ); - } - - @computed get playButtonFrames() { - const targetDoc = this.targetDoc; - return !this.targetDoc ? null : ( - <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}> - <div>{NumCast(targetDoc._currentFrame)}</div> - <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div> - <div>{NumCast(targetDoc.lastFrame)}</div> - </div> - ); - } - @computed get playButtons() { const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1; const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); // 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 ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}> + <div + className="presPanel-button" + style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} + onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}> <FontAwesomeIcon icon={'redo-alt'} /> </div> </Tooltip> @@ -2469,43 +2051,77 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} - onClick={e => { - this.back(); - if (this._presTimer) { - clearTimeout(this._presTimer); - this.layoutDoc.presStatus = PresStatus.Manual; - } - e.stopPropagation(); - }}> + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + () => { + this.back(); + if (this._presTimer) { + clearTimeout(this._presTimer); + this.layoutDoc.presStatus = PresStatus.Manual; + } + e.stopPropagation(); + }, + false, + false + ) + }> <FontAwesomeIcon icon={'arrow-left'} /> </div> <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}> - <div className="presPanel-button" onClick={e => this.startOrPause(true)}> + <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.startOrPause(true), false, false)}> <FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} /> </div> </Tooltip> <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} - onClick={e => { - this.next(); - if (this._presTimer) { - clearTimeout(this._presTimer); - this.layoutDoc.presStatus = PresStatus.Manual; - } - e.stopPropagation(); - }}> + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + () => { + this.next(); + if (this._presTimer) { + clearTimeout(this._presTimer); + this.layoutDoc.presStatus = PresStatus.Manual; + } + e.stopPropagation(); + }, + false, + false + ) + }> <FontAwesomeIcon icon={'arrow-right'} /> </div> <div className="presPanel-divider"></div> <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}> - <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.nextSlide(0)}> + <div + className="presPanel-button" + style={{ border: 'solid 1px white' }} + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + () => { + this.nextSlide(0); + }, + false, + false + ) + }> <b>1</b> </div> </Tooltip> - <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}> - Slide {this.itemIndex + 1} / {this.childDocs.length} - {this.playButtonFrames} + <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}> + {`${inOverlay ? '' : 'Slide'} ${this.itemIndex + 1} / ${this.childDocs.length}`} </div> <div className="presPanel-divider"></div> {this.props.PanelWidth() > 250 ? ( @@ -2513,14 +2129,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { className="presPanel-button-text" onClick={undoBatch( action(() => { - this.layoutDoc.presStatus = 'edit'; + this.layoutDoc.presStatus = PresStatus.Edit; clearTimeout(this._presTimer); }) )}> EXIT </div> ) : ( - <div className="presPanel-button" onClick={undoBatch(action(() => (this.layoutDoc.presStatus = 'edit')))}> + <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.exitClicked, false, false)}> <FontAwesomeIcon icon={'times'} /> </div> )} @@ -2531,7 +2147,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @action startOrPause = (makeActive = true) => { makeActive && this.updateCurrentPresentation(); - if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex); + if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startPresentation(this.itemIndex); else this.pauseAutoPres(); }; @@ -2554,15 +2170,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }; @undoBatch @action - exitClicked = (e: PointerEvent) => { - this.updateMinimize(); - this.layoutDoc.presStatus = PresStatus.Edit; + exitClicked = () => { + this.layoutDoc.presStatus = this._exitTrail?.() ?? this.exitMinimize(); clearTimeout(this._presTimer); }; - @action - startMarqueeCreateSlide = () => (PresBox.startMarquee = true); - AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => { var indexNum = 0; for (let i = 0; i < index.length; i++) { @@ -2594,8 +2206,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { // needed to ensure that the childDocs are loaded for looking up fields this.childDocs.slice(); const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; - const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1; - const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0; + const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1; + const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); return this.props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player <div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}> <div @@ -2629,7 +2242,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </Tooltip> <div className="presPanel-button-text"> Slide {this.itemIndex + 1} / {this.childDocs.length} - {this.playButtonFrames} </div> <div className="presPanel-divider" /> <div className="presPanel-button-text" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}> @@ -2638,7 +2250,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> ) : ( - <div className="presBox-cont" style={{ minWidth: DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}> + <div className="presBox-cont" style={{ minWidth: inOverlay ? PresBox.minimizedWidth : undefined }}> {this.topPanel} {this.toolbar} {this.newDocumentToolbarDropdown} @@ -2652,6 +2264,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { PanelHeight={this.panelHeight} childIgnoreNativeSize={true} moveDocument={returnFalse} + ignoreUnrendered={true} //childFitWidth={returnTrue} childOpacity={returnOne} childLayoutTemplate={this.childLayoutTemplate} @@ -2664,7 +2277,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { ScreenToLocalTransform={this.getTransform} AddToMap={this.AddToMap} RemFromMap={this.RemFromMap} - hierarchyIndex={[]} + hierarchyIndex={emptyPath} /> ) : null} </div> @@ -2684,7 +2297,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { static NavigateToDoc(bestTarget: Doc, activeItem: Doc) { const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null); const openInTab = (doc: Doc, finished?: () => void) => { - CollectionDockingView.AddSplit(doc, 'right'); + CollectionDockingView.AddSplit(doc, OpenWhereMod.right); finished?.(); }; PresBox.NavigateToTarget(bestTarget, activeItem, openInTab, srcContext); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 5d14a0e9a..f1c97d26a 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -50,9 +50,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get collapsedHeight() { return 35; } // the collapsed height changes depending on the state of the presBox. We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up - @computed get presStatus() { - return this.presBox.presStatus; - } @computed get selectedArray() { return this.presBoxView?.selectedArray; } @@ -364,14 +361,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { }; @computed get recordingIsInOverlay() { - let isInOverlay = false; - DocListCast(Doc.MyOverlayDocs.data).forEach(doc => { - if (doc.slides === this.rootDoc) { - isInOverlay = true; - // return - } - }); - return isInOverlay; + return DocListCast(Doc.MyOverlayDocs.data).some(doc => doc.slides === this.rootDoc); } // a previously recorded video will have timecode defined @@ -502,16 +492,17 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { </Tooltip> ); } - if (this.indexInPres === 0) { + if (this.indexInPres !== 0) { items.push( <Tooltip key="arrow" title={<div className="dash-tooltip">{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}</div>}> <div className="slideButton" - onClick={() => (activeItem.groupWithUp = !activeItem.groupWithUp)} + onClick={() => (activeItem.groupWithUp = (NumCast(activeItem.groupWithUp) + 1) % 3)} style={{ zIndex: 1000 - this.indexInPres, fontWeight: 700, backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined, + outline: NumCast(activeItem.groupWithUp) > 1 ? 'solid black 1px' : undefined, height: activeItem.groupWithUp ? 53 : 18, transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined, }}> diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts index c6a222c3a..564829d54 100644 --- a/src/client/views/nodes/trails/PresEnums.ts +++ b/src/client/views/nodes/trails/PresEnums.ts @@ -1,6 +1,7 @@ export enum PresMovement { Zoom = 'zoom', Pan = 'pan', + Center = 'center', Jump = 'jump', None = 'none', } @@ -14,11 +15,15 @@ export enum PresEffect { Bounce = 'Bounce', Roll = 'Roll', None = 'None', +} + +export enum PresEffectDirection { Left = 'Enter from left', Right = 'Enter from right', Center = 'Enter from center', Top = 'Enter from Top', Bottom = 'Enter from bottom', + None = 'None', } export enum PresStatus { |
