aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/VideoBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/VideoBox.tsx')
-rw-r--r--src/client/views/nodes/VideoBox.tsx211
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' },
+});