diff options
Diffstat (limited to 'src/client/views/nodes/VideoBox.tsx')
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 211 |
1 files changed, 134 insertions, 77 deletions
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 4773a21c9..afd73cfe8 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1,22 +1,22 @@ +/* eslint-disable jsx-a11y/media-has-caption */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { basename } from 'path'; import * as React from 'react'; +import { ClientUtils, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; import { Doc, Opt, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; import { AudioField, ImageField, VideoField } from '../../../fields/URLField'; -import { emptyFunction, formatTime, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { emptyFunction, formatTime } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; -import { DocumentManager } from '../../util/DocumentManager'; -import { dropActionType } from '../../util/DragManager'; -import { FollowLinkScript } from '../../util/LinkFollower'; -import { LinkManager } from '../../util/LinkManager'; +import { DocUtils, FollowLinkScript } from '../../documents/DocUtils'; +import { dropActionType } from '../../util/DropActionTypes'; import { ReplayMovements } from '../../util/ReplayMovements'; import { undoBatch } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -24,13 +24,15 @@ import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionS import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { VideoThumbnails } from '../global/globalEnums'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; -import { StyleProp } from '../StyleProvider'; +import { PinDocView, PinProps } from '../PinFuncs'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { RecordingBox } from './RecordingBox'; -import { PinProps, PresBox } from './trails'; import './VideoBox.scss'; /** @@ -51,7 +53,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl return FieldView.LayoutString(VideoBox, fieldKey); } static heightPercent = 80; // height of video relative to videoBox when timeline is open - static numThumbnails = 20; private unmounting = false; private _disposers: { [name: string]: IReactionDisposer } = {}; private _videoRef: HTMLVideoElement | null = null; // <video> ref @@ -62,6 +63,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _playRegionTimer: any = null; // timeout for playback private _controlsFadeTimer: any = null; // timeout for controls fade + private _ffref = React.createRef<CollectionFreeFormView>(); constructor(props: FieldViewProps) { super(props); @@ -84,7 +86,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl @observable _scrubbing: boolean = false; @computed get links() { - return LinkManager.Links(this.dataDoc); + return Doc.Links(this.dataDoc); } @computed get heightPercent() { return NumCast(this.layoutDoc._layout_timelineHeightPercent, 100); @@ -152,11 +154,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl clearTimeout(this._controlsFadeTimer); this._scrubbing = true; this._controlsFadeTimer = setTimeout( - action(() => (this._scrubbing = false)), + action(() => { + this._scrubbing = false; + }), 500 ); e.stopPropagation(); break; + default: } } }; @@ -203,7 +208,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl else { this._keepCurrentlyPlaying = true; this.pause(); - setTimeout(() => (this._keepCurrentlyPlaying = false)); + setTimeout(() => { + this._keepCurrentlyPlaying = false; + }); } }; @@ -246,7 +253,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl clearTimeout(this._controlsFadeTimer); this._controlsVisible = true; this._controlsFadeTimer = setTimeout( - action(() => (this._controlsVisible = false)), + action(() => { + this._controlsVisible = false; + }), 3000 ); } @@ -262,7 +271,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl setupMoveUpEvents( e.target, e, - action((e, down, delta) => { + action((moveEv, down, delta) => { if (this._controlsTransform) { this._controlsTransform.X = Math.max(0, Math.min(delta[0] + this._controlsTransform.X, window.innerWidth)); this._controlsTransform.Y = Math.max(0, Math.min(delta[1] + this._controlsTransform.Y, window.innerHeight)); @@ -280,7 +289,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl const canvas = document.createElement('canvas'); canvas.width = 640; canvas.height = (640 * Doc.NativeHeight(this.layoutDoc)) / (Doc.NativeWidth(this.layoutDoc) || 1); - const ctx = canvas.getContext('2d'); //draw image to canvas. scale to target dimensions + const ctx = canvas.getContext('2d'); // draw image to canvas. scale to target dimensions if (ctx) { this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height); } @@ -297,13 +306,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl this._props.addDocument?.(b); DocUtils.MakeLink(b, this.Document, { link_relationship: 'video snapshot' }); } else { - //convert to desired file format + // convert to desired file format const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png' // if you want to preview the captured image, - const retitled = StrCast(this.Document.title).replace(/[ -\.:]/g, ''); - const encodedFilename = encodeURIComponent(('snapshot' + retitled + '_' + (this.layoutDoc._layout_currentTimecode || 0).toString()).replace(/[\.\/\?\=]/g, '_')); + const retitled = StrCast(this.Document.title).replace(/[ -.:]/g, ''); + const encodedFilename = encodeURIComponent(('snapshot' + retitled + '_' + (this.layoutDoc._layout_currentTimecode || 0).toString()).replace(/[./?=]/g, '_')); const filename = basename(encodedFilename); - Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY)); + ClientUtils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createSnapshotLink)(returnedFilename, downX, downY)); } }; @@ -318,7 +327,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl // creates link for snapshot createSnapshotLink = (imagePath: string, downX?: number, downY?: number) => { - const url = !imagePath.startsWith('/') ? Utils.CorsProxy(imagePath) : imagePath; + const url = !imagePath.startsWith('/') ? ClientUtils.CorsProxy(imagePath) : imagePath; const width = NumCast(this.layoutDoc._width) || 1; const height = NumCast(this.layoutDoc._height); const imageSnapshot = Docs.Create.ImageDocument(url, { @@ -334,9 +343,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl Doc.SetNativeWidth(imageSnapshot[DocData], Doc.NativeWidth(this.layoutDoc)); Doc.SetNativeHeight(imageSnapshot[DocData], Doc.NativeHeight(this.layoutDoc)); this._props.addDocument?.(imageSnapshot); - const link = DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' }); + DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' }); // link && (DocCast(link.link_anchor_2)[DocData].timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3); // do we need to set an end time? should default to +0.1 - setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true)); + setTimeout(() => downX !== undefined && downY !== undefined && DocumentView.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true)); }; getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { @@ -345,9 +354,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent'; const anchor = addAsAnnotation && marquee - ? CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || this.Document + ? CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode || undefined, undefined, marquee, addAsAnnotation) || this.Document : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true, pannable: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true, pannable: true } }, this.Document); return anchor; }; @@ -375,12 +384,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl if (this._stackedTimeline?.makeDocUnfiltered(doc)) { if (this.heightPercent === 100) { // do we want to always open up the timeline when followin a link? kind of clunky visually - //this.layoutDoc._layout_timelineHeightPercent = VideoBox.heightPercent; + // this.layoutDoc._layout_timelineHeightPercent = VideoBox.heightPercent; options.didMove = true; } return this._stackedTimeline.getView(doc, options); } - return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + return new Promise<Opt<DocumentView>>(res => { + DocumentView.addViewRenderedCb(doc, dv => res(dv)); + }); }; // extracts video thumbnails and saves them as field of doc @@ -390,21 +401,25 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl const thumbnailPromises: Promise<any>[] = []; const video = document.createElement('video'); - video.onloadedmetadata = () => (video.currentTime = 0); + video.onloadedmetadata = () => { + video.currentTime = 0; + }; video.onseeked = () => { const canvas = document.createElement('canvas'); canvas.height = 100; canvas.width = 100; canvas.getContext('2d')?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, 100, 100); - const retitled = StrCast(this.Document.title).replace(/[ -\.:]/g, ''); + const retitled = StrCast(this.Document.title).replace(/[ -.:]/g, ''); const encodedFilename = encodeURIComponent('thumbnail' + retitled + '_' + video.currentTime.toString().replace(/\./, '_')); - thumbnailPromises?.push(Utils.convertDataUri(canvas.toDataURL(), basename(encodedFilename), true)); - const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1); + thumbnailPromises?.push(ClientUtils.convertDataUri(canvas.toDataURL(), basename(encodedFilename), true)); + const newTime = video.currentTime + video.duration / (VideoThumbnails.DENSE - 1); if (newTime < video.duration) { video.currentTime = newTime; } else { - Promise.all(thumbnailPromises).then(thumbnails => (this.dataDoc[this.fieldKey + '_thumbnails'] = new List<string>(thumbnails))); + Promise.all(thumbnailPromises).then(thumbnails => { + this.dataDoc[this.fieldKey + '_thumbnails'] = new List<string>(thumbnails); + }); } }; @@ -423,11 +438,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl this._disposers.reactionDisposer?.(); this._disposers.reactionDisposer = reaction( () => NumCast(this.layoutDoc._layout_currentTimecode), - time => !this._playing && (vref.currentTime = time), + time => { + !this._playing && (vref.currentTime = time); + }, { fireImmediately: true } ); - (!this.dataDoc[this.fieldKey + '_thumbnails'] || StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']).length != VideoBox.numThumbnails) && this.getVideoThumbnails(); + (!this.dataDoc[this.fieldKey + '_thumbnails'] || StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']).length !== VideoThumbnails.DENSE) && this.getVideoThumbnails(); } }; @@ -436,7 +453,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl setContentRef = (cref: HTMLDivElement | null) => { this._contentRef = cref; if (cref) { - cref.onfullscreenchange = action(e => { + cref.onfullscreenchange = action(() => { this._fullScreen = document.fullscreenElement === cref; this._controlsVisible = true; this._scrubbing = false; @@ -451,7 +468,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl }; // context menu - specificContextMenu = (e: React.MouseEvent): void => { + specificContextMenu = (): void => { const field = Cast(this.dataDoc[this._props.fieldKey], VideoField); if (field) { const url = field.url.href; @@ -462,25 +479,41 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl subitems.push({ description: 'Screen Capture', event: async () => { - runInAction(() => (this._screenCapture = !this._screenCapture)); + runInAction(() => { + this._screenCapture = !this._screenCapture; + }); this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); }, icon: 'expand-arrows-alt', }); - subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? '' : "Don't") + ' follow links when encountered', event: () => (this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks), icon: 'expand-arrows-alt' }); + subitems.push({ + description: (this.layoutDoc.dontAutoFollowLinks ? '' : "Don't") + ' follow links when encountered', + event: () => { + this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks; + }, + icon: 'expand-arrows-alt', + }); subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? '' : "Don't") + ' play when link is selected', - event: () => (this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks), + event: () => { + this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks; + }, + icon: 'expand-arrows-alt', + }); + subitems.push({ + description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : 'Auto play') + ' anchors onClick', + event: () => { + this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors; + }, icon: 'expand-arrows-alt', }); - subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : 'Auto play') + ' anchors onClick', event: () => (this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors), icon: 'expand-arrows-alt' }); // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" }); // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" }); // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" }); subitems.push({ description: 'Copy path', event: () => { - Utils.CopyText(url); + ClientUtils.CopyText(url); }, icon: 'expand-arrows-alt', }); @@ -504,7 +537,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl }; // ref for updating time - setAudioRef = (e: HTMLAudioElement | null) => (this._audioPlayer = e); + setAudioRef = (e: HTMLAudioElement | null) => { + this._audioPlayer = e; + }; // renders the video and audio @computed get content() { @@ -570,8 +605,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl setupMoveUpEvents( this, e, - e => { - this.Snapshot(e.clientX, e.clientY); + moveEv => { + this.Snapshot(moveEv.clientX, moveEv.clientY); return true; }, emptyFunction, @@ -586,7 +621,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl setupMoveUpEvents( this, e, - action(encodeURIComponent => { + action(() => { this._clicking = false; if (this._props.isContentActive()) { // const local = this.ScreenToLocalTransform().scale(this._props.scaling?.() || 1).transformPoint(e.clientX, e.clientY); @@ -600,7 +635,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl () => { this.layoutDoc._layout_timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent; setTimeout( - action(() => (this._clicking = false)), + action(() => { + this._clicking = false; + }), 500 ); }, @@ -613,30 +650,32 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl @action removeCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (CollectionStackedTimeline.CurrentlyPlaying && docView) { - const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView); - index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); + if (DocumentView.CurrentlyPlaying && docView) { + const index = DocumentView.CurrentlyPlaying.indexOf(docView); + index !== -1 && DocumentView.CurrentlyPlaying.splice(index, 1); } }; // adds doc to currently playing display @action addCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (!CollectionStackedTimeline.CurrentlyPlaying) { - CollectionStackedTimeline.CurrentlyPlaying = []; + if (!DocumentView.CurrentlyPlaying) { + DocumentView.CurrentlyPlaying = []; } - if (docView && CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView) === -1) { - CollectionStackedTimeline.CurrentlyPlaying.push(docView); + if (docView && DocumentView.CurrentlyPlaying.indexOf(docView) === -1) { + DocumentView.CurrentlyPlaying.push(docView); } }; // for annotating, adds doc with time info @action.bound - addDocWithTimecode(doc: Doc | Doc[]): boolean { - const docs = doc instanceof Doc ? [doc] : doc; + addDocWithTimecode(docIn: Doc | Doc[]): boolean { + const docs = toList(docIn); const curTime = NumCast(this.layoutDoc._layout_currentTimecode); - docs.forEach(doc => (doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1)); - return this.addDocument(doc); + docs.forEach(doc => { + doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1; + }); + return this.addDocument(docs); } // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range @@ -644,7 +683,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => { clearTimeout(this._playRegionTimer); this._playRegionTimer = undefined; - if (Number.isNaN(this.player?.duration)) { + if (this.player?.duration === undefined || isNaN(this.player.duration)) { setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500); } else if (this.player) { // trimBounds override requested playback bounds @@ -696,7 +735,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl e, returnFalse, returnFalse, - action((e: PointerEvent, doubleTap?: boolean) => { + action((clickEv: PointerEvent, doubleTap?: boolean) => { if (doubleTap) { this.startTrim(TrimScope.All); } else if (this.timeline) { @@ -731,11 +770,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl // stretches vertically or horizontally depending on video orientation so video fits full screen fullScreenSize() { if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) { - //prettier-ignore - return ({ height: '100%' }); + return { height: '100%' }; } - //prettier-ignore - return ({ width: '100%' }); + return ({ width: '100%' }); // prettier-ignore } // for zoom slider, sets timeline waveform zoom @@ -757,9 +794,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl setupMoveUpEvents( this, e, - action(e => { + action(moveEv => { MarqueeAnnotator.clearAnnotations(this._savedAnnotations); - this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]); + this._marqueeref.current?.onInitiateSelection([moveEv.clientX, moveEv.clientY]); return true; }), returnFalse, @@ -777,7 +814,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl this._props.select(true); }; - timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive))); + timelineWhenChildContentsActiveChanged = action((isActive: boolean) => { + this._isAnyChildContentActive = isActive; + this._props.whenChildContentsActiveChanged(isActive); + }); timelineScreenToLocal = () => this._props @@ -785,7 +825,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl .scale(this.scaling()) .translate(0, (-this.heightPercent / 100) * this._props.PanelHeight()); - setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._layout_currentTimecode = time); + setPlayheadTime = (time: number) => { + this.player!.currentTime = this.layoutDoc._layout_currentTimecode = time; + }; timelineHeight = () => (this._props.PanelHeight() * (100 - this.heightPercent)) / 100; @@ -806,7 +848,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0]; - timelineDocFilter = () => [`_isTimelineLabel:true,${Utils.noRecursionHack}:x`]; + timelineDocFilter = () => [`_isTimelineLabel:true,${ClientUtils.noRecursionHack}:x`]; // renders video controls componentUI = (boundsLeft: number, boundsTop: number) => { @@ -848,7 +890,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl return ( <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%`, display: this.heightPercent === 100 ? 'none' : '' }}> <CollectionStackedTimeline - ref={action((r: any) => (this._stackedTimeline = r))} + ref={action((r: any) => { + this._stackedTimeline = r; + })} + // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} dataFieldKey={this.fieldKey} fieldKey={this.annotationKey} @@ -886,7 +931,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl } crop = (region: Doc | undefined, addCrop?: boolean) => { - if (!region) return; + if (!region) return undefined; const cropping = Doc.MakeCopy(region, true); const regionData = region[DocData]; regionData.backgroundColor = 'transparent'; @@ -915,8 +960,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl croppingProto.type = DocumentType.VID; croppingProto.layout = VideoBox.LayoutString('data'); croppingProto.data = ObjectField.MakeCopy(this.dataDoc[this.fieldKey] as ObjectField); - croppingProto['data_nativeWidth'] = anchw; - croppingProto['data_nativeHeight'] = anchh; + croppingProto.data_nativeWidth = anchw; + croppingProto.data_nativeHeight = anchh; croppingProto.videoCrop = true; croppingProto.layout_currentTimecode = this.layoutDoc._layout_currentTimecode; croppingProto.freeform_scale = viewScale; @@ -933,6 +978,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl this._props.bringToFront?.(cropping); return cropping; }; + focus = (anchor: Doc, options: FocusViewOptions) => (anchor.type === DocumentType.CONFIG ? undefined : this._ffref.current?.focus(anchor, options)); savedAnnotations = () => this._savedAnnotations; render() { const borderRad = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding); @@ -958,14 +1004,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl left: (this._props.PanelWidth() - this.panelWidth()) / 2, }}> <CollectionFreeFormView + // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} + ref={this._ffref} setContentViewBox={emptyFunction} NativeWidth={returnZero} NativeHeight={returnZero} renderDepth={this._props.renderDepth + 1} fieldKey={this.annotationKey} - isAnnotationOverlay={true} - annotationLayerHostsContent={true} + isAnnotationOverlay + annotationLayerHostsContent PanelWidth={this._props.PanelWidth} PanelHeight={this._props.PanelHeight} isAnyChildContentActive={returnFalse} @@ -1049,12 +1097,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl </div> )} - <div className="videobox-button" title={'full screen'} onPointerDown={this.onFullDown}> + <div className="videobox-button" title="full screen" onPointerDown={this.onFullDown}> <FontAwesomeIcon icon="expand" /> </div> {!this._fullScreen && width > 300 && ( - <div className="videobox-button" title={'show timeline'} onPointerDown={this.onTimelineHdlDown}> + <div className="videobox-button" title="show timeline" onPointerDown={this.onTimelineHdlDown}> <FontAwesomeIcon icon="eye" /> </div> )} @@ -1113,3 +1161,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() impl ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.VID, { + layout: { view: VideoBox, dataField: 'data' }, + options: { acl: '', _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' }, +}); +Docs.Prototypes.TemplateMap.set(DocumentType.REC, { + layout: { view: VideoBox, dataField: 'data' }, + options: { acl: '', _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' }, +}); |