diff options
| author | brynnchernosky <56202540+brynnchernosky@users.noreply.github.com> | 2022-10-28 10:10:04 -0400 |
|---|---|---|
| committer | brynnchernosky <56202540+brynnchernosky@users.noreply.github.com> | 2022-10-28 10:10:04 -0400 |
| commit | ceb338752aacc383c97a0e3a9b608365a1cf39b6 (patch) | |
| tree | d2f355b726a9b21950f332c0f65931d7d6eef515 /src/client/views/nodes/trails/PresBox.tsx | |
| parent | 5d6a0458b9d4f35e0c568a4d76d4fcab4e22f698 (diff) | |
| parent | 2fc88a931cb2fc3408297b000208990633445585 (diff) | |
merge
Diffstat (limited to 'src/client/views/nodes/trails/PresBox.tsx')
| -rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 420 |
1 files changed, 215 insertions, 205 deletions
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 847617378..4e8aed8a6 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -5,14 +5,15 @@ import { action, computed, IReactionDisposer, observable, ObservableSet, reactio 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, DocListCastAsync, FieldResult } from '../../../../fields/Doc'; -import { Copy } from '../../../../fields/FieldSymbols'; +import { Doc, DocListCast, FieldResult, 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 { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; @@ -29,6 +30,7 @@ import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; +import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresMovement, PresStatus } from './PresEnums'; @@ -36,15 +38,9 @@ export interface PinProps { audioRange?: boolean; activeFrame?: number; hidePresBox?: boolean; - pinWithView?: PinViewProps; - pinDocView?: boolean; // whether the current view specs of the document should be saved the pinned document - panelWidth?: number; // panel width and height of the document (used to compute the bounds of the pinned view area) - panelHeight?: number; -} - -export interface PinViewProps { - bounds: MarqueeViewBounds; - scale: number; + 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) } @observer @@ -54,22 +50,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } /** - * transitions & effects for documents - * @param renderDoc - * @param layoutDoc + * 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 */ - static renderEffectsDoc(renderDoc: any, layoutDoc: Doc, presDoc: Doc) { + public static AnimationEffect(renderDoc: JSX.Element, presEffectDoc: Doc) { const effectProps = { - left: presDoc.presEffectDirection === PresEffect.Left, - right: presDoc.presEffectDirection === PresEffect.Right, - top: presDoc.presEffectDirection === PresEffect.Top, - bottom: presDoc.presEffectDirection === PresEffect.Bottom, + left: presEffectDoc.presEffectDirection === PresEffect.Left, + right: presEffectDoc.presEffectDirection === PresEffect.Right, + top: presEffectDoc.presEffectDirection === PresEffect.Top, + bottom: presEffectDoc.presEffectDirection === PresEffect.Bottom, opposite: true, - delay: presDoc.presTransition, - // when: this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc, + delay: NumCast(presEffectDoc.presTransition), }; //prettier-ignore - switch (presDoc.presEffect) { + switch (StrCast(presEffectDoc.presEffect)) { default: case PresEffect.None: return renderDoc; case PresEffect.Zoom: return <Zoom {...effectProps}>{renderDoc}</Zoom>; @@ -81,18 +76,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>; } } - public static EffectsProvider(layoutDoc: Doc, renderDoc: any) { - return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ? PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex]) : renderDoc; - } private _disposers: { [name: string]: IReactionDisposer } = {}; public selectedArray = new ObservableSet<Doc>(); - constructor(props: any) { - super(props); - if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this)); - } - @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 @@ -155,6 +142,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } + isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentationTargetDoc === layoutDoc; clearSelectedArray = () => this.selectedArray.clear(); addToSelectedArray = (doc: Doc) => this.selectedArray.add(doc); removeFromSelectedArray = (doc: Doc) => this.selectedArray.delete(doc); @@ -199,7 +187,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this.layoutDoc._gridGap = 0; this.layoutDoc._yMargin = 0; this.turnOffEdit(true); - DocListCastAsync(Doc.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.MyTrails, 'data', this.rootDoc)); this._disposers.selection = reaction( () => SelectionManager.Views(), views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation() @@ -244,17 +231,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time // 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 = (activeNext: Doc) => { - const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null); - console.info('nextSlide', activeNext.title, targetNext?.title); - let nextSelected = this.itemIndex + 1; + 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) { - break; - } else { - console.log('Title: ' + this.childDocs[nextSelected].title); + if (this.childDocs[nextSelected].groupWithUp) { this.gotoDocument(nextSelected, this.activeItem, true); + } else { + break; } } }; @@ -274,11 +258,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { 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 - this.nextSlide(activeNext); + 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.gotoDocument(0, this.activeItem); + this.nextSlide(0); } + return this.itemIndex; }; // Called when the user activates 'back' - to move to the previous part of the pres. trail @@ -293,8 +280,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { let prevSelected = this.itemIndex; // Functionality for group with up let didZoom = activeItem.presMovement; - for (; !didZoom && prevSelected > 0 && this.childDocs[prevSelected].groupButton; prevSelected--) { - didZoom = this.childDocs[prevSelected].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 @@ -302,12 +289,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } else 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.gotoDocument(prevSelected, activeItem); + 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); } + return this.itemIndex; }; //The function that is called when a document is clicked or reached through next or back. @@ -351,7 +340,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } if (!group) this.clearSelectedArray(); this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array - this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list + this.turnOffEdit(); + this.navigateToActiveItem(); //Handles movement to element only when presTrail is list this.onHideDocument(); //Handles hide after/before } }); @@ -360,30 +350,70 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { 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].includes(target.type as any) && target.activeFrame === undefined; + 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 }; + return { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview }; } @action - static restoreTargetDocView(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) { + static restoreTargetDocView(bestTarget: Doc, activeItem: Doc) { const transTime = NumCast(activeItem.presTransition, 500); const presTransitionTime = `all ${transTime}ms`; - const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(bestTarget); + 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; - if (dataview) Doc.GetProto(bestTarget).data = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; - if (textview) Doc.GetProto(bestTarget).text = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; + 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] }); + } + } + 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) { + 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.x = data.x; + doc.y = data.y; + doc._width = data.w; + doc._height = data.h; + }); + setTimeout( + () => + 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; + }) + ), + transTime + 10 + ); + } if (pannable) { - bestTarget._panX = activeItem.presPinViewX; - bestTarget._panY = activeItem.presPinViewY; - bestTarget._viewScale = activeItem.presPinViewScale; 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] }; + bestTarget._panX = viewport.panX; + bestTarget._panY = viewport.panY; const dv = DocumentManager.Instance.getDocumentView(bestTarget); - dv && (bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0]))); + 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); + dv.ComponentView?.brushView?.(viewport); + } + } else { + bestTarget._panX = activeItem.presPinViewX; + bestTarget._panY = activeItem.presPinViewY; + activeItem.presMovement === 'zoom' && (bestTarget._viewScale = activeItem.presPinViewScale); } } return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10); @@ -394,46 +424,45 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { /// target doc when navigating to it. @action static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined, targetDoc: Doc) { - if (pinProps?.pinWithView) { - // If pinWithView option set then update scale and x / y props of slide - const bounds = pinProps.pinWithView.bounds; - pinDoc.presPinView = true; - pinDoc.presPinViewX = bounds.left + bounds.width / 2; - pinDoc.presPinViewY = bounds.top + bounds.height / 2; - pinDoc.presPinViewScale = pinProps.pinWithView.scale; - pinDoc.presPinViewBounds = new List<number>([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); - } - if (pinProps?.pinDocView) { - const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(pinDoc); - pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || textview || pinProps.activeFrame !== undefined; + const { scrollable, pannable, temporal, clippable, dataview, textview, poslayoutview } = this.pinDataTypes(pinDoc); + if (pinProps?.pinDocLayout) { + pinDoc.presPinLayout = true; pinDoc.presX = NumCast(targetDoc.x); pinDoc.presY = NumCast(targetDoc.y); - pinDoc.presRot = NumCast(targetDoc.jitterRotation); + pinDoc.presRot = NumCast(targetDoc.rotation); 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 (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1; - if (textview) pinDoc.presData = targetDoc.text instanceof ObjectField ? targetDoc.text[Copy]() : targetDoc.text; - if (dataview) pinDoc.presData = targetDoc.data instanceof ObjectField ? targetDoc.data[Copy]() : targetDoc.data; + 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) { - const panX = NumCast(pinDoc._panX); - const panY = NumCast(pinDoc._panY); - const pw = NumCast(pinProps.panelWidth); - const ph = NumCast(pinProps.panelHeight); - const ps = NumCast(pinDoc._viewScale, 1); - if (pw && ph && ps) { - pinDoc.presPinViewBounds = new List<number>([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]); - } - pinDoc.presPinViewX = panX; - pinDoc.presPinViewY = panY; - pinDoc.presPinViewScale = ps; + pinDoc.presPinViewX = NumCast(pinDoc._panX); + pinDoc.presPinViewY = NumCast(pinDoc._panY); + pinDoc.presPinViewScale = NumCast(pinDoc._viewScale, 1); + } + 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?.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.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]); + } } - _navTimer!: NodeJS.Timeout; + static _navTimer: NodeJS.Timeout; /** * This method makes sure that cursor navigates to the element that * has the option open and last in the group. @@ -442,7 +471,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { * a new tab. If presCollection is undefined it will open the document * on the right. */ - navigateToElement = async (curDoc: Doc) => { + navigateToActiveItem = () => { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null); @@ -450,7 +479,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { 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); - this.turnOffEdit(); // Handles the setting of presCollection if (includesDoc) { //Case 1: Pres collection should not change as it is already the same @@ -473,7 +501,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { self._eleArray.splice(0, self._eleArray.length, ...eleViewCache); }); const openInTab = (doc: Doc, finished?: () => void) => { - collectionDocView ? collectionDocView.props.addDocTab(doc, '') : this.props.addDocTab(doc, ''); + (collectionDocView ?? this).props.addDocTab(doc, ''); this.layoutDoc.presCollection = targetDoc; // this still needs some fixing setTimeout(resetSelection, 500); @@ -483,13 +511,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { finished?.(); } }; - if (activeItem.presPinView && DocCast(targetDoc.context)?._currentFrame === undefined) { + PresBox.NavigateToTarget(targetDoc, activeItem, openInTab, srcContext, includesDoc || tab ? undefined : 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.jitterRotation = NumCast(activeItem.presRot, NumCast(targetDoc.jitterRotation)); + 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); @@ -497,20 +529,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { // If openDocument is selected then it should open the document for the user if (activeItem.openDocument) { LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc); - } else if (targetDoc && curDoc.presMovement !== PresMovement.None && targetDoc) { + } else if (targetDoc && activeItem.presMovement !== PresMovement.None) { LightboxView.SetLightboxDoc(undefined); - const zooming = curDoc.presMovement !== PresMovement.Pan; - DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); + 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.presPinView) { - // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target - clearTimeout(this._navTimer); + 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) this._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem, false); + if (bestTarget) PresBox._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem); } - }; + } /** * Uses the viewfinder to progressivize through the different views of a single collection. @@ -601,42 +635,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc @action startAutoPres = (startSlide: number) => { - this.updateCurrentPresentation(); - let activeItem: Doc = this.activeItem; - let targetDoc: Doc = this.targetDoc; - let duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition); - const timer = (ms: number) => new Promise(res => (this._presTimer = setTimeout(res, ms))); - const load = async () => { - // Wrap the loop into an async function for this to work - for (var i = startSlide; i < this.childDocs.length; i++) { - activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); - targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null); - duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition); - if (duration < 100) { - duration = 2500; - } - if (NumCast(targetDoc.lastFrame) > 0) { - for (var f = 0; f < NumCast(targetDoc.lastFrame); f++) { - await timer(duration / NumCast(targetDoc.lastFrame)); - this.next(); - } - } - - await timer(duration); - this.next(); // then the created Promise can be awaited - if (i === this.childDocs.length - 1) { - setTimeout(() => { - clearTimeout(this._presTimer); - if (this.layoutDoc.presStatus === 'auto' && !this.layoutDoc.presLoop) this.layoutDoc.presStatus = PresStatus.Manual; - else if (this.layoutDoc.presLoop) this.startAutoPres(0); - }, duration); - } - } - }; this.layoutDoc.presStatus = PresStatus.Autoplay; this.startPresentation(startSlide); - this.gotoDocument(startSlide, activeItem); - load(); + 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) + ); + }; + + func(this.itemIndex); }; // The function pauses the auto presentation @@ -645,7 +656,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (this.layoutDoc.presStatus === PresStatus.Autoplay) { if (this._presTimer) clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; - this.layoutDoc.presLoop = false; this.childDocs.forEach(this.stopTempMedia); } }; @@ -653,7 +663,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { //The function that resets the presentation by removing every action done by it. It also //stops the presentaton. resetPresentation = () => { - this.rootDoc._itemIndex = 0; this.childDocs .map(doc => Cast(doc.presentationTargetDoc, Doc, null)) .filter(doc => doc instanceof Doc) @@ -692,8 +701,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { * @param startIndex: index that the presentation will start at */ startPresentation = (startIndex: number) => { - this.updateCurrentPresentation(); - this.childDocs.map(doc => { + this.childDocs.forEach(doc => { const tagDoc = doc.presentationTargetDoc as Doc; if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) { tagDoc.opacity = 0; @@ -702,6 +710,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { tagDoc.opacity = 0; } }); + this.gotoDocument(startIndex, this.activeItem); }; /** @@ -717,8 +726,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this.layoutDoc.presStatus = PresStatus.Edit; clearTimeout(this._presTimer); const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250); - this.rootDoc.y = pt[1] + 10; + 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); @@ -982,7 +991,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { break; case 'Spacebar': case ' ': - if (this.layoutDoc.presStatus === PresStatus.Manual) this.startAutoPres(this.itemIndex); + if (this.layoutDoc.presStatus === PresStatus.Manual) this.startOrPause(true); else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer); handled = true; break; @@ -1139,7 +1148,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { let scale = Number(number) / 100; if (change) scale += change; if (scale < 0.01) scale = 0.01; - if (scale > 1.5) scale = 1.5; + if (scale > 1) scale = 1; this.selectedArray.forEach(doc => (doc.presZoom = scale)); }; @@ -1225,7 +1234,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { max={max} value={value} className={`toolbar-slider ${active ? '' : 'none'}`} - onPointerDown={() => (this._batch = UndoManager.StartBatch('pres slider'))} + onPointerDown={e => { + this._batch = UndoManager.StartBatch('pres slider'); + e.stopPropagation(); + }} onPointerUp={() => this._batch?.end()} onChange={e => { e.stopPropagation(); @@ -1237,7 +1249,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { if (activeItem && targetDoc) { const type = targetDoc.type; const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5; - const zoom = activeItem.presZoom ? NumCast(activeItem.presZoom) * 100 : 75; + const zoom = NumCast(activeItem.presZoom, 1) * 100; let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2; if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration); const effect = this.activeItem.presEffect ? this.activeItem.presEffect : 'None'; @@ -1254,28 +1266,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { })}> <div className="ribbon-box"> Movement - {isPresCollection || (isPresCollection && isPinWithView) ? ( - <div className="ribbon-property" style={{ marginLeft: 0, height: 25, textAlign: 'left', paddingLeft: 5, paddingRight: 5, fontSize: 10 }}> - {this.scrollable ? 'Scroll to pinned view' : !isPinWithView ? 'No movement' : 'Pan & Zoom to pinned view'} - </div> - ) : ( - <div - className="presBox-dropdown" - onClick={action(e => { - e.stopPropagation(); - this._openMovementDropdown = !this._openMovementDropdown; - })} - style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> - {this.movementName(activeItem)} - <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' }}> - {presMovement(PresMovement.None)} - {presMovement(PresMovement.Zoom)} - {presMovement(PresMovement.Pan)} - {presMovement(PresMovement.Jump)} - </div> + <div + className="presBox-dropdown" + onClick={action(e => { + e.stopPropagation(); + this._openMovementDropdown = !this._openMovementDropdown; + })} + style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> + {this.movementName(activeItem)} + <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.Zoom)} + {presMovement(PresMovement.Pan)} + {isPresCollection || (isPresCollection && isPinWithView) ? null : presMovement(PresMovement.Jump)} </div> - )} + </div> <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}> <div className="presBox-subheading">Zoom (% screen filled)</div> <div className="ribbon-property"> @@ -1290,7 +1296,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> </div> - {inputter('0', '1', '150', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)} + {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> <div className="ribbon-property"> @@ -1423,7 +1429,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const clipStart: number = NumCast(activeItem.clipStart); - const clipEnd: number = NumCast(activeItem.clipEnd); + const clipEnd: number = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '-duration'])); const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc); if (activeItem && targetDoc) { return ( @@ -1440,9 +1446,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div id={'startTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}> <input className="presBox-input" - style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }} + style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }} type="number" - value={NumCast(activeItem.presStartTime)} + value={NumCast(activeItem.presStartTime).toFixed(2)} onKeyDown={e => e.stopPropagation()} onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); @@ -1466,9 +1472,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <input className="presBox-input" onKeyDown={e => e.stopPropagation()} - style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }} + style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }} type="number" - value={NumCast(activeItem.presEndTime)} + value={NumCast(activeItem.presEndTime).toFixed(2)} onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presEndTime = Number(e.target.value); })} @@ -1486,13 +1492,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { style={{ gridColumn: 1, gridRow: 1 }} className={`toolbar-slider ${'end'}`} id="toolbar-slider" - onPointerDown={() => { + onPointerDown={e => { this._batch = UndoManager.StartBatch('presEndTime'); const endBlock = document.getElementById('endTime'); if (endBlock) { endBlock.style.color = Colors.LIGHT_GRAY; endBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } + e.stopPropagation(); }} onPointerUp={() => { this._batch?.end(); @@ -1516,13 +1523,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { style={{ gridColumn: 1, gridRow: 1 }} className={`toolbar-slider ${'start'}`} id="toolbar-slider" - onPointerDown={() => { + onPointerDown={e => { this._batch = UndoManager.StartBatch('presStartTime'); const startBlock = document.getElementById('startTime'); if (startBlock) { startBlock.style.color = Colors.LIGHT_GRAY; startBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } + e.stopPropagation(); }} onPointerUp={() => { this._batch?.end(); @@ -1538,10 +1546,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }} /> </div> - <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}> - <div className="slider-text">{clipStart} s</div> + <div className="slider-headers"> + <div className="slider-text">{clipStart.toFixed(2)} s</div> <div className="slider-text"></div> - <div className="slider-text">{clipEnd} s</div> + <div className="slider-text">{clipEnd.toFixed(2)} s</div> </div> </div> <div className="ribbon-final-box"> @@ -1795,6 +1803,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { ); } + scrollFocus = () => { + 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 => { @@ -2320,7 +2333,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; return ( - <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? 'none' : undefined }}> + <div className="presBox-buttons" 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}> @@ -2456,35 +2469,37 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} - onClick={() => { + onClick={e => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + e.stopPropagation(); }}> <FontAwesomeIcon icon={'arrow-left'} /> </div> <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}> - <div className="presPanel-button" onClick={this.startOrPause}> + <div className="presPanel-button" onClick={e => this.startOrPause(true)}> <FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} /> </div> </Tooltip> <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} - onClick={() => { + onClick={e => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + e.stopPropagation(); }}> <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.gotoDocument(0, this.activeItem)}> + <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.nextSlide(0)}> <b>1</b> </div> </Tooltip> @@ -2514,7 +2529,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } @action - startOrPause = () => { + startOrPause = (makeActive = true) => { + makeActive && this.updateCurrentPresentation(); if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex); else this.pauseAutoPres(); }; @@ -2580,9 +2596,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { 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; - return DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc) ? ( + 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 className="presPanelOverlay" style={{ display: 'inline-flex', height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: this._presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}> + <div + className="presPanelOverlay" + style={{ display: 'inline-flex', height: 30, background: Doc.ActivePresentation === this.rootDoc ? 'green' : '#323232', top: 0, zIndex: 3000000, boxShadow: this._presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}> <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}> <div className="presPanel-button" @@ -2596,7 +2614,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <FontAwesomeIcon icon={'arrow-left'} /> </div> <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}> - <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}> + <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.startOrPause(true), false, false)}> <FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} /> </div> </Tooltip> @@ -2625,7 +2643,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { {this.toolbar} {this.newDocumentToolbarDropdown} <div className="presBox-listCont"> - <div className="Slide" style={{ height: `calc(100% - 30px)` }}> + <div className="Slide"> {mode !== CollectionViewType.Invalid ? ( <CollectionView {...this.props} @@ -2634,9 +2652,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { PanelHeight={this.panelHeight} childIgnoreNativeSize={true} moveDocument={returnFalse} - childFitWidth={returnTrue} + //childFitWidth={returnTrue} childOpacity={returnOne} childLayoutTemplate={this.childLayoutTemplate} + childXPadding={Doc.IsComicStyle(this.rootDoc) ? 20 : undefined} filterAddDocument={this.addDocumentFilter} removeDocument={returnFalse} dontRegisterView={true} @@ -2650,37 +2669,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { ) : null} </div> - { + {/* { // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack <Tooltip title={<div className="dash-tooltip">{'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}</div>}> <button className="add-slide-button" onPointerDown={this.startMarqueeCreateSlide}> + NEW SLIDE </button> </Tooltip> - } + } */} </div> </div> ); } + 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'); + finished?.(); + }; + PresBox.NavigateToTarget(bestTarget, activeItem, openInTab, srcContext); + } } ScriptingGlobals.add(function 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'); - finished?.(); - }; - DocumentManager.Instance.jumpToDocument( - bestTarget, - true, - openInTab, - srcContext ? [srcContext] : [], - undefined, - undefined, - undefined, - () => PresBox.restoreTargetDocView(bestTarget, activeItem, true), - undefined, - true, - NumCast(activeItem.presZoom) - ); + PresBox.NavigateToDoc(bestTarget, activeItem); }); |
