aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/DocComponent.tsx2
-rw-r--r--src/client/views/MarqueeAnnotator.tsx6
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx16
-rw-r--r--src/client/views/nodes/AudioBox.tsx9
-rw-r--r--src/client/views/nodes/VideoBox.tsx24
-rw-r--r--src/client/views/nodes/formattedText/nodes_rts.ts2
6 files changed, 40 insertions, 19 deletions
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 99f13295d..b800ba777 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -74,6 +74,7 @@ export interface ViewBoxAnnotatableProps {
fieldKey: string;
layerProvider?: (doc: Doc) => boolean;
active: () => boolean;
+ select: (isCtrlPressed: boolean) => void;
whenActiveChanged: (isActive: boolean) => void;
isSelected: (outsideReaction?: boolean) => boolean;
rootSelected: (outsideReaction?: boolean) => boolean;
@@ -145,6 +146,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T
Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true);
});
+ this.props.select(false);
return true;
}
}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 8ef69802b..4ffccbc90 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -21,6 +21,7 @@ export interface MarqueeAnnotatorProps {
rootDoc: Doc;
down: number[];
scaling?: () => number;
+ containerOffset?: () => number[];
mainCont: HTMLDivElement;
savedAnnotations: Dictionary<number, HTMLDivElement[]>;
annotationLayer: HTMLDivElement;
@@ -69,9 +70,10 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
if ((this.props.savedAnnotations.values()[0][0] as any).marqueeing) {
const scale = this.props.scaling?.() || 1;
const anno = this.props.savedAnnotations.values()[0][0];
+ const containerOffset = this.props.containerOffset?.() || [0, 0];
const mainAnnoDoc = Docs.Create.FreeformDocument([], { backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title });
- if (anno.style.left) mainAnnoDoc.x = parseInt(anno.style.left) / scale;
- if (anno.style.top) mainAnnoDoc.y = (NumCast(this.props.rootDoc._scrollTop) + parseInt(anno.style.top)) / scale;
+ if (anno.style.left) mainAnnoDoc.x = (parseInt(anno.style.left) - containerOffset[0]) / scale;
+ if (anno.style.top) mainAnnoDoc.y = (parseInt(anno.style.top) - containerOffset[1] + NumCast(this.props.rootDoc._scrollTop)) / scale;
if (anno.style.height) mainAnnoDoc._height = parseInt(anno.style.height) / scale;
if (anno.style.width) mainAnnoDoc._width = parseInt(anno.style.width) / scale;
mainAnnoDoc.group = mainAnnoDoc;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 02e88d939..a59ac109f 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
-import { Doc, Opt } from "../../../fields/Doc";
+import { Doc, Opt, DocListCast } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { listSpec, makeInterface } from "../../../fields/Schema";
@@ -31,6 +31,7 @@ export type CollectionStackedTimelineProps = {
isChildActive: () => boolean;
startTag: string;
endTag: string;
+ fieldKeySuffix?: string;
};
@observer
@@ -46,7 +47,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
@observable _markerEnd: number = 0;
get duration() { return this.props.duration; }
- @computed get anchorDocs() { return this.childDocs; }
+ @computed get anchorDocs() { return this.props.fieldKeySuffix ? this.childDocs.concat(...DocListCast(this.rootDoc[this.props.fieldKey + this.props.fieldKeySuffix])) : this.childDocs; }
@computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); }
@computed get selectionContainer() {
return CollectionStackedTimeline.SelectingRegion !== this ? (null) : <div className="collectionStackedTimeline-selector" style={{
@@ -71,7 +72,10 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
}
anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag]));
- anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this.props.endTag], val));
+ anchorEnd = (anchor: Doc, val: any = null) => {
+ const endVal = NumCast(anchor[this.props.endTag], val);
+ return NumCast(anchor._timecodeToHide, endVal === undefined ? null : endVal);
+ }
toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.duration, screen_delta / width * this.duration));
rangeClickScript = () => CollectionStackedTimeline.RangeScript;
labelClickScript = () => CollectionStackedTimeline.LabelScript;
@@ -88,7 +92,7 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
this._markerStart = this._markerEnd = this.currentTime;
CollectionStackedTimeline.SelectingRegion = this;
} else {
- this.createAnchor(this._markerStart, this.currentTime);
+ CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
CollectionStackedTimeline.SelectingRegion = undefined;
}
}
@@ -132,13 +136,13 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
this._markerEnd = tmp;
}
if (!isClick) {
- CollectionStackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15) && this.createAnchor(this._markerStart, this._markerEnd);
+ CollectionStackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15) && CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag);
}
(!isClick || !wasSelecting) && (CollectionStackedTimeline.SelectingRegion = undefined);
}),
(e, doubleTap) => {
this.props.select(false);
- e.shiftKey && this.createAnchor(this.currentTime);
+ e.shiftKey && CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
!wasPlaying && doubleTap && this.props.Play();
},
this.props.isSelected(true) || this.props.isChildActive(), undefined,
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 57b5f3ec7..692eaae66 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -326,15 +326,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
playing = () => this.audioState === "playing";
playLink = (link: Doc) => {
+ const stack = this._stackedTimeline.current;
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);
+ if (this.layoutDoc.playOnSelect) this.playFrom(stack?.anchorStart(link) || 0, stack?.anchorEnd(link));
+ else this._ele!.currentTime = this.layoutDoc._currentTimecode = (stack?.anchorStart(link) || 0);
}
else {
this.links.filter(l => l.anchor1 === link || l.anchor2 === link).forEach(l => {
const { la1, la2 } = this.getLinkData(l);
- const startTime = this._stackedTimeline.current?.anchorStart(la1) || this._stackedTimeline.current?.anchorStart(la2);
- const endTime = this._stackedTimeline.current?.anchorEnd(la1) || this._stackedTimeline.current?.anchorEnd(la2);
+ const startTime = stack?.anchorStart(la1) || stack?.anchorStart(la2);
+ const endTime = stack?.anchorEnd(la1) || stack?.anchorEnd(la2);
if (startTime !== undefined) {
if (this.layoutDoc.playOnSelect) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
else this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime;
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 79d584a1d..8a1cefbd9 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -70,7 +70,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
}
getAnchor = () => {
- return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "videoStart", "videoEnd", Cast(this.layoutDoc._currentTimecode, "number", null)) || this.rootDoc;
+ return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey + "-timeline", "videoStart", "videoEnd", Cast(this.layoutDoc._currentTimecode, "number", null)) || this.rootDoc;
}
choosePath(url: string) {
@@ -167,7 +167,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
//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 filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
+ const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, "");
+ const filename = path.basename(encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
returnedFilename && this.createRealSummaryLink(returnedFilename));
}
@@ -179,14 +180,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const height = this.layoutDoc._height || 0;
const imageSummary = Docs.Create.ImageDocument(url, {
_nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
- x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
+ x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), isLinkButton: true,
_width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-"
});
Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
- imageSummary.isLinkButton = true;
this.props.addDocument?.(imageSummary);
- DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot");
+ DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
}
@action
@@ -495,6 +495,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
renderDepth={this.props.renderDepth + 1}
startTag={"videoStart"}
endTag={"videoEnd"}
+ fieldKeySuffix={"-timeline"}
focus={emptyFunction}
bringToFront={emptyFunction}
CollectionView={undefined}
@@ -536,6 +537,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
}
+ marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
+ marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
render() {
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
@@ -570,7 +573,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
{this.annotationLayer}
{this.renderTimeline}
{!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
- <MarqueeAnnotator rootDoc={this.rootDoc} down={this._marqueeing} scaling={this.props.scaling} addDocument={this.addDocWithTimecode} finishMarquee={this.finishMarquee} savedAnnotations={this._savedAnnotations} annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current} />}
+ <MarqueeAnnotator
+ rootDoc={this.rootDoc}
+ down={this._marqueeing}
+ scaling={this.marqueeFitScaling}
+ containerOffset={this.marqueeOffset}
+ addDocument={this.addDocWithTimecode}
+ finishMarquee={this.finishMarquee}
+ savedAnnotations={this._savedAnnotations}
+ annotationLayer={this._annotationLayer.current} mainCont={this._mainCont.current}
+ />}
</div>
</div >);
}
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index f5bc05a2d..722c0a836 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -36,7 +36,7 @@ export const nodes: { [index: string]: NodeSpec } = {
"data-audioid": node.attrs.audioId,
},
formatAudioTime(node.attrs.timeCode.toString())
- ]
+ ];
},
parseDOM: [
{