diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/GlobalKeyHandler.ts | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackedTimeline.scss (renamed from src/client/views/nodes/StackedTimeline.scss) | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackedTimeline.tsx (renamed from src/client/views/nodes/StackedTimeline.tsx) | 83 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 49 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 27 |
6 files changed, 91 insertions, 78 deletions
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index a07ba0a77..e56ba38dd 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -24,7 +24,7 @@ import { DocumentDecorations } from "./DocumentDecorations"; import { InkStrokeProperties } from "./InkStrokeProperties"; import { MainView } from "./MainView"; import { DocumentLinksButton } from "./nodes/DocumentLinksButton"; -import { StackedTimeline } from "./nodes/StackedTimeline"; +import { CollectionStackedTimeline } from "./collections/CollectionStackedTimeline"; import { AnchorMenu } from "./pdf/AnchorMenu"; import { SearchBox } from "./search/SearchBox"; @@ -121,8 +121,8 @@ export class KeyManager { DragManager.AbortDrag(); } else if (CollectionDockingView.Instance.HasFullScreen) { CollectionDockingView.Instance.CloseFullScreen(); - } else if (StackedTimeline.SelectingRegion) { - StackedTimeline.SelectingRegion = undefined; + } else if (CollectionStackedTimeline.SelectingRegion) { + CollectionStackedTimeline.SelectingRegion = undefined; doDeselect = false; } else { doDeselect = !ContextMenu.Instance.closeMenu(); diff --git a/src/client/views/nodes/StackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index da7310794..1bb5bc720 100644 --- a/src/client/views/nodes/StackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -350,7 +350,6 @@ } } - @media only screen and (max-device-width: 480px) { .audiobox-dictation { font-size: 5em; diff --git a/src/client/views/nodes/StackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 808ca982d..1775250fa 100644 --- a/src/client/views/nodes/StackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -2,27 +2,24 @@ import React = require("react"); import { action, computed, IReactionDisposer, observable } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; -import { Doc, DocListCast, Opt } from "../../../fields/Doc"; +import { Doc, Opt } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; +import { listSpec, makeInterface } from "../../../fields/Schema"; import { ComputedField, ScriptField } from "../../../fields/ScriptField"; import { Cast, NumCast } from "../../../fields/Types"; import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; -import { Transform } from "../../util/Transform"; -import "./StackedTimeline.scss"; -import { DocumentView, DocumentViewProps } from "./DocumentView"; -import { LabelBox } from "./LabelBox"; +import { CollectionSubView } from "../collections/CollectionSubView"; +import { DocumentView } from "../nodes/DocumentView"; +import { LabelBox } from "../nodes/LabelBox"; +import "./CollectionStackedTimeline.scss"; -export interface StackedTimelineProps { - Document: Doc; - dataDoc: Doc; - anchorProps: DocumentViewProps; - renderDepth: number; - annotationKey: string; +type PanZoomDocument = makeInterface<[]>; +const PanZoomDocument = makeInterface(); +export type CollectionStackedTimelineProps = { duration: number; Play: () => void; Pause: () => void; @@ -30,19 +27,11 @@ export interface StackedTimelineProps { playFrom: (seekTimeInSeconds: number, endTime?: number) => void; playing: () => boolean; setTime: (time: number) => void; - select: (ctrlKey: boolean) => void; - isSelected: (outsideReaction: boolean) => boolean; - whenActiveChanged: (isActive: boolean) => void; - removeDocument: (doc: Doc | Doc[]) => boolean; - ScreenToLocalTransform: () => Transform; isChildActive: () => boolean; - active: () => boolean; - PanelWidth: () => number; - PanelHeight: () => number; -} +}; @observer -export class StackedTimeline extends React.Component<StackedTimelineProps> { +export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument, CollectionStackedTimelineProps>(PanZoomDocument) { static RangeScript: ScriptField; static LabelScript: ScriptField; static RangePlayScript: ScriptField; @@ -60,19 +49,19 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { _markerStart: number = 0; _currAnchor: Opt<Doc>; - @observable static SelectingRegion: StackedTimeline | undefined = undefined; + @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined; @observable _markerEnd: number = 0; @observable _position: number = 0; - @computed get anchorDocs() { return DocListCast(this.props.dataDoc[this.props.annotationKey]); } + @computed get anchorDocs() { return this.childDocs; } @computed get currentTime() { return NumCast(this.props.Document._currentTimecode); } - constructor(props: Readonly<StackedTimelineProps>) { + constructor(props: any) { super(props); // onClick play scripts - StackedTimeline.RangeScript = StackedTimeline.RangeScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this)`, { self: Doc.name, scriptContext: "any" })!; - StackedTimeline.LabelScript = StackedTimeline.LabelScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this)`, { self: Doc.name, scriptContext: "any" })!; - StackedTimeline.RangePlayScript = StackedTimeline.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this)`, { self: Doc.name, scriptContext: "any" })!; - StackedTimeline.LabelPlayScript = StackedTimeline.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this)`, { self: Doc.name, scriptContext: "any" })!; + CollectionStackedTimeline.RangeScript = CollectionStackedTimeline.RangeScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this)`, { self: Doc.name, scriptContext: "any" })!; + CollectionStackedTimeline.LabelScript = CollectionStackedTimeline.LabelScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this)`, { self: Doc.name, scriptContext: "any" })!; + CollectionStackedTimeline.RangePlayScript = CollectionStackedTimeline.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this)`, { self: Doc.name, scriptContext: "any" })!; + CollectionStackedTimeline.LabelPlayScript = CollectionStackedTimeline.LabelPlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this)`, { self: Doc.name, scriptContext: "any" })!; } // for creating key anchors with key events @@ -95,14 +84,14 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { } } - anchorStart = (anchor: Doc) => NumCast(anchor.anchorStartTime, NumCast(anchor._timecodeToShow, NumCast(anchor.videoStart))) - anchorEnd = (anchor: Doc, defaultVal: any = null) => NumCast(anchor.anchorEndTime, NumCast(anchor._timecodeToHide, NumCast(anchor.videoEnd, defaultVal))) + anchorStart = (anchor: Doc) => NumCast(anchor.anchorStartTime, NumCast(anchor._timecodeToShow, NumCast(anchor.videoStart))); + anchorEnd = (anchor: Doc, defaultVal: any = null) => NumCast(anchor.anchorEndTime, NumCast(anchor._timecodeToHide, NumCast(anchor.videoEnd, defaultVal))); getLinkData(l: Doc) { let la1 = l.anchor1 as Doc; let la2 = l.anchor2 as Doc; const linkTime = NumCast(la2.anchorStartTime, NumCast(la1.anchorStartTime)); - if (Doc.AreProtosEqual(la1, this.props.dataDoc)) { + if (Doc.AreProtosEqual(la1, this.dataDoc)) { la1 = l.anchor2 as Doc; la2 = l.anchor1 as Doc; } @@ -126,7 +115,7 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { } @computed get selectionContainer() { - return StackedTimeline.SelectingRegion !== this ? (null) : <div className="audiobox-container" style={{ + return CollectionStackedTimeline.SelectingRegion !== this ? (null) : <div className="audiobox-container" style={{ left: `${Math.min(NumCast(this._markerStart), NumCast(this._markerEnd)) / this.props.duration * 100}%`, width: `${Math.abs(this._markerStart - this._markerEnd) / this.props.duration * 100}%`, height: "100%", top: "0%" }} />; @@ -146,7 +135,7 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { }, 300); } this._markerStart = this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width); - StackedTimeline.SelectingRegion = this; + CollectionStackedTimeline.SelectingRegion = this; setupMoveUpEvents(this, e, action(e => { this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width); @@ -159,8 +148,8 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { this._markerStart = this._markerEnd; this._markerEnd = tmp; } - StackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createAnchor(this._markerStart, this._markerEnd); - StackedTimeline.SelectingRegion = undefined; + CollectionStackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createAnchor(this._markerStart, this._markerEnd); + CollectionStackedTimeline.SelectingRegion = undefined; }), (e, doubleTap) => { this.props.select(false); @@ -182,10 +171,10 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { anchorEndTime, annotationOn: this.props.Document }); - if (Cast(this.props.dataDoc[this.props.annotationKey], listSpec(Doc), null) !== undefined) { - Cast(this.props.dataDoc[this.props.annotationKey], listSpec(Doc), []).push(anchor); + if (Cast(this.dataDoc[this.props.fieldKey], listSpec(Doc), null) !== undefined) { + Cast(this.dataDoc[this.props.fieldKey], listSpec(Doc), []).push(anchor); } else { - this.props.dataDoc[this.props.annotationKey] = new List<Doc>([anchor]); + this.dataDoc[this.props.fieldKey] = new List<Doc>([anchor]); } return anchor; } @@ -225,10 +214,10 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { emptyFunction); } - rangeClickScript = () => StackedTimeline.RangeScript; - labelClickScript = () => StackedTimeline.LabelScript; - rangePlayScript = () => StackedTimeline.RangePlayScript; - labelPlayScript = () => StackedTimeline.LabelPlayScript; + rangeClickScript = () => CollectionStackedTimeline.RangeScript; + labelClickScript = () => CollectionStackedTimeline.LabelScript; + rangePlayScript = () => CollectionStackedTimeline.RangePlayScript; + labelPlayScript = () => CollectionStackedTimeline.LabelPlayScript; // makes sure no anchors overlaps each other by setting the correct position and width getLevel = (m: Doc, placed: { anchorStartTime: number, anchorEndTime: number, level: number }[]) => { @@ -252,10 +241,10 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { return level; } - renderInner = computedFn(function (this: StackedTimeline, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) { + renderInner = computedFn(function (this: CollectionStackedTimeline, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) { const anchor = observable({ view: undefined as any }); return { - anchor, view: <DocumentView key="view" {...OmitKeys(this.props.anchorProps, ["NativeWidth", "NativeHeight"]).omit} + anchor, view: <DocumentView key="view" {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} ref={action((r: DocumentView | null) => anchor.view = r)} Document={mark} DataDoc={undefined} @@ -278,7 +267,7 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { scriptContext={this} /> }; }); - renderAnchor = computedFn(function (this: StackedTimeline, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) { + renderAnchor = computedFn(function (this: CollectionStackedTimeline, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) { const inner = this.renderInner(mark, script, doublescript, x, y, width, height); return <> {inner.view} @@ -329,7 +318,7 @@ export class StackedTimeline extends React.Component<StackedTimelineProps> { <div className="audiobox-current" ref={this._audioRef} onClick={e => { e.stopPropagation(); e.preventDefault(); }} style={{ left: `${this.currentTime / this.props.duration * 100}%`, pointerEvents: "none" }} /> - </div> + </div>; } } Scripting.addGlobal(function formatToTime(time: number): any { return formatTime(time); });
\ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 6b9b1a3c0..03d8606d7 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -59,7 +59,8 @@ export enum CollectionViewType { //Staff = "staff", Map = "map", Grid = "grid", - Pile = "pileup" + Pile = "pileup", + StackedTimeline = "stacked timeline" } export interface CollectionViewProps extends FieldViewProps { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index f509bfd64..c8bec74fb 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -9,14 +9,15 @@ import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { documentSchema } from "../../../fields/documentSchemas"; import { List } from "../../../fields/List"; import { createSchema, listSpec, makeInterface } from "../../../fields/Schema"; -import { ComputedField, ScriptField } from "../../../fields/ScriptField"; +import { ComputedField } from "../../../fields/ScriptField"; import { Cast, NumCast } from "../../../fields/Types"; import { AudioField, nullAudio } from "../../../fields/URLField"; -import { formatTime, numberRange, Utils } from "../../../Utils"; +import { emptyFunction, formatTime, numberRange, Utils } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; import { Networking } from "../../Network"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { SnappingManager } from "../../util/SnappingManager"; +import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; @@ -24,7 +25,6 @@ import "./AudioBox.scss"; import { FieldView, FieldViewProps } from './FieldView'; import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment"; import { LinkDocPreview } from "./LinkDocPreview"; -import { StackedTimeline } from "./StackedTimeline"; declare class MediaRecorder { // whatever MediaRecorder has constructor(e: any); @@ -46,7 +46,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD _disposers: { [name: string]: IReactionDisposer } = {}; _ele: HTMLAudioElement | null = null; _audioRef = React.createRef<HTMLDivElement>(); - _stackedTimeline = React.createRef<StackedTimeline>(); + _stackedTimeline = React.createRef<CollectionStackedTimeline>(); _recorder: any; _recordStart = 0; _pauseStart = 0; @@ -327,30 +327,43 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD ); } - playing = () => { return this.audioState === "playing"; } + playing = () => this.audioState === "playing"; playLink = (link: Doc) => { if (link.annotationOn === this.rootDoc) { if (this.layoutDoc.playOnSelect) this.playFrom(this._stackedTimeline.current?.anchorStart(link) || 0, this._stackedTimeline.current?.anchorEnd(link)); else this._ele!.currentTime = this.layoutDoc._currentTimecode = (this._stackedTimeline.current?.anchorStart(link) || 0); } - else this.links.filter(l => l.anchor1 === link || l.anchor2 === link).forEach(l => { - const { la1, la2 } = this.getLinkData(l); - const startTime = NumCast(la1.anchorStartTime, NumCast(la2.anchorStartTime, null)); - const endTime = NumCast(la1.anchorEndTime, NumCast(la2.anchorEndTime, null)); - if (startTime !== undefined) { - if (this.layoutDoc.playOnSelect) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime); - else this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime; - } - }); + else { + this.links.filter(l => l.anchor1 === link || l.anchor2 === link).forEach(l => { + const { la1, la2 } = this.getLinkData(l); + const startTime = NumCast(la1.anchorStartTime, NumCast(la2.anchorStartTime, null)); + const endTime = NumCast(la1.anchorEndTime, NumCast(la2.anchorEndTime, null)); + if (startTime !== undefined) { + if (this.layoutDoc.playOnSelect) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime); + else this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime; + } + }); + } } @computed get renderTimeline() { - return <StackedTimeline ref={this._stackedTimeline} + return <CollectionStackedTimeline ref={this._stackedTimeline} Document={this.props.Document} - dataDoc={this.dataDoc} - anchorProps={this.props} + fieldKey={this.annotationKey} renderDepth={this.props.renderDepth + 1} - annotationKey={this.annotationKey} + parentActive={this.props.parentActive} + focus={emptyFunction} + styleProvider={this.props.styleProvider} + docFilters={this.props.docFilters} + docRangeFilters={this.props.docRangeFilters} + searchFilterDocs={this.props.searchFilterDocs} + rootSelected={this.props.rootSelected} + addDocTab={this.props.addDocTab} + pinToPres={this.props.pinToPres} + bringToFront={emptyFunction} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} + ContainingCollectionView={this.props.ContainingCollectionView} + CollectionView={undefined} duration={this.duration} playFrom={this.playFrom} setTime={(time: number) => this._ele!.currentTime = this.layoutDoc._currentTimecode = time} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index f89f54309..bfac7dc1c 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -16,6 +16,7 @@ import { Networking } from "../../Network"; import { SelectionManager } from "../../util/SelectionManager"; import { SnappingManager } from "../../util/SnappingManager"; import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; +import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; import { ViewBoxAnnotatableComponent } from "../DocComponent"; @@ -25,7 +26,6 @@ import { StyleProp } from "../StyleProvider"; import { FieldView, FieldViewProps } from './FieldView'; import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment"; import { LinkDocPreview } from "./LinkDocPreview"; -import { StackedTimeline } from "./StackedTimeline"; import "./VideoBox.scss"; const path = require('path'); @@ -46,7 +46,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD private _videoRef: HTMLVideoElement | null = null; private _youtubeIframeId: number = -1; private _youtubeContentCreated = false; - private _stackedTimeline = React.createRef<StackedTimeline>(); + private _stackedTimeline = React.createRef<CollectionStackedTimeline>(); private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef(); private _playRegionTimer: any = null; @@ -72,8 +72,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD VideoBox.Instance = this; } - anchorStart = (anchor: Doc) => NumCast(anchor.anchorStartTime, NumCast(anchor._timecodeToShow, NumCast(anchor.videoStart))) - anchorEnd = (anchor: Doc, defaultVal: any = null) => NumCast(anchor.anchorEndTime, NumCast(anchor._timecodeToHide, NumCast(anchor.videoEnd, defaultVal))) + anchorStart = (anchor: Doc) => NumCast(anchor.anchorStartTime, NumCast(anchor._timecodeToShow, NumCast(anchor.videoStart))); + anchorEnd = (anchor: Doc, defaultVal: any = null) => NumCast(anchor.anchorEndTime, NumCast(anchor._timecodeToHide, NumCast(anchor.videoEnd, defaultVal))); getAnchor = () => { return this._stackedTimeline.current?.createAnchor(Cast(this.layoutDoc._currentTimecode, "number", null)) || this.rootDoc; @@ -484,12 +484,23 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD // returns the timeline @computed get renderTimeline() { return <div style={{ width: "100%", height: `${100 - this.heightPercent}%`, position: "absolute" }}> - <StackedTimeline ref={this._stackedTimeline} + <CollectionStackedTimeline ref={this._stackedTimeline} Document={this.props.Document} - dataDoc={this.dataDoc} - anchorProps={this.props} + fieldKey={this.annotationKey} renderDepth={this.props.renderDepth + 1} - annotationKey={this.annotationKey + "-timeline"} + parentActive={this.props.parentActive} + focus={emptyFunction} + styleProvider={this.props.styleProvider} + docFilters={this.props.docFilters} + docRangeFilters={this.props.docRangeFilters} + searchFilterDocs={this.props.searchFilterDocs} + rootSelected={this.props.rootSelected} + addDocTab={this.props.addDocTab} + pinToPres={this.props.pinToPres} + bringToFront={emptyFunction} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} + ContainingCollectionView={this.props.ContainingCollectionView} + CollectionView={undefined} duration={this.duration} playFrom={this.playFrom} setTime={(time: number) => this.player!.currentTime = this.layoutDoc._currentTimecode = time} |