aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/youtube/YoutubeBox.tsx3
-rw-r--r--src/client/documents/Documents.ts3
-rw-r--r--src/client/util/DocumentManager.ts2
-rw-r--r--src/client/util/DragManager.ts50
-rw-r--r--src/client/util/Import & Export/DirectoryImportBox.tsx4
-rw-r--r--src/client/views/DocumentDecorations.scss5
-rw-r--r--src/client/views/DocumentDecorations.tsx6
-rw-r--r--src/client/views/GlobalKeyHandler.ts2
-rw-r--r--src/client/views/InkingStroke.tsx25
-rw-r--r--src/client/views/TemplateMenu.tsx4
-rw-r--r--src/client/views/animationtimeline/Keyframe.tsx8
-rw-r--r--src/client/views/animationtimeline/Timeline.tsx218
-rw-r--r--src/client/views/animationtimeline/TimelineOverview.tsx4
-rw-r--r--src/client/views/animationtimeline/Track.tsx5
-rw-r--r--src/client/views/collections/CollectionPileView.tsx7
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx8
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx8
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx3
-rw-r--r--src/client/views/collections/CollectionView.tsx5
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx150
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx11
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx17
-rw-r--r--src/client/views/nodes/DocumentView.tsx18
-rw-r--r--src/client/views/nodes/PDFBox.tsx2
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx3
-rw-r--r--src/client/views/nodes/VideoBox.tsx2
-rw-r--r--src/client/views/nodes/WebBox.tsx14
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx4
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx7
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss11
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx8
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx2
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx4
-rw-r--r--src/client/views/pdf/PDFViewer.tsx21
-rw-r--r--src/client/views/webcam/DashWebRTCVideo.tsx3
-rw-r--r--src/client/views/webcam/WebCamLogic.js5
-rw-r--r--src/fields/Doc.ts27
-rw-r--r--src/fields/RichTextUtils.ts6
-rw-r--r--src/fields/documentSchemas.ts10
-rw-r--r--src/server/ApiManagers/UploadManager.ts3
43 files changed, 333 insertions, 371 deletions
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index 748d571c0..2a1f55710 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -10,6 +10,7 @@ import { FieldView, FieldViewProps } from "../../views/nodes/FieldView";
import "../../views/nodes/WebBox.scss";
import "./YoutubeBox.scss";
import React = require("react");
+import { InkTool } from '../../../fields/InkField';
interface VideoTemplate {
thumbnailUrl: string;
@@ -349,7 +350,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
- const classname = "webBox-cont" + (this.props.isSelected() && !Doc.GetSelectedTool() && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+ const classname = "webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
return (
<>
<div className={classname} >
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9c807eff2..8d867348f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -514,6 +514,7 @@ export namespace Docs {
const dataDoc = MakeDataDelegate(proto, protoProps, data, fieldKey);
const viewDoc = Doc.MakeDelegate(dataDoc, delegId);
+ viewDoc.author = Doc.CurrentUserEmail;
viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc);
return Doc.assign(viewDoc, delegateProps, true);
@@ -630,7 +631,7 @@ export namespace Docs {
return doc;
}
- export function InkDocument(color: string, tool: number, strokeWidth: string, strokeBezier: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+ export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
const I = new Doc();
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString("data");
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 67f2f244c..78c05f572 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -168,7 +168,7 @@ export class DocumentManager {
highlight();
} else { // otherwise try to get a view of the context of the target
const targetDocContextView = getFirstDocView(targetDocContext);
- targetDocContext.scrollY = 0; // this will force PDFs to activate and load their annotations / allow scrolling
+ targetDocContext._scrollY = 0; // this will force PDFs to activate and load their annotations / allow scrolling
if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
targetDocContext.panTransformType = "Ease";
targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom);
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index e7a14f9c5..f1afaf734 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -67,6 +67,7 @@ export function SetupDrag(
export namespace DragManager {
let dragDiv: HTMLDivElement;
+ let dragLabel: HTMLDivElement;
export let StartWindowDrag: Opt<((e: any, dragDocs: Doc[]) => void)> = undefined;
export function Root() {
@@ -97,7 +98,8 @@ export namespace DragManager {
readonly shiftKey: boolean,
readonly altKey: boolean,
readonly metaKey: boolean,
- readonly ctrlKey: boolean
+ readonly ctrlKey: boolean,
+ readonly embedKey: boolean,
) { }
}
@@ -309,14 +311,24 @@ export namespace DragManager {
};
}
export let docsBeingDragged: Doc[] = [];
+ export let CanEmbed = false;
export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void) {
eles = eles.filter(e => e);
+ CanEmbed = false;
if (!dragDiv) {
dragDiv = document.createElement("div");
dragDiv.className = "dragManager-dragDiv";
dragDiv.style.pointerEvents = "none";
+ dragLabel = document.createElement("div") as HTMLDivElement;
+ dragLabel.className = "dragManager-dragLabel";
+ dragLabel.style.zIndex = "100001";
+ dragLabel.style.fontSize = "10";
+ dragLabel.style.position = "absolute";
+ dragLabel.innerText = "press 'a' to embed on drop";
+ dragDiv.appendChild(dragLabel);
DragManager.Root().appendChild(dragDiv);
}
+ dragLabel.style.display = "";
SnappingManager.SetIsDragging(true);
const scaleXs: number[] = [];
const scaleYs: number[] = [];
@@ -358,6 +370,7 @@ export namespace DragManager {
dragElement.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`;
dragElement.style.width = `${rect.width / scaleX}px`;
dragElement.style.height = `${rect.height / scaleY}px`;
+ dragLabel.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0) - 20}px)`;
if (docsBeingDragged.length) {
const pdfBox = dragElement.getElementsByTagName("canvas");
@@ -399,20 +412,21 @@ export namespace DragManager {
if (dragData instanceof DocumentDragData) {
dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : undefined;
}
- if (e.shiftKey && dragData.droppedDocuments.length === 1) {
- !dragData.dropAction && (dragData.dropAction = alias);
- if (dragData.dropAction === "move") {
- dragData.removeDocument?.(dragData.draggedDocuments[0]);
+ if (e)
+ if (e.shiftKey && dragData.droppedDocuments.length === 1) {
+ !dragData.dropAction && (dragData.dropAction = alias);
+ if (dragData.dropAction === "move") {
+ dragData.removeDocument?.(dragData.draggedDocuments[0]);
+ }
+ AbortDrag();
+ finishDrag?.(new DragCompleteEvent(true, dragData));
+ DragManager.StartWindowDrag?.({
+ pageX: e.pageX,
+ pageY: e.pageY,
+ preventDefault: emptyFunction,
+ button: 0
+ }, dragData.droppedDocuments);
}
- AbortDrag();
- finishDrag?.(new DragCompleteEvent(true, dragData));
- DragManager.StartWindowDrag?.({
- pageX: e.pageX,
- pageY: e.pageY,
- preventDefault: emptyFunction,
- button: 0
- }, dragData.droppedDocuments);
- }
const { thisX, thisY } = snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom);
@@ -421,12 +435,14 @@ export namespace DragManager {
const moveY = thisY - lastY;
lastX = thisX;
lastY = thisY;
+ dragLabel.style.transform = `translate(${xs[0] + moveX + (options?.offsetX || 0)}px, ${ys[0] + moveY + (options?.offsetY || 0) - 20}px)`;
dragElements.map((dragElement, i) => (dragElement.style.transform =
`translate(${(xs[i] += moveX) + (options?.offsetX || 0)}px, ${(ys[i] += moveY) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)
);
};
const hideDragShowOriginalElements = () => {
+ dragLabel.style.display = "none";
dragElements.map(dragElement => dragElement.parentNode === dragDiv && dragDiv.removeChild(dragElement));
eles.map(ele => ele.parentElement && ele.parentElement?.className === dragData.dragDivName ? (ele.parentElement.hidden = false) : (ele.hidden = false));
};
@@ -481,7 +497,8 @@ export namespace DragManager {
shiftKey: e.shiftKey,
altKey: e.altKey,
metaKey: e.metaKey,
- ctrlKey: e.ctrlKey
+ ctrlKey: e.ctrlKey,
+ embedKey: CanEmbed
}
})
);
@@ -496,7 +513,8 @@ export namespace DragManager {
shiftKey: e.shiftKey,
altKey: e.altKey,
metaKey: e.metaKey,
- ctrlKey: e.ctrlKey
+ ctrlKey: e.ctrlKey,
+ embedKey: CanEmbed
}
})
);
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 4e5cde558..25c556697 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -7,7 +7,7 @@ import Measure, { ContentRect } from "react-measure";
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTag, faPlus, faCloudUploadAlt } from '@fortawesome/free-solid-svg-icons';
-import { Docs, DocumentOptions } from "../../documents/Documents";
+import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
import { observer } from "mobx-react";
import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from "./ImportMetadataEntry";
import { Utils } from "../../../Utils";
@@ -123,7 +123,7 @@ export default class DirectoryImportBox extends React.Component<FieldViewProps>
}
const { accessPaths, exifData } = result;
const path = Utils.prepend(accessPaths.agnostic.client);
- const document = await Doc.Get.DocumentFromType(type, path, { _width: 300, title: name });
+ const document = await DocUtils.DocumentFromType(type, path, { _width: 300, title: name });
const { data, error } = exifData;
if (document) {
Doc.GetProto(document).exif = error || Doc.Get.FromJson({ data });
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index a4d4af2f0..0a96b058b 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -152,6 +152,11 @@ $linkGap : 3px;
text-align: center;
display: flex;
border-bottom: solid 1px;
+ margin-left:10px;
+ width: calc(100% - 10px);
+ }
+ .focus-visible {
+ margin-left:0px;
}
.publishBox {
width: 20px;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 04f02c683..6ca7331d6 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -403,7 +403,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
onPointerUp = (e: PointerEvent): void => {
SelectionManager.SelectedDocuments().map(dv => {
if (NumCast(dv.layoutDoc._delayAutoHeight) < this._dragHeights.get(dv.layoutDoc)!) {
- dv.nativeWidth > 0 && Doc.toggleNativeDimensions(dv.layoutDoc, dv.props.ContentScaling(), dv.panelWidth(), dv.panelHeight());
+ dv.nativeWidth > 0 && Doc.toggleNativeDimensions(dv.layoutDoc, dv.props.ContentScaling(), dv.props.PanelWidth(), dv.props.PanelHeight());
dv.layoutDoc._autoHeight = true;
}
dv.layoutDoc._delayAutoHeight = undefined;
@@ -466,7 +466,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
const titleArea = this._edtingTitle ?
<>
- <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle} style={{ width: minimal ? "100%" : "calc(100% - 20px)" }}
+ <input ref={this._keyinput} className="documentDecorations-title" type="text" name="dynbox" autoComplete="on" value={this._accumulatedTitle}
onBlur={e => this.titleBlur(true)} onChange={action(e => this._accumulatedTitle = e.target.value)} onKeyPress={this.titleEntered} />
{minimal ? (null) : <div className="publishBox" title="make document referenceable by its title"
onPointerDown={action(e => {
@@ -485,7 +485,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
<FontAwesomeIcon size="lg" icon="cog" />
</div>}
<div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
- <span style={{ width: "calc(100% - 25px)", display: "inline-block" }}>{`${this.selectionTitle}`}</span>
+ <span style={{ width: "100%", display: "inline-block" }}>{`${this.selectionTitle}`}</span>
</div>
</>;
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 2d5af5386..a3b144055 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -72,6 +72,8 @@ export default class KeyManager {
private unmodified = action((keyname: string, e: KeyboardEvent) => {
switch (keyname) {
+ case "a": DragManager.CanEmbed = true;
+ break;
case " ":
MarqueeView.DragMarquee = !MarqueeView.DragMarquee;
break;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index acbfde5a1..a7650163f 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -32,6 +32,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
CognitiveServices.Inking.Appliers.ConcatenateHandwriting(this.dataDoc, ["inkAnalysis", "handwriting"], [data]);
}
+ private makeMask = () => {
+ this.props.Document._backgroundColor = "rgba(0,0,0,0.7)";
+ this.props.Document.mixBlendMode = "hard-light";
+ this.props.Document.color = "#9b9b9bff";
+ this.props.Document.stayInCollection = true;
+ this.props.Document.isInkMask = true;
+ }
+
render() {
TraceMobx();
const data: InkData = Cast(this.dataDoc[this.fieldKey], InkField)?.inkData ?? [];
@@ -59,13 +67,14 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
<svg className="inkingStroke"
width={width}
height={height}
- style={{ pointerEvents: "none", mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset" }}
+ style={{
+ pointerEvents: this.props.Document.isInkMask ? "all" : "none",
+ transform: this.props.Document.isInkMask ? "translate(2500px, 2500px)" : undefined,
+ mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? "multiply" : "unset"
+ }}
onContextMenu={() => {
- ContextMenu.Instance.addItem({
- description: "Analyze Stroke",
- event: this.analyzeStrokes,
- icon: "paint-brush"
- });
+ ContextMenu.Instance.addItem({ description: "Analyze Stroke", event: this.analyzeStrokes, icon: "paint-brush" });
+ ContextMenu.Instance.addItem({ description: "Make Mask", event: this.makeMask, icon: "paint-brush" });
}}
><defs>
<filter id="dangerShine">
@@ -108,5 +117,5 @@ Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any) {
Scripting.addGlobal(function activateEraser(pen: any) { return Doc.SetSelectedTool(pen ? InkTool.Eraser : InkTool.None); });
Scripting.addGlobal(function activateStamp(pen: any) { return Doc.SetSelectedTool(pen ? InkTool.Stamp : InkTool.None); });
Scripting.addGlobal(function deactivateInk() { return Doc.SetSelectedTool(InkTool.None); });
-Scripting.addGlobal(function setInkWidth(width: any) { return Doc.SetSelectedTool(width); });
-Scripting.addGlobal(function setInkColor(color: any) { return Doc.SetSelectedTool(color); }); \ No newline at end of file
+Scripting.addGlobal(function setInkWidth(width: any) { return SetActiveInkWidth(width); });
+Scripting.addGlobal(function setInkColor(color: any) { return SetActiveInkColor(color); }); \ No newline at end of file
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 77e6ebf44..f135823a8 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -7,7 +7,7 @@ import { DocumentView } from "./nodes/DocumentView";
import { Template } from "./Templates";
import React = require("react");
import { Doc, DocListCast } from "../../fields/Doc";
-import { Docs, } from "../documents/Documents";
+import { Docs, DocUtils, } from "../documents/Documents";
import { StrCast, Cast } from "../../fields/Types";
import { CollectionTreeView } from "./collections/CollectionTreeView";
import { returnTrue, emptyFunction, returnFalse, returnOne, emptyPath, returnZero } from "../../Utils";
@@ -176,5 +176,5 @@ Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) {
template = Cast(template.dragFactory, Doc, null);
}
const templateTitle = StrCast(template?.title);
- return templateTitle && Doc.makeCustomViewClicked(doc, Docs.Create.FreeformDocument, templateTitle, template);
+ return templateTitle && DocUtils.makeCustomViewClicked(doc, Docs.Create.FreeformDocument, templateTitle, template);
});
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index b562bd957..3a7182a94 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -180,10 +180,10 @@ export class Keyframe extends React.Component<IProps> {
const fadeIn = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.fadeIn, KeyframeFunc.KeyframeType.fade);
const fadeOut = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration - this.regiondata.fadeOut, KeyframeFunc.KeyframeType.fade);
const finish = this.props.makeKeyData(this.regiondata, this.regiondata.position + this.regiondata.duration, KeyframeFunc.KeyframeType.end);
- (fadeIn as Doc).opacity = 1;
- (fadeOut as Doc).opacity = 1;
- (start as Doc).opacity = 0.1;
- (finish as Doc).opacity = 0.1;
+ fadeIn.opacity = 1;
+ fadeOut.opacity = 1;
+ start.opacity = 0.1;
+ finish.opacity = 0.1;
this.forceUpdate(); //not needed, if setTimeout is gone...
}, 1000);
}
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index 30692944d..ab984f727 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -1,19 +1,17 @@
-import * as React from "react";
-import "./Timeline.scss";
-import { listSpec } from "../../../fields/Schema";
+import { faBackward, faForward, faGripLines, faPauseCircle, faPlayCircle } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
-import { Track } from "./Track";
-import { observable, action, computed, runInAction, IReactionDisposer, reaction, trace } from "mobx";
-import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types";
-import { List } from "../../../fields/List";
+import * as React from "react";
import { Doc, DocListCast } from "../../../fields/Doc";
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faPlayCircle, faBackward, faForward, faGripLines, faPauseCircle, faEyeSlash, faEye, faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons";
-import { ContextMenu } from "../ContextMenu";
-import { TimelineOverview } from "./TimelineOverview";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
import { FieldViewProps } from "../nodes/FieldView";
import { KeyframeFunc } from "./Keyframe";
-import { Utils } from "../../../Utils";
+import "./Timeline.scss";
+import { TimelineOverview } from "./TimelineOverview";
+import { Track } from "./Track";
+import clamp from "../../util/clamp";
/**
* Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are
@@ -39,7 +37,6 @@ import { Utils } from "../../../Utils";
@observer
export class Timeline extends React.Component<FieldViewProps> {
-
//readonly constants
private readonly DEFAULT_TICK_SPACING: number = 50;
private readonly MAX_TITLE_HEIGHT = 75;
@@ -57,7 +54,7 @@ export class Timeline extends React.Component<FieldViewProps> {
@observable private _infoContainer = React.createRef<HTMLDivElement>();
@observable private _roundToggleRef = React.createRef<HTMLDivElement>();
@observable private _roundToggleContainerRef = React.createRef<HTMLDivElement>();
- @observable private _timeInputRef = React.createRef<HTMLInputElement>();
+
//boolean vars and instance vars
@observable private _currentBarX: number = 0;
@@ -71,27 +68,18 @@ export class Timeline extends React.Component<FieldViewProps> {
@observable private _tickIncrement = this.DEFAULT_TICK_INCREMENT;
@observable private _time = 100000; //DEFAULT
@observable private _playButton = faPlayCircle;
- @observable private _mouseToggled = false;
- @observable private _doubleClickEnabled = false;
@observable private _titleHeight = 0;
- // so a reaction can be made
- @observable public _isAuthoring = this.props.Document.isATOn;
-
/**
* collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit.
*/
@computed
- private get children(): List<Doc> {
- const extendedDocument = ["image", "video", "pdf"].includes(StrCast(this.props.Document.type));
- if (extendedDocument) {
- if (this.props.Document.data_ext) {
- return Cast((Cast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + "-annotations"], Doc) as Doc).annotations, listSpec(Doc)) as List<Doc>;
- } else {
- return new List<Doc>();
- }
+ private get children(): Doc[] {
+ const annotatedDoc = ["image", "video", "pdf"].includes(StrCast(this.props.Document.type));
+ if (annotatedDoc) {
+ return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + "-annotations"]);
}
- return Cast(this.props.Document[this.props.fieldKey], listSpec(Doc)) as List<Doc>;
+ return DocListCast(this.props.Document[this.props.fieldKey]);
}
/////////lifecycle functions////////////
@@ -144,9 +132,7 @@ export class Timeline extends React.Component<FieldViewProps> {
}
//for playing
- @action
onPlay = (e: React.MouseEvent) => {
- e.preventDefault();
e.stopPropagation();
this.play();
}
@@ -156,24 +142,15 @@ export class Timeline extends React.Component<FieldViewProps> {
*/
@action
play = () => {
- if (this._isPlaying) {
- this._isPlaying = false;
- this._playButton = faPlayCircle;
- } else {
- this._isPlaying = true;
- this._playButton = faPauseCircle;
- const playTimeline = () => {
- if (this._isPlaying) {
- if (this._currentBarX >= this._totalLength) {
- this.changeCurrentBarX(0);
- } else {
- this.changeCurrentBarX(this._currentBarX + this._windSpeed);
- }
- setTimeout(playTimeline, 15);
- }
- };
- playTimeline();
- }
+ const playTimeline = () => {
+ if (this._isPlaying) {
+ this.changeCurrentBarX(this._currentBarX >= this._totalLength ? 0 : this._currentBarX + this._windSpeed);
+ setTimeout(playTimeline, 15);
+ }
+ };
+ this._isPlaying = !this._isPlaying;
+ this._playButton = this._isPlaying ? faPauseCircle : faPlayCircle;
+ this._isPlaying && playTimeline();
}
@@ -206,12 +183,7 @@ export class Timeline extends React.Component<FieldViewProps> {
*/
@action
onScrubberDown = (e: React.PointerEvent) => {
- e.preventDefault();
- e.stopPropagation();
- document.addEventListener("pointermove", this.onScrubberMove);
- document.addEventListener("pointerup", () => {
- document.removeEventListener("pointermove", this.onScrubberMove);
- });
+ setupMoveUpEvents(this, e, this.onScrubberMove, emptyFunction, emptyFunction);
}
/**
@@ -219,12 +191,11 @@ export class Timeline extends React.Component<FieldViewProps> {
*/
@action
onScrubberMove = (e: PointerEvent) => {
- e.preventDefault();
- e.stopPropagation();
const scrubberbox = this._infoContainer.current!;
const left = scrubberbox.getBoundingClientRect().left;
const offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale;
this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position
+ return false;
}
/**
@@ -232,27 +203,8 @@ export class Timeline extends React.Component<FieldViewProps> {
*/
@action
onPanDown = (e: React.PointerEvent) => {
- e.preventDefault();
- e.stopPropagation();
- const clientX = e.clientX;
- if (this._doubleClickEnabled) {
- this._doubleClickEnabled = false;
- } else {
- setTimeout(() => {
- if (!this._mouseToggled && this._doubleClickEnabled) this.changeCurrentBarX(this._trackbox.current!.scrollLeft + clientX - this._trackbox.current!.getBoundingClientRect().left);
- this._mouseToggled = false;
- this._doubleClickEnabled = false;
- }, 200);
- this._doubleClickEnabled = true;
- document.addEventListener("pointermove", this.onPanMove);
- document.addEventListener("pointerup", () => {
- document.removeEventListener("pointermove", this.onPanMove);
- if (!this._doubleClickEnabled) {
- this._mouseToggled = false;
- }
- });
-
- }
+ setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, (e) =>
+ this.changeCurrentBarX(this._trackbox.current!.scrollLeft + e.clientX - this._trackbox.current!.getBoundingClientRect().left));
}
/**
@@ -260,11 +212,6 @@ export class Timeline extends React.Component<FieldViewProps> {
*/
@action
onPanMove = (e: PointerEvent) => {
- e.preventDefault();
- e.stopPropagation();
- if (e.movementX !== 0 || e.movementY !== 0) {
- this._mouseToggled = true;
- }
const trackbox = this._trackbox.current!;
const titleContainer = this._titleContainer.current!;
this.movePanX(this._visibleStart - e.movementX);
@@ -276,43 +223,25 @@ export class Timeline extends React.Component<FieldViewProps> {
this._time -= KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this._tickSpacing, this._tickIncrement);
this.props.Document.AnimationLength = this._time;
}
-
+ return false;
}
@action
movePanX = (pixel: number) => {
- const infoContainer = this._infoContainer.current!;
- infoContainer.scrollLeft = pixel;
- this._visibleStart = infoContainer.scrollLeft;
+ this._infoContainer.current!.scrollLeft = pixel;
+ this._visibleStart = this._infoContainer.current!.scrollLeft;
}
/**
* resizing timeline (in editing mode) (the hamburger drag icon)
*/
- @action
onResizeDown = (e: React.PointerEvent) => {
- e.preventDefault();
- e.stopPropagation();
- document.addEventListener("pointermove", this.onResizeMove);
- document.addEventListener("pointerup", () => {
- document.removeEventListener("pointermove", this.onResizeMove);
- });
- }
-
- @action
- onResizeMove = (e: PointerEvent) => {
- e.preventDefault();
- e.stopPropagation();
- const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
- // let offset = 0;
- if (this._containerHeight + offset <= this.MIN_CONTAINER_HEIGHT) {
- this._containerHeight = this.MIN_CONTAINER_HEIGHT;
- } else if (this._containerHeight + offset >= this.MAX_CONTAINER_HEIGHT) {
- this._containerHeight = this.MAX_CONTAINER_HEIGHT;
- } else {
- this._containerHeight += offset;
- }
+ setupMoveUpEvents(this, e, action((e) => {
+ const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
+ this._containerHeight = clamp(this.MIN_CONTAINER_HEIGHT, this._containerHeight + offset, this.MAX_CONTAINER_HEIGHT);
+ return false;
+ }), emptyFunction, emptyFunction);
}
/**
@@ -323,8 +252,8 @@ export class Timeline extends React.Component<FieldViewProps> {
time = time / 1000;
const inSeconds = Math.round(time * 100) / 100;
- const min: (string | number) = Math.floor(inSeconds / 60);
- const sec: (string | number) = (Math.round((inSeconds % 60) * 100) / 100);
+ const min = Math.floor(inSeconds / 60);
+ const sec = (Math.round((inSeconds % 60) * 100) / 100);
let secString = sec.toFixed(2);
if (Math.floor(sec / 10) === 0) {
@@ -346,7 +275,7 @@ export class Timeline extends React.Component<FieldViewProps> {
const offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left;
const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement);
const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement);
- e.deltaY < 0 ? this.zoom(true) : this.zoom(false);
+ this.zoom(e.deltaY < 0);
const currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement);
const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement);
this._infoContainer.current!.scrollLeft = currPixel - offset;
@@ -405,30 +334,17 @@ export class Timeline extends React.Component<FieldViewProps> {
private timelineToolBox = (scale: number, totalTime: number) => {
const size = 40 * scale; //50 is default
const iconSize = 25;
+ const width: number = this.props.PanelWidth();
+ const modeType = this.props.Document.isATOn ? "Author" : "Play";
//decides if information should be omitted because the timeline is very small
// if its less than 950 pixels then it's going to be overlapping
- let shouldCompress = false;
- const width: number = this.props.PanelWidth();
+ let modeString = modeType, overviewString = "", lengthString = "";
if (width < 850) {
- shouldCompress = true;
- }
-
- let modeString, overviewString, lengthString;
- const modeType = this.props.Document.isATOn ? "Author" : "Play";
-
- if (!shouldCompress) {
modeString = "Mode: " + modeType;
overviewString = "Overview:";
lengthString = "Length: ";
}
- else {
- modeString = modeType;
- overviewString = "";
- lengthString = "";
- }
-
- // let rightInfo = this.timeIndicator;
return (
<div key="timeline_toolbox" className="timeline-toolbox" style={{ height: `${size}px` }}>
@@ -451,8 +367,7 @@ export class Timeline extends React.Component<FieldViewProps> {
<div className="time-box overview-tool" style={{ display: "flex" }}>
{this.timeIndicator(lengthString, totalTime)}
<div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this.props.Document)}><FontAwesomeIcon icon="compress-arrows-alt" size="lg" /></div>
- <div className="resetView-tool" style={{ display: this._isAuthoring ? "flex" : "none" }} title="Set Default View" onClick={() => this.setView(this.props.Document)}><FontAwesomeIcon icon="expand-arrows-alt" size="lg" /></div>
-
+ <div className="resetView-tool" style={{ display: this.props.Document.isATOn ? "flex" : "none" }} title="Set Default View" onClick={() => this.setView(this.props.Document)}><FontAwesomeIcon icon="expand-arrows-alt" size="lg" /></div>
</div>
</div>
</div>
@@ -498,15 +413,15 @@ export class Timeline extends React.Component<FieldViewProps> {
const roundToggle = this._roundToggleRef.current!;
const roundToggleContainer = this._roundToggleContainerRef.current!;
const timelineContainer = this._timelineContainer.current!;
- if (BoolCast(this.props.Document.isATOn)) {
+
+ this.props.Document.isATOn = !this.props.Document.isATOn;
+ if (!BoolCast(this.props.Document.isATOn)) {
//turning on playmode...
roundToggle.style.transform = "translate(0px, 0px)";
roundToggle.style.animationName = "turnoff";
roundToggleContainer.style.animationName = "turnoff";
roundToggleContainer.style.backgroundColor = "white";
timelineContainer.style.top = `${-this._containerHeight}px`;
- this.props.Document.isATOn = false;
- this._isAuthoring = false;
this.toPlay();
} else {
//turning on authoring mode...
@@ -515,8 +430,6 @@ export class Timeline extends React.Component<FieldViewProps> {
roundToggleContainer.style.animationName = "turnon";
roundToggleContainer.style.backgroundColor = "#9acedf";
timelineContainer.style.top = "0px";
- this.props.Document.isATOn = true;
- this._isAuthoring = true;
this.toAuthoring();
}
}
@@ -532,11 +445,8 @@ export class Timeline extends React.Component<FieldViewProps> {
// @computed
getCurrentTime = () => {
- let current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement);
- if (current > this._time) {
- current = this._time;
- }
- return this.toReadTime(current);
+ const current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement);
+ return this.toReadTime(current > this._time ? this._time : current);
}
@observable private mapOfTracks: (Track | null)[] = [];
@@ -562,18 +472,13 @@ export class Timeline extends React.Component<FieldViewProps> {
@action
toAuthoring = () => {
- let longestTime = this.findLongestTime();
- if (longestTime === 0) longestTime = 1;
- const adjustedTime = Math.ceil(longestTime / 100000) * 100000;
- // console.log(adjustedTime);
- this._totalLength = KeyframeFunc.convertPixelTime(adjustedTime, "mili", "pixel", this._tickSpacing, this._tickIncrement);
- this._time = adjustedTime;
+ this._time = Math.ceil((this.findLongestTime() ?? 1) / 100000) * 100000;
+ this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement);
}
@action
toPlay = () => {
- const longestTime = this.findLongestTime();
- this._time = longestTime;
+ this._time = this.findLongestTime();
this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement);
}
@@ -582,40 +487,35 @@ export class Timeline extends React.Component<FieldViewProps> {
* basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region)
*/
render() {
- setTimeout(() => {
- this.changeLengths();
- // this.toPlay();
- // this._time = longestTime;
- }, 0);
+ setTimeout(() => this.changeLengths(), 0);
- const longestTime = this.findLongestTime();
trace();
// change visible and total width
return (
<div style={{ visibility: "visible" }}>
- <div key="timeline_wrapper" style={{ visibility: BoolCast(this.props.Document.isATOn) ? "visible" : "hidden", left: "0px", top: "0px", position: "absolute", width: "100%", transform: "translate(0px, 0px)" }}>
+ <div key="timeline_wrapper" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden", left: "0px", top: "0px", position: "absolute", width: "100%", transform: "translate(0px, 0px)" }}>
<div key="timeline_container" className="timeline-container" ref={this._timelineContainer} style={{ height: `${this._containerHeight}px`, top: `0px` }}>
- <div key="timeline_info" className="info-container" ref={this._infoContainer} onWheel={this.onWheelZoom}>
+ <div key="timeline_info" className="info-container" onPointerDown={this.onPanDown} ref={this._infoContainer} onWheel={this.onWheelZoom}>
{this.drawTicks()}
<div key="timeline_scrubber" className="scrubber" style={{ transform: `translate(${this._currentBarX}px)` }}>
<div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown} ></div>
</div>
- <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} onPointerDown={this.onPanDown} style={{ width: `${this._totalLength}px` }}>
- {DocListCast(this.children).map(doc =>
+ <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}>
+ {this.children.map(doc =>
<Track ref={ref => this.mapOfTracks.push(ref)} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={true} />
)}
</div>
</div>
<div className="currentTime">Current: {this.getCurrentTime()}</div>
<div key="timeline_title" className="title-container" ref={this._titleContainer}>
- {DocListCast(this.children).map(doc => <div style={{ height: `${(this._titleHeight)}px` }} className="datapane" onPointerOver={() => { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}><p>{doc.title}</p></div>)}
+ {this.children.map(doc => <div style={{ height: `${(this._titleHeight)}px` }} className="datapane" onPointerOver={() => { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}><p>{doc.title}</p></div>)}
</div>
<div key="timeline_resize" onPointerDown={this.onResizeDown}>
<FontAwesomeIcon className="resize" icon={faGripLines} />
</div>
</div>
</div>
- {this.timelineToolBox(1, longestTime)}
+ {this.timelineToolBox(1, this.findLongestTime())}
</div>
);
}
diff --git a/src/client/views/animationtimeline/TimelineOverview.tsx b/src/client/views/animationtimeline/TimelineOverview.tsx
index 31e248823..81a5587e4 100644
--- a/src/client/views/animationtimeline/TimelineOverview.tsx
+++ b/src/client/views/animationtimeline/TimelineOverview.tsx
@@ -42,9 +42,9 @@ export class TimelineOverview extends React.Component<TimelineOverviewProps>{
this.setOverviewWidth();
this._authoringReaction = reaction(
- () => this.props.parent._isAuthoring,
+ () => this.props.isAuthoring,
() => {
- if (!this.props.parent._isAuthoring) {
+ if (!this.props.isAuthoring) {
runInAction(() => {
this.setOverviewWidth();
});
diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx
index fc96c320a..4987006e7 100644
--- a/src/client/views/animationtimeline/Track.tsx
+++ b/src/client/views/animationtimeline/Track.tsx
@@ -31,13 +31,14 @@ export class Track extends React.Component<IProps> {
@observable private _autoKfReaction: any;
@observable private _newKeyframe: boolean = false;
private readonly MAX_TITLE_HEIGHT = 75;
- private _trackHeight = 0;
+ @observable private _trackHeight = 0;
private primitiveWhitelist = [
"x",
"y",
"_width",
"_height",
"opacity",
+ "_scrollTop"
];
private objectWhitelist = [
"data"
@@ -51,7 +52,7 @@ export class Track extends React.Component<IProps> {
if (!regions) this.props.node.regions = new List<Doc>(); //if there is no region, then create new doc to store stuff
//these two lines are exactly same from timeline.tsx
const relativeHeight = window.innerHeight / 20;
- this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness
+ runInAction(() => this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT); //for responsiveness
this._timelineVisibleReaction = this.timelineVisibleReaction();
this._currentBarXReaction = this.currentBarXReaction();
if (DocListCast(this.props.node.regions).length === 0) this.createRegion(this.time);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index fc48e0327..22a3877ab 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -12,6 +12,7 @@ import { SelectionManager } from "../../util/SelectionManager";
import { UndoManager, undoBatch } from "../../util/UndoManager";
import { SnappingManager } from "../../util/SnappingManager";
import { DragManager } from "../../util/DragManager";
+import { DocUtils } from "../../documents/Documents";
@observer
export class CollectionPileView extends CollectionSubView(doc => doc) {
@@ -45,12 +46,12 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
if (this.layoutEngine() === 'starburst') {
const defaultSize = 110;
this.layoutDoc._overflow = undefined;
- this.childDocs.forEach(d => Doc.iconify(d));
+ this.childDocs.forEach(d => DocUtils.iconify(d));
this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - NumCast(this.layoutDoc._starburstPileWidth, defaultSize) / 2;
this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
- Doc.pileup(this.childDocs);
+ DocUtils.pileup(this.childDocs);
this.layoutDoc._panX = 0;
this.layoutDoc._panY = -10;
this.props.Document._pileLayoutEngine = 'pass';
@@ -76,7 +77,7 @@ export class CollectionPileView extends CollectionSubView(doc => doc) {
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (super.onInternalDrop(e, de)) {
if (de.complete.docDragData) {
- Doc.pileup(this.childDocs);
+ DocUtils.pileup(this.childDocs);
}
}
return true;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7fd19a23c..0a1b03522 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -27,6 +27,7 @@ import { CollectionSubView } from "./CollectionSubView";
import { CollectionViewType } from "./CollectionView";
import { SnappingManager } from "../../util/SnappingManager";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+import { DocUtils } from "../../documents/Documents";
const _global = (window /* browser */ || global /* node */) as any;
type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -412,7 +413,7 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
if (value && this.sectionHeaders) {
const schemaHdrField = new SchemaHeaderField(value);
this.sectionHeaders.push(schemaHdrField);
- Doc.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: schemaHdrField.color }]);
+ DocUtils.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: schemaHdrField.color }]);
return true;
}
return false;
@@ -474,7 +475,10 @@ export class CollectionStackingView extends CollectionSubView(StackingDocument)
width: `${1 / this.scaling * 100}%`,
transformOrigin: "top left",
}}
- onScroll={action((e: React.UIEvent<HTMLDivElement>) => this._scroll = e.currentTarget.scrollTop)}
+ onScroll={action(e => {
+ if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
+ else this._scroll = e.currentTarget.scrollTop;
+ })}
onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
onWheel={e => this.props.active() && e.stopPropagation()} >
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index a269b21f5..bcd55f0fe 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -235,7 +235,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
Array.from(Object.keys(Doc.GetProto(dataDoc))).filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof (dataDoc[fieldKey]) === "string").map(fieldKey =>
docItems.push({
description: ":" + fieldKey, event: () => {
- const created = Docs.Get.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.parent.props.Document));
+ const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.parent.props.Document));
if (created) {
if (this.props.parent.Document.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, this.props.parent.props.Document);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index b60419258..93d20c475 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -10,7 +10,7 @@ import { WebField } from "../../../fields/URLField";
import { Cast, ScriptCast, NumCast } from "../../../fields/Types";
import { GestureUtils } from "../../../pen-gestures/GestureUtils";
import { Upload } from "../../../server/SharedMediaTypes";
-import { Utils } from "../../../Utils";
+import { Utils, returnFalse } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { Networking } from "../../Network";
import { ImageUtils } from "../../util/Import & Export/ImageUtils";
@@ -217,11 +217,11 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
const res = addedDocs.length ? this.addDocument(addedDocs) : true;
- added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, this.addDocument) : res;
+ added = movedDocs.length ? docDragData.moveDocument(movedDocs, this.props.Document, de.embedKey || !this.props.isAnnotationOverlay ? this.addDocument : returnFalse) : res;
} else {
added = this.addDocument(docDragData.droppedDocuments);
}
- e.stopPropagation();
+ added && e.stopPropagation();
return added;
}
else if (de.complete.annoDragData) {
@@ -335,7 +335,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
if (focusNode) {
const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect();
const x = (rect?.x || 0);
- const y = NumCast(srcWeb.scrollTop) + (rect?.y || 0);
+ const y = NumCast(srcWeb._scrollTop) + (rect?.y || 0);
const anchor = Docs.Create.FreeformDocument([], { _LODdisable: true, _backgroundColor: "transparent", _width: 25, _height: 25, x, y, annotationOn: srcWeb });
anchor.context = srcWeb;
const key = Doc.LayoutFieldKey(srcWeb);
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 15bc0bfd5..c2d682361 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -19,6 +19,7 @@ const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
import React = require("react");
+import { DocUtils } from "../../documents/Documents";
@observer
export class CollectionTimeView extends CollectionSubView(doc => doc) {
@@ -28,7 +29,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
@observable _childClickedScript: Opt<ScriptField>;
@observable _viewDefDivClick: Opt<ScriptField>;
async componentDidMount() {
- const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || Doc.findTemplate("detailView", StrCast(this.props.Document.type), "");
+ const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.props.Document.type), "");
const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List<string>(['dropAction']); useRightSplit(alias, shiftKey); ";
runInAction(() => {
this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! });
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 3330abbc4..a25a864af 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -32,7 +32,7 @@ import { CollectionDockingView } from "./CollectionDockingView";
import { AddCustomFreeFormLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionLinearView } from './CollectionLinearView';
-import { CollectionMapView } from './CollectionMapView';
+import CollectionMapView from './CollectionMapView';
import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { CollectionPileView } from './CollectionPileView';
@@ -163,7 +163,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus
if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
return true;
}
- return this.removeDocument(doc) ? addDocument(doc) : false;
+ const first = doc instanceof Doc ? doc : doc[0];
+ return !first?.stayInCollection && addDocument !== returnFalse && this.removeDocument(doc) ? addDocument(doc) : false;
}
showIsTagged = () => {
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 29a3e559a..3dc740c25 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -93,7 +93,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro
this.props.collapse?.(true);
break;
}
- })
+ });
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 9c0e5e917..678ad2a53 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -118,10 +118,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
this.Document.scale || 1)
@computed get cachedCenteringShiftX(): number {
- return !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / this.contentScaling : 0; // shift so pan position is at center of window for non-overlay collections
+ const scaling = this.fitToContent ? 1 : this.contentScaling;
+ return !this.isAnnotationOverlay ? this.props.PanelWidth() / 2 / this.parentScaling / scaling : 0; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
- return !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / this.contentScaling : 0;// shift so pan position is at center of window for non-overlay collections
+ const scaling = this.fitToContent ? 1 : this.contentScaling;
+ return !this.isAnnotationOverlay ? this.props.PanelHeight() / 2 / this.parentScaling / scaling : 0;// shift so pan position is at center of window for non-overlay collections
}
@computed get cachedGetLocalTransform(): Transform {
return Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY());
@@ -175,7 +177,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
}
return retVal;
- })
+ });
private selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
@@ -187,79 +189,76 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
}
+ onExternalDrop = (e: React.DragEvent) => {
+ return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY));
+ }
+
+ @undoBatch
+ @action
+ internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
+ if (!super.onInternalDrop(e, de)) return false;
+ const [xpo, ypo] = this.getTransformOverlay().transformPoint(de.x, de.y);
+ const z = NumCast(docDragData.droppedDocuments[0].z);
+ const x = (z ? xpo : xp) - docDragData.offset[0];
+ const y = (z ? ypo : yp) - docDragData.offset[1];
+ const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+ zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1);
+ const dropPos = [NumCast(docDragData.droppedDocuments[0].x), NumCast(docDragData.droppedDocuments[0].y)];
+ for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
+ const d = docDragData.droppedDocuments[i];
+ const layoutDoc = Doc.Layout(d);
+ if (this.Document.currentFrame !== undefined && !this.props.isAnnotationOverlay) {
+ const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
+ CollectionFreeFormDocumentView.setValues(this.Document.currentFrame, d, x + vals.x - dropPos[0], y + vals.y - dropPos[1], vals.opacity);
+ } else {
+ d.x = x + NumCast(d.x) - dropPos[0];
+ d.y = y + NumCast(d.y) - dropPos[1];
+ }
+ const nd = [NumCast(layoutDoc._nativeWidth), NumCast(layoutDoc._nativeHeight)];
+ layoutDoc._width = NumCast(layoutDoc._width, 300);
+ layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
+ d.isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+ }
+
+ (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
+ return true;
+ }
+
+ @undoBatch
@action
- onExternalDrop = (e: React.DragEvent): Promise<void> => {
- const pt = this.getTransform().transformPoint(e.pageX, e.pageY);
- return super.onExternalDrop(e, { x: pt[0], y: pt[1] });
+ internalPdfAnnoDrop(e: Event, annoDragData: DragManager.PdfAnnoDragData, xp: number, yp: number) {
+ const dragDoc = annoDragData.dropDocument;
+ const dropPos = [NumCast(dragDoc.x), NumCast(dragDoc.y)];
+ dragDoc.x = xp - annoDragData.offset[0] + (NumCast(dragDoc.x) - dropPos[0]);
+ dragDoc.y = yp - annoDragData.offset[1] + (NumCast(dragDoc.y) - dropPos[1]);
+ annoDragData.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
+ this.bringToFront(dragDoc);
+ return true;
}
@undoBatch
@action
+ internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
+ if (linkDragData.linkSourceDocument === this.props.Document) return false;
+ const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
+ this.props.addDocument(source);
+ linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: linkDragData.linkSourceDocument }, "doc annotation"); // TODODO this is where in text links get passed
+ e.stopPropagation();
+ return true;
+ }
+
+ @action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
// if (this.props.Document.isBackground) return false;
- const xf = this.getTransform();
- const xfo = this.getTransformOverlay();
- const [xp, yp] = xf.transformPoint(de.x, de.y);
- const [xpo, ypo] = xfo.transformPoint(de.x, de.y);
- const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- if (!this.isAnnotationOverlay && de.complete.linkDragData && de.complete.linkDragData.linkSourceDocument !== this.props.Document) {
- const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
- this.props.addDocument(source);
- (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceDocument },
- "doc annotation")); // TODODO this is where in text links get passed
- e.stopPropagation();
+ const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+ if (this.isAnnotationOverlay !== true && de.complete.linkDragData) {
+ return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
+ } else if (de.complete.annoDragData?.dropDocument && super.onInternalDrop(e, de)) {
+ return this.internalPdfAnnoDrop(e, de.complete.annoDragData, xp, yp);
+ } else if (de.complete.docDragData?.droppedDocuments.length && this.internalDocDrop(e, de, de.complete.docDragData, xp, yp)) {
return true;
- }
- if (super.onInternalDrop(e, de)) {
- if (de.complete.docDragData) {
- if (de.complete.docDragData.droppedDocuments.length) {
- const firstDoc = de.complete.docDragData.droppedDocuments[0];
- const z = NumCast(firstDoc.z);
- const x = (z ? xpo : xp) - de.complete.docDragData.offset[0];
- const y = (z ? ypo : yp) - de.complete.docDragData.offset[1];
- const dropX = NumCast(firstDoc.x);
- const dropY = NumCast(firstDoc.y);
- const droppedDocs = de.complete.docDragData.droppedDocuments;
- runInAction(() => {
- zsorted.forEach((doc, index) => doc.zIndex = index + 1);
- for (let i = 0; i < droppedDocs.length; i++) {
- const d = droppedDocs[i];
- const layoutDoc = Doc.Layout(d);
- if (this.Document.currentFrame !== undefined && !this.props.isAnnotationOverlay) {
- const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
- CollectionFreeFormDocumentView.setValues(this.Document.currentFrame, d, x + vals.x - dropX, y + vals.y - dropY, vals.opacity);
- } else {
- d.x = x + NumCast(d.x) - dropX;
- d.y = y + NumCast(d.y) - dropY;
- }
- if (!NumCast(layoutDoc._width)) {
- layoutDoc._width = 300;
- }
- if (!NumCast(layoutDoc._height)) {
- const nw = NumCast(layoutDoc._nativeWidth);
- const nh = NumCast(layoutDoc._nativeHeight);
- layoutDoc._height = nw && nh ? nh / nw * NumCast(layoutDoc._width) : 300;
- }
- d.isBackground === undefined && (d.zIndex = zsorted.length + 1 + i); // bringToFront
- }
- });
-
- (de.complete.docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(de.complete.docDragData.droppedDocuments);
- }
- }
- else if (de.complete.annoDragData) {
- if (de.complete.annoDragData.dropDocument) {
- const dragDoc = de.complete.annoDragData.dropDocument;
- const x = xp - de.complete.annoDragData.offset[0];
- const y = yp - de.complete.annoDragData.offset[1];
- const dropX = NumCast(dragDoc.x);
- const dropY = NumCast(dragDoc.y);
- dragDoc.x = x + NumCast(dragDoc.x) - dropX;
- dragDoc.y = y + NumCast(dragDoc.y) - dropY;
- de.complete.annoDragData.targetContext = this.props.Document; // dropped a PDF annotation, so we need to set the targetContext on the dragData which the PDF view uses at the end of the drop operation
- this.bringToFront(dragDoc);
- }
- }
+ } else {
+ UndoManager.Undo();
}
return false;
}
@@ -531,9 +530,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
});
- console.log(this._wordPalette)
CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
- console.log(results);
const wordResults = results.filter((r: any) => r.category === "inkWord");
for (const word of wordResults) {
const indices: number[] = word.strokeIds;
@@ -618,8 +615,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return;
}
if (!e.cancelBubble) {
- const selectedTool = Doc.GetSelectedTool();
- if (selectedTool === InkTool.None) {
+ if (Doc.GetSelectedTool() === InkTool.None) {
if (this._hitCluster && this.tryDragCluster(e)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
@@ -839,8 +835,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
bringToFront = action((doc: Doc, sendToBack?: boolean) => {
if (sendToBack || doc.isBackground) {
doc.zIndex = 0;
- }
- else {
+ } else if (doc.isInkMask) {
+ doc.zIndex = 5000;
+ } else {
const docs = this.childLayoutPairs.map(pair => pair.layout);
docs.slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
let zlast = docs.length ? NumCast(docs[docs.length - 1].zIndex) : 1;
@@ -887,7 +884,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
} else {
const contextHgt = Doc.AreProtosEqual(annotOn, this.props.Document) && this.props.VisibleHeight ? this.props.VisibleHeight() : NumCast(annotOn._height);
const offset = annotOn && (contextHgt / 2 * 96 / 72);
- this.props.Document.scrollY = NumCast(doc.y) - offset;
+ this.props.Document._scrollY = NumCast(doc.y) - offset;
}
afterFocus && setTimeout(afterFocus, 1000);
@@ -1345,7 +1342,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
<CollectionFreeFormViewPannableContents
centeringShiftX={this.centeringShiftX}
centeringShiftY={this.centeringShiftY}
- shifted={!this.nativeHeight && !this.isAnnotationOverlay}
easing={this.easing}
transition={Cast(this.layoutDoc.transition, "string", null)}
viewDefDivClick={this.props.viewDefDivClick}
@@ -1431,7 +1427,6 @@ interface CollectionFreeFormViewPannableContentsProps {
easing: () => boolean;
viewDefDivClick?: ScriptField;
children: () => JSX.Element[];
- shifted: boolean;
transition?: string;
}
@@ -1446,7 +1441,6 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const zoom = this.props.zoomScaling();
return <div className={freeformclass}
style={{
- width: this.props.shifted ? 0 : undefined, height: this.props.shifted ? 0 : undefined,
transform: `translate(${cenx}px, ${ceny}px) scale(${zoom}) translate(${panx}px, ${pany}px)`,
transition: this.props.transition
}}>
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index a811dd15a..62510ce9d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -28,6 +28,6 @@
white-space:nowrap;
}
.marquee-legend::after {
- content: "Press: c (collection), s (summary), or Delete"
+ content: "Press <space> for lasso"
}
} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b0461609a..085625e69 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -62,7 +62,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
this._pointsX = [];
this._pointsY = [];
- this._freeHand = false;
}
@undoBatch
@@ -353,7 +352,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const selected = this.marqueeSelect(false);
SelectionManager.DeselectAll();
selected.forEach(d => this.props.removeDocument(d));
- const newCollection = Doc.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
+ const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2);
this.props.addDocument(newCollection!);
this.props.selectDocuments([newCollection!], []);
MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -528,7 +527,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
this.cleanupInteractions(false);
}
- if (e.key === "r") {
+ if (e.key === "r" || e.key === " ") {
this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
@@ -538,7 +537,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
changeFreeHand = (x: boolean) => {
- this._freeHand = x;
+ this._freeHand = !this._freeHand;
}
// @action
// marqueeInkSelect(ink: Map<any, any>) {
@@ -685,7 +684,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@computed
get marqueeDiv() {
- const p: [number, number] = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
+ const p = this._visible ? this.props.getContainerTransform().transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY) : [0, 0];
const v = this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
/**
* @RE - The commented out span below
@@ -698,7 +697,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
width: `${Math.abs(v[0])}`,
height: `${Math.abs(v[1])}`, zIndex: 2000
}} >
- {/* <span className="marquee-legend" /> */}
+ <span className="marquee-legend"></span>
</div>;
} else {
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 509f78021..f934945a6 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -37,7 +37,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
return min + rnd * (max - min);
}
get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
- get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X}px, ${this.Y}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
+ get maskCentering() { return this.props.Document.isInkMask ? 2500 : 0; }
+ get transform() { return `scale(${this.props.ContentScaling()}) translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.random(-1, 1) * this.props.jitterRotation}deg)`; }
get X() { return this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
get Y() { return this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
get Opacity() { return this.dataProvider ? this.dataProvider.opacity : Cast(this.layoutDoc.opacity, "number", null); }
@@ -80,8 +81,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setValues(time: number, d: Doc, x?: number, y?: number, opacity?: number) {
const timecode = Math.round(time);
- Cast(d["x-indexed"], listSpec("number"), [])[Math.max(0, timecode - 1)] = x as any as number;
- Cast(d["y-indexed"], listSpec("number"), [])[Math.max(0, timecode - 1)] = y as any as number;
Cast(d["x-indexed"], listSpec("number"), [])[timecode] = x as any as number;
Cast(d["y-indexed"], listSpec("number"), [])[timecode] = y as any as number;
Cast(d["opacity-indexed"], listSpec("number"), null)[timecode] = opacity as any as number;
@@ -111,12 +110,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
const xlist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
const ylist = new List<number>(numberRange(timecode + 1).map(i => undefined) as any as number[]);
const olist = new List<number>(numberRange(timecode + 1).map(t => progressivize && t < i ? 0 : 1));
- xlist[Math.max(curTimecode - 1, 0)] = xlist[curTimecode] = NumCast(doc.x);
- ylist[Math.max(curTimecode - 1, 0)] = ylist[curTimecode] = NumCast(doc.y);
+ xlist[curTimecode] = NumCast(doc.x);
+ ylist[curTimecode] = NumCast(doc.y);
doc["x-indexed"] = xlist;
doc["y-indexed"] = ylist;
doc["opacity-indexed"] = olist;
- doc.activeFrame = ComputedField.MakeFunction("self.context ? (self.context.currentFrame||0) : 0");
+ doc.activeFrame = ComputedField.MakeFunction("self.context?.currentFrame||0");
doc.x = ComputedField.MakeInterpolated("x", "activeFrame");
doc.y = ComputedField.MakeInterpolated("y", "activeFrame");
doc.opacity = ComputedField.MakeInterpolated("opacity", "activeFrame");
@@ -152,12 +151,12 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
outline: this.Highlight ? "orange solid 2px" : "",
transform: this.transform,
transition: this.props.transition ? this.props.transition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.transition),
- width: this.width,
- height: this.height,
+ width: this.props.Document.isInkMask ? 5000 : this.width,
+ height: this.props.Document.isInkMask ? 5000 : this.height,
zIndex: this.ZInd,
mixBlendMode: StrCast(this.layoutDoc.mixBlendMode) as any,
display: this.ZInd === -99 ? "none" : undefined,
- pointerEvents: this.props.Document.isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK ? "none" : this.props.pointerEvents ? "all" : undefined
+ pointerEvents: this.props.Document.isBackground || this.Opacity === 0 || this.props.Document.type === DocumentType.INK || this.props.Document.isInkMask ? "none" : this.props.pointerEvents ? "all" : undefined
}} >
{Doc.UserDoc().renderStyle !== "comic" ? (null) :
<div style={{ width: "100%", height: "100%", position: "absolute" }}>
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 9f2b0124a..1d98a2628 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -43,6 +43,7 @@ import "./DocumentView.scss";
import { LinkAnchorBox } from './LinkAnchorBox';
import { RadialMenu } from './RadialMenu';
import React = require("react");
+import { undo } from 'prosemirror-history';
library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight,
fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale,
@@ -681,6 +682,17 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.Document.lockedPosition = this.Document.lockedPosition ? undefined : true;
}
+ @undoBatch
+ @action
+ setAcl = (acl: "readOnly" | "addOnly" | "ownerOnly") => {
+ this.layoutDoc.ACL = this.dataDoc.ACL = acl;
+ DocListCast(this.dataDoc[Doc.LayoutFieldKey(this.dataDoc)]).map(d => {
+ if (d.author === Doc.CurrentUserEmail) d.ACL = acl;
+ const data = d[DataSym];
+ if (data && data.author === Doc.CurrentUserEmail) data.ACL = acl;
+ });
+ }
+
@action
onContextMenu = async (e: React.MouseEvent | Touch): Promise<void> => {
// the touch onContextMenu is button 0, the pointer onContextMenu is button 2
@@ -741,9 +753,9 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const more = cm.findByDescription("More...");
const moreItems: ContextMenuProps[] = more && "subitems" in more ? more.subitems : [];
- moreItems.push({ description: "Make Add Only", event: () => this.dataDoc.ACL = this.layoutDoc.ACL = "addOnly", icon: "concierge-bell" });
- moreItems.push({ description: "Make Read Only", event: () => this.dataDoc.ACL = this.layoutDoc.ACL = "readOnly", icon: "concierge-bell" });
- moreItems.push({ description: "Make Private", event: () => this.dataDoc.ACL = this.layoutDoc.ACL = "noAccess", icon: "concierge-bell" });
+ moreItems.push({ description: "Make Add Only", event: () => this.setAcl("addOnly"), icon: "concierge-bell" });
+ moreItems.push({ description: "Make Read Only", event: () => this.setAcl("readOnly"), icon: "concierge-bell" });
+ moreItems.push({ description: "Make Private", event: () => this.setAcl("ownerOnly"), icon: "concierge-bell" });
moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
moreItems.push({ description: `${this.Document._chromeStatus !== "disabled" ? "Hide" : "Show"} Chrome`, event: () => this.Document._chromeStatus = (this.Document._chromeStatus !== "disabled" ? "disabled" : "enabled"), icon: "project-diagram" });
moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" });
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 493f23dc4..fae216f17 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -248,7 +248,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
_pdfjsRequested = false;
render() {
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField, null);
- if (this.props.isSelected() || this.props.renderDepth <= 1 || this.props.Document.scrollY !== undefined) this._everActive = true;
+ if (this.props.isSelected() || this.props.renderDepth <= 1 || this.props.Document._scrollY !== undefined) this._everActive = true;
if (pdfUrl && (this._everActive || this.props.Document._scrollTop || (this.dataDoc[this.props.fieldKey + "-nativeWidth"] && this.props.ScreenToLocalTransform().Scale < 2.5))) {
if (pdfUrl instanceof PdfField && this._pdf) {
return this.renderPdfView;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index d75b864cf..f7dee0896 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -18,6 +18,7 @@ import { ContextMenuProps } from "../ContextMenuItem";
import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from './FieldView';
import "./ScreenshotBox.scss";
+import { InkTool } from "../../../fields/InkField";
const path = require('path');
type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
@@ -132,7 +133,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
}
@computed get content() {
- const interactive = Doc.GetSelectedTool() || !this.props.isSelected() ? "" : "-interactive";
+ const interactive = Doc.GetSelectedTool() !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
const style = "videoBox-content" + interactive;
return <video className={`${style}`} key="video" autoPlay={this._screenCapture} ref={this.setVideoRef}
style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 1e0a859d3..e4dbceca6 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -228,7 +228,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@computed get content() {
const field = Cast(this.dataDoc[this.fieldKey], VideoField);
- const interactive = Doc.GetSelectedTool() || !this.props.isSelected() ? "" : "-interactive";
+ const interactive = Doc.GetSelectedTool() !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
const style = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
return !field ? <div>Loading</div> :
<video className={`${style}`} key="video" autoPlay={this._screenCapture} ref={this.setVideoRef}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 8fdde61e0..fb9e57b51 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -57,15 +57,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
iframe.contentDocument.addEventListener('pointerdown', this.iframedown, false);
iframe.contentDocument.addEventListener('scroll', this.iframeScrolled, false);
this.layoutDoc.scrollHeight = iframe.contentDocument.children?.[0].scrollHeight || 1000;
- iframe.contentDocument.children[0].scrollTop = NumCast(this.layoutDoc.scrollTop);
- iframe.contentDocument.children[0].scrollLeft = NumCast(this.layoutDoc.scrollLeft);
+ iframe.contentDocument.children[0].scrollTop = NumCast(this.layoutDoc._scrollTop);
+ iframe.contentDocument.children[0].scrollLeft = NumCast(this.layoutDoc._scrollLeft);
}
this._reactionDisposer?.();
- this._reactionDisposer = reaction(() => ({ y: this.layoutDoc.scrollY, x: this.layoutDoc.scrollX }),
+ this._reactionDisposer = reaction(() => ({ y: this.layoutDoc._scrollY, x: this.layoutDoc._scrollX }),
({ x, y }) => {
if (y !== undefined) {
this._outerRef.current!.scrollTop = y;
- this.layoutDoc.scrollY = undefined;
+ this.layoutDoc._scrollY = undefined;
}
if (x !== undefined) {
this._outerRef.current!.scrollLeft = x;
@@ -82,8 +82,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
iframeScrolled = (e: any) => {
const scrollTop = e.target?.children?.[0].scrollTop;
const scrollLeft = e.target?.children?.[0].scrollLeft;
- this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scrollTop;
- this.layoutDoc.scrollLeft = this._outerRef.current!.scrollLeft = scrollLeft;
+ this.layoutDoc._scrollTop = this._outerRef.current!.scrollTop = scrollTop;
+ this.layoutDoc._scrollLeft = this._outerRef.current!.scrollLeft = scrollLeft;
}
async componentDidMount() {
const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField);
@@ -439,7 +439,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
{this.urlEditor()}
</>);
}
- scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc.scrollLeft), NumCast(this.layoutDoc.scrollTop));
+ scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop));
render() {
return (<div className={`webBox-container`}
style={{
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 05e6a5959..55b3f6f1e 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -7,7 +7,7 @@ import { ComputedField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, Utils, returnZero } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
-import { Docs } from "../../../documents/Documents";
+import { Docs, DocUtils } from "../../../documents/Documents";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
import { Transform } from "../../../util/Transform";
@@ -48,7 +48,7 @@ export class DashDocView extends React.Component<IDashDocView> {
if (dashDocBase instanceof Doc) {
const aliasedDoc = Doc.MakeAlias(dashDocBase, docid + alias);
aliasedDoc.layoutKey = "layout";
- node.attrs.fieldKey && Doc.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined);
+ node.attrs.fieldKey && DocUtils.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined);
this._dashDoc = aliasedDoc;
// self.doRender(aliasedDoc, removeDoc, node, view, getPos);
}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index d05e8f1ea..9a1b909c1 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -12,6 +12,7 @@ import React = require("react");
import * as ReactDOM from 'react-dom';
import "./DashFieldView.scss";
import { observer } from "mobx-react";
+import { DocUtils } from "../../../documents/Documents";
export class DashFieldView {
@@ -117,7 +118,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
@action
fieldSpanKeyDown = (e: KeyboardEvent, span: HTMLSpanElement) => {
if (e.key === "Enter") { // handle the enter key by "submitting" the current text to Dash's database.
- e.ctrlKey && Doc.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: span.textContent! }]);
+ e.ctrlKey && DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: span.textContent! }]);
this.updateText(span.textContent!, true);
e.preventDefault();// prevent default to avoid a newline from being generated and wiping out this field view
}
@@ -147,7 +148,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
(options instanceof Doc) && DocListCast(options.data).forEach(opt => (forceMatch ? StrCast(opt.title).startsWith(newText) : StrCast(opt.title) === newText) && (modText = StrCast(opt.title)));
if (modText) {
// elementfieldSpan.innerHTML = this._dashDoc![this._fieldKey as string] = modText;
- Doc.addFieldEnumerations(this._textBoxDoc, this._fieldKey, []);
+ DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, []);
this._dashDoc![this._fieldKey] = modText;
} // if the text starts with a ':=' then treat it as an expression by making a computed field from its value storing it in the key
else if (nodeText.startsWith(":=")) {
@@ -167,7 +168,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
// display a collection of all the enumerable values for this field
onPointerDownEnumerables = async (e: any) => {
e.stopPropagation();
- const collview = await Doc.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]);
+ const collview = await DocUtils.addFieldEnumerations(this._textBoxDoc, this._fieldKey, [{ title: this._fieldKey }]);
collview instanceof Doc && this.props.tbox.props.addDocTab(collview, "onRight");
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 477a2ca08..49114d967 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -95,6 +95,17 @@
.formattedTextBox-inner {
height: 100%;
white-space: pre-wrap;
+ hr {
+ display: block;
+ unicode-bidi: isolate;
+ margin-block-start: 0.5em;
+ margin-block-end: 0.5em;
+ margin-inline-start: auto;
+ margin-inline-end: auto;
+ overflow: hidden;
+ border-style: inset;
+ border-width: 1px;
+ }
}
// .menuicon {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2c1d93222..1fab54d7e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -719,7 +719,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
},
{ fireImmediately: true }
);
- this._disposers.scroll = reaction(() => NumCast(this.layoutDoc.scrollPos),
+ this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop),
pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true }
);
@@ -1172,7 +1172,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}
onscrolled = (ev: React.UIEvent) => {
- this.layoutDoc.scrollPos = this._scrollRef.current!.scrollTop;
+ this.layoutDoc._scrollTop = this._scrollRef.current!.scrollTop;
}
@action
tryUpdateHeight(limitHeight?: number) {
@@ -1211,7 +1211,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
TraceMobx();
const scale = this.props.ContentScaling() * NumCast(this.layoutDoc.scale, 1);
const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
- const interactive = Doc.GetSelectedTool() || this.layoutDoc.isBackground;
+ const interactive = Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc.isBackground;
if (this.props.isSelected()) {
this._editorView && RichTextMenu.Instance.updateFromDash(this._editorView, undefined, this.props);
} else if (FormattedTextBoxComment.textBox === this) {
@@ -1232,7 +1232,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
background: Doc.UserDoc().renderStyle === "comic" ? "transparent" : this.props.background ? this.props.background : StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], this.props.hideOnLeave ? "rgba(0,0,0 ,0.4)" : ""),
opacity: this.props.hideOnLeave ? (this._entered ? 1 : 0.1) : 1,
color: this.props.color ? this.props.color : StrCast(this.layoutDoc[this.props.fieldKey + "-color"], this.props.hideOnLeave ? "white" : "inherit"),
- pointerEvents: interactive ? "none" : undefined,
+ pointerEvents: interactive ? undefined : "none",
fontSize: Cast(this.layoutDoc._fontSize, "number", null),
fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit")
}}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index d47ae63af..59a6045ab 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -183,7 +183,7 @@ export class FormattedTextBoxComment {
const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc);
const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor;
if (anchor !== target && anchor && target) {
- target.scrollY = NumCast(anchor?.y);
+ target._scrollY = NumCast(anchor?.y);
}
if (target) {
ReactDOM.render(<ContentFittingDocumentView
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 91280dea4..1cc7ec8bf 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -18,7 +18,7 @@ import { ComputedField } from "../../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast, FieldValue } from "../../../../fields/Types";
import { emptyFunction, returnEmptyString, returnFalse, returnOne, Utils, returnZero } from "../../../../Utils";
import { DocServer } from "../../../DocServer";
-import { Docs } from "../../../documents/Documents";
+import { Docs, DocUtils } from "../../../documents/Documents";
import { CollectionViewType } from "../../collections/CollectionView";
import { DocumentView } from "../DocumentView";
import { FormattedTextBox } from "./FormattedTextBox";
@@ -245,7 +245,7 @@ export class DashDocView {
if (dashDocBase instanceof Doc) {
const aliasedDoc = Doc.MakeAlias(dashDocBase, docid + alias);
aliasedDoc.layoutKey = "layout";
- node.attrs.fieldKey && Doc.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined);
+ node.attrs.fieldKey && DocUtils.makeCustomViewClicked(aliasedDoc, Docs.Create.StackingDocument, node.attrs.fieldKey, undefined);
self.doRender(aliasedDoc, removeDoc, node, view, getPos);
}
});
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 8cf5026c8..e39e96607 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -36,9 +36,7 @@ import { Networking } from "../../Network";
export const pageSchema = createSchema({
curPage: "number",
- fitWidth: "boolean",
rotation: "number",
- scrollY: "number",
scrollHeight: "number",
serachMatch: "boolean"
});
@@ -93,7 +91,6 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@observable private _showWaiting = true;
@observable private _showCover = false;
@observable private _zoomed = 1;
- @observable private _scrollTop = 0;
private _pdfViewer: any;
private _retries = 0; // number of times tried to create the PDF viewer
@@ -141,6 +138,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
this._coverPath = href.startsWith(window.location.origin) ? await Networking.PostToServer("/thumbnail", params) : { width: 100, height: 100, path: "" };
runInAction(() => this._showWaiting = this._showCover = true);
this.props.startupLive && this.setupPdfJsViewer();
+ this._mainCont.current!.scrollTop = this.layoutDoc._scrollTop || 0;
this._searchReactionDisposer = reaction(() => this.Document.searchMatch, search => {
if (search) {
this.search(Doc.SearchQuery(), true);
@@ -156,14 +154,12 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
() => (SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(),
{ fireImmediately: true });
this._reactionDisposer = reaction(
- () => this.Document.scrollY,
+ () => this.Document._scrollY,
(scrollY) => {
if (scrollY !== undefined) {
- if (this._showCover || this._showWaiting) {
- this.setupPdfJsViewer();
- }
- this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document.scrollY || 0));
- this.Document.scrollY = undefined;
+ (this._showCover || this._showWaiting) && this.setupPdfJsViewer();
+ this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0));
+ setTimeout(() => this.Document._scrollY = undefined, 1000);
}
},
{ fireImmediately: true }
@@ -218,7 +214,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
await this.initialLoad();
this._scrollTopReactionDisposer = reaction(() => Cast(this.layoutDoc._scrollTop, "number", null),
- (stop) => (stop !== undefined) && this._mainCont.current && smoothScroll(500, this._mainCont.current, stop), { fireImmediately: true });
+ (stop) => (stop !== undefined && this.layoutDoc._scrollY === undefined) && (this._mainCont.current!.scrollTop = stop), { fireImmediately: true });
+
this._annotationReactionDisposer = reaction(
() => DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]),
annotations => annotations?.length && (this._annotations = annotations),
@@ -350,7 +347,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
@action
onScroll = (e: React.UIEvent<HTMLElement>) => {
- this._scrollTop = this._mainCont.current!.scrollTop;
+ this.Document._scrollY === undefined && (this.layoutDoc._scrollTop = this._mainCont.current!.scrollTop);
this._pdfViewer && (this.Document.curPage = this._pdfViewer.currentPageNumber);
}
@@ -602,7 +599,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu
}
scrollXf = () => {
- return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform();
+ return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this.layoutDoc._scrollTop || 0) : this.props.ScreenToLocalTransform();
}
onClick = (e: React.MouseEvent) => {
this._setPreviewCursor &&
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 83de9cc15..647e1ce6f 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -11,6 +11,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { IconProp, library } from '@fortawesome/fontawesome-svg-core';
import { faSync, faPhoneSlash } from "@fortawesome/free-solid-svg-icons";
import { Doc } from "../../../fields/Doc";
+import { InkTool } from "../../../fields/InkField";
library.add(faSync);
library.add(faPhoneSlash);
@@ -72,7 +73,7 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
</div >;
const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
- const classname = "webBox-cont" + (this.props.isSelected() && !Doc.GetSelectedTool() && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+ const classname = "webBox-cont" + (this.props.isSelected() && Doc.GetSelectedTool() === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
return (
<>
diff --git a/src/client/views/webcam/WebCamLogic.js b/src/client/views/webcam/WebCamLogic.js
index a8a2f5fa4..5f6202bc8 100644
--- a/src/client/views/webcam/WebCamLogic.js
+++ b/src/client/views/webcam/WebCamLogic.js
@@ -1,8 +1,5 @@
'use strict';
import io from "socket.io-client";
-import {
- resolvedPorts
-} from "../Main";
var socket;
var isChannelReady = false;
@@ -32,7 +29,7 @@ export function initialize(roomName, handlerUI) {
room = roomName;
- socket = io.connect(`${window.location.protocol}//${window.location.hostname}:${resolvedPorts.socket}`);
+ socket = io.connect(`${window.location.protocol}//${window.location.hostname}:4321`);
if (room !== '') {
socket.emit('create or join', room);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 981025483..ffef9a384 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -93,7 +93,7 @@ export const HeightSym = Symbol("Height");
export const DataSym = Symbol("Data");
export const LayoutSym = Symbol("Layout");
export const AclSym = Symbol("Acl");
-export const AclPrivate = Symbol("AclNoAccess");
+export const AclPrivate = Symbol("AclOwnerOnly");
export const AclReadonly = Symbol("AclReadOnly");
export const AclAddonly = Symbol("AclAddonly");
export const UpdatingFromServer = Symbol("UpdatingFromServer");
@@ -102,13 +102,17 @@ const CachedUpdates = Symbol("Cached updates");
function fetchProto(doc: Doc) {
if (doc.author !== Doc.CurrentUserEmail) {
- if (doc.ACL === "noAccess") {
- doc[AclSym] = AclPrivate;
- return undefined;
- } else if (doc.ACL === "readOnly") {
- doc[AclSym] = AclReadonly;
- } else if (doc.ACL === "addOnly") {
- doc[AclSym] = AclAddonly;
+ const acl = Doc.Get(doc, "ACL", true);
+ switch (acl) {
+ case "ownerOnly":
+ doc[AclSym] = AclPrivate;
+ return undefined;
+ case "readOnly":
+ doc[AclSym] = AclReadonly;
+ break;
+ case "addOnly":
+ doc[AclSym] = AclAddonly;
+ break;
}
}
@@ -116,7 +120,7 @@ function fetchProto(doc: Doc) {
if (proto instanceof Promise) {
proto.then(proto => {
if (proto.author !== Doc.CurrentUserEmail) {
- if (proto.ACL === "noAccess") {
+ if (proto.ACL === "ownerOnly") {
proto[AclSym] = doc[AclSym] = AclPrivate;
return undefined;
} else if (proto.ACL === "readOnly") {
@@ -483,6 +487,7 @@ export namespace Doc {
}
alias.aliasOf = doc;
alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`);
+ alias.author = Doc.CurrentUserEmail;
return alias;
}
@@ -685,7 +690,7 @@ export namespace Doc {
}
}
});
-
+ copy.author = Doc.CurrentUserEmail;
return copy;
}
@@ -817,7 +822,7 @@ export namespace Doc {
export function UserDoc(): Doc { return manager._user_doc; }
export function SetSelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
- export function GetSelectedTool(): InkTool { return (FieldValue(StrCast(Doc.UserDoc().activeInkTool)) ?? InkTool.None) as InkTool; }
+ export function GetSelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
export function SetUserDoc(doc: Doc) { manager._user_doc = doc; }
export function IsBrushed(doc: Doc) {
return computedFn(function IsBrushed(doc: Doc) {
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index c475d0d73..f81ec8c6d 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -3,7 +3,7 @@ import { docs_v1 } from "googleapis";
import { Fragment, Mark, Node } from "prosemirror-model";
import { sinkListItem } from "prosemirror-schema-list";
import { Utils } from "../Utils";
-import { Docs } from "../client/documents/Documents";
+import { Docs, DocUtils } from "../client/documents/Documents";
import { schema } from "../client/views/nodes/formattedText/schema_rts";
import { GooglePhotos } from "../client/apis/google_docs/GooglePhotosClientUtils";
import { DocServer } from "../client/DocServer";
@@ -272,7 +272,7 @@ export namespace RichTextUtils {
const backingDocId = StrCast(textNote[guid]);
if (!backingDocId) {
const backingDoc = Docs.Create.ImageDocument(agnostic, { _width: 300, _height: 300 });
- Doc.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument);
+ DocUtils.makeCustomViewClicked(backingDoc, Docs.Create.FreeformDocument);
docid = backingDoc[Id];
textNote[guid] = docid;
} else {
@@ -401,7 +401,7 @@ export namespace RichTextUtils {
let exported = (await Cast(linkDoc.anchor2, Doc))!;
if (!exported.customLayout) {
exported = Doc.MakeAlias(exported);
- Doc.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
+ DocUtils.makeCustomViewClicked(exported, Docs.Create.FreeformDocument);
linkDoc.anchor2 = exported;
}
url = Utils.shareUrl(exported[Id]);
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index 31f589ec0..40dadf5a8 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -22,10 +22,10 @@ export const documentSchema = createSchema({
y: "number", // y coordinate when in a freeform view
z: "number", // z "coordinate" - non-zero specifies the overlay layer of a freeformview
zIndex: "number", // zIndex of a document in a freeform view
- scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
- scrollX: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
- scrollTop: "number", // scroll position of a scrollable document (pdf, text, web)
- scrollLeft: "number", // scroll position of a scrollable document (pdf, text, web)
+ _scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
+ _scrollX: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that )
+ _scrollTop: "number", // scroll position of a scrollable document (pdf, text, web)
+ _scrollLeft: "number", // scroll position of a scrollable document (pdf, text, web)
// appearance properties on the layout
_autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents
@@ -58,6 +58,7 @@ export const documentSchema = createSchema({
color: "string", // foreground color of document
fitToBox: "boolean", // whether freeform view contents should be zoomed/panned to fill the area of the document view
fontSize: "string",
+ isInkMask: "boolean", // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
layout: "string", // this is the native layout string for the document. templates can be added using other fields and setting layoutKey below
layoutKey: "string", // holds the field key for the field that actually holds the current lyoat
letterSpacing: "string",
@@ -83,6 +84,7 @@ export const documentSchema = createSchema({
_lockedTransform: "boolean",// whether a freeformview can pan/zoom
// drag drop properties
+ stayInCollection: "boolean",// whether document can be dropped into a different collection
dragFactory: Doc, // the document that serves as the "template" for the onDragStart script. ie, to drag out copies of the dragFactory document.
dropAction: "string", // override specifying what should happen when this document is dropped (can be "alias", "copy", "move")
targetDropAction: "string", // allows the target of a drop event to specify the dropAction ("alias", "copy", "move") NOTE: if the document is dropped within the same collection, the dropAction is coerced to 'move'
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 756bde738..fe39b84e6 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -301,8 +301,7 @@ async function captureYoutubeScreenshot(targetUrl: string): Promise<Opt<Buffer>>
await videoPlayer?.click();
await delay(1000);
// hide youtube player controls.
- await page.evaluate(() =>
- (document.querySelector('.ytp-chrome-bottom') as any).style.display = 'none');
+ await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
await browser.close();