aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
authorusodhi <61431818+usodhi@users.noreply.github.com>2020-11-17 19:28:30 +0530
committerusodhi <61431818+usodhi@users.noreply.github.com>2020-11-17 19:28:30 +0530
commit86408e6d93fbe6501694371736fe74b81ed39cf3 (patch)
tree17b89a6209c66284f89e2636a8157435ce1045c0 /src/client/views/nodes
parenta002e0e5c5910f78c8f3910ad4101386d30ebf70 (diff)
parent28dccafaa4aa446dd88c1b6f4218a0d7f79fa1bb (diff)
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into acls_uv
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx26
-rw-r--r--src/client/views/nodes/ColorBox.tsx4
-rw-r--r--src/client/views/nodes/ComparisonBox.scss2
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx10
-rw-r--r--src/client/views/nodes/ContentFittingDocumentView.tsx16
-rw-r--r--src/client/views/nodes/DocumentLinksButton.scss6
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx109
-rw-r--r--src/client/views/nodes/DocumentView.tsx68
-rw-r--r--src/client/views/nodes/FieldView.tsx3
-rw-r--r--src/client/views/nodes/ImageBox.tsx13
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx17
-rw-r--r--src/client/views/nodes/PDFBox.scss2
-rw-r--r--src/client/views/nodes/PDFBox.tsx11
-rw-r--r--src/client/views/nodes/PresBox.scss8
-rw-r--r--src/client/views/nodes/PresBox.tsx1182
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx14
-rw-r--r--src/client/views/nodes/VideoBox.scss3
-rw-r--r--src/client/views/nodes/VideoBox.tsx117
-rw-r--r--src/client/views/nodes/WebBox.tsx196
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx2
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx43
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx4
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx8
-rw-r--r--src/client/views/nodes/formattedText/RichTextSchema.tsx2
25 files changed, 1107 insertions, 762 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5a8ce4e14..c87239ee9 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -16,7 +16,7 @@ import { ComputedField } from "../../../fields/ScriptField";
import { listSpec } from "../../../fields/Schema";
import { DocumentType } from "../../documents/DocumentTypes";
import { Zoom, Fade, Flip, Rotate, Bounce, Roll, LightSpeed } from 'react-reveal';
-import { PresBox } from "./PresBox";
+import { PresBox, PresEffect } from "./PresBox";
import { InkingStroke } from "../InkingStroke";
export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
@@ -55,8 +55,8 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
@computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
@computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); }
- @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc._nativeWidth, this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0)); }
- @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc._nativeHeight, this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0)); }
+ @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, undefined, this.freezeDimensions)); }
+ @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, undefined, this.freezeDimensions)); }
public static getValues(doc: Doc, time: number) {
const timecode = Math.round(time);
@@ -212,23 +212,23 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
PanelHeight={this.panelHeight} />;
if (PresBox.Instance && this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc) {
const effectProps = {
- left: this.layoutDoc.presEffectDirection === 'left',
- right: this.layoutDoc.presEffectDirection === 'right',
- top: this.layoutDoc.presEffectDirection === 'top',
- bottom: this.layoutDoc.presEffectDirection === 'bottom',
+ left: this.layoutDoc.presEffectDirection === PresEffect.Left,
+ right: this.layoutDoc.presEffectDirection === PresEffect.Right,
+ top: this.layoutDoc.presEffectDirection === PresEffect.Top,
+ bottom: this.layoutDoc.presEffectDirection === PresEffect.Bottom,
opposite: true,
delay: this.layoutDoc.presTransition,
// when: this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc,
};
switch (this.layoutDoc.presEffect) {
case "Zoom": return (<Zoom {...effectProps}>{node}</Zoom>); break;
- case "Fade": return (<Fade {...effectProps}>{node}</Fade>); break;
- case "Flip": return (<Flip {...effectProps}>{node}</Flip>); break;
- case "Rotate": return (<Rotate {...effectProps}>{node}</Rotate>); break;
- case "Bounce": return (<Bounce {...effectProps}>{node}</Bounce>); break;
- case "Roll": return (<Roll {...effectProps}>{node}</Roll>); break;
+ case PresEffect.Fade: return (<Fade {...effectProps}>{node}</Fade>); break;
+ case PresEffect.Flip: return (<Flip {...effectProps}>{node}</Flip>); break;
+ case PresEffect.Rotate: return (<Rotate {...effectProps}>{node}</Rotate>); break;
+ case PresEffect.Bounce: return (<Bounce {...effectProps}>{node}</Bounce>); break;
+ case PresEffect.Roll: return (<Roll {...effectProps}>{node}</Roll>); break;
case "LightSpeed": return (<LightSpeed {...effectProps}>{node}</LightSpeed>); break;
- case "None": return node; break;
+ case PresEffect.None: return node; break;
default: return node; break;
}
} else {
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index fcc9e50f5..4fb350b55 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -69,11 +69,11 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument
SetActiveInkWidth(e.target.value);
SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value));
}} />
- <div> {ActiveInkBezierApprox() ?? 2}</div>
+ {/* <div> {ActiveInkBezierApprox() ?? 2}</div>
<input type="range" defaultValue={ActiveInkBezierApprox() ?? 2} min={0} max={300} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
SetActiveBezierApprox(e.target.value);
SelectionManager.SelectedDocuments().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeBezier = e.target.value);
- }} />
+ }} /> */}
<br />
<br />
</div>
diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss
index acf6b1636..44cf5d046 100644
--- a/src/client/views/nodes/ComparisonBox.scss
+++ b/src/client/views/nodes/ComparisonBox.scss
@@ -13,6 +13,7 @@
left: 0;
height: 100%;
overflow: hidden;
+ transition: 200ms;
.beforeBox-cont {
height: 100%;
@@ -26,6 +27,7 @@
width: 3px;
display: inline-block;
background: white;
+ transition: 200ms;
.slide-handle {
position: absolute;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 2af5b3182..0ba53dee6 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -51,9 +51,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C
private registerSliding = (e: React.PointerEvent<HTMLDivElement>, targetWidth: number) => {
setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, action(() => {
// on click, animate slider movement to the targetWidth
- this._animating = "all 1s";
+ this._animating = "all 200ms";
this.layoutDoc._clipWidth = targetWidth * 100 / this.props.PanelWidth();
- setTimeout(action(() => this._animating = ""), 1000);
+ setTimeout(action(() => this._animating = ""), 200);
}), false);
}
@@ -102,12 +102,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps, C
return (
<div className={`comparisonBox${this.active() || SnappingManager.GetIsDragging() ? "-interactive" : ""}` /* change className to easily disable/enable pointer events in CSS */}>
- {displayBox("after", 1, this.props.PanelWidth() - 5)}
+ {displayBox("after", 1, this.props.PanelWidth() - 3)}
<div className="clip-div" style={{ width: clipWidth, transition: this._animating, background: StrCast(this.layoutDoc._backgroundColor, "gray") }}>
- {displayBox("before", 0, 5)}
+ {displayBox("before", 0, 0)}
</div>
- <div className="slide-bar" style={{ left: `calc(${clipWidth} - 0.5px)` }}
+ <div className="slide-bar" style={{ left: `calc(${clipWidth} - 0.5px)`, cursor: NumCast(this.layoutDoc._clipWidth) < 5 ? "e-resize" : NumCast(this.layoutDoc._clipWidth) / 100 > (this.props.PanelWidth() - 5) / this.props.PanelWidth() ? "w-resize" : undefined }}
onPointerDown={e => this.registerSliding(e, this.props.PanelWidth() / 2)} /* if clicked, return slide-bar to center */ >
<div className="slide-handle" />
</div>
diff --git a/src/client/views/nodes/ContentFittingDocumentView.tsx b/src/client/views/nodes/ContentFittingDocumentView.tsx
index 09051da78..0c52b9044 100644
--- a/src/client/views/nodes/ContentFittingDocumentView.tsx
+++ b/src/client/views/nodes/ContentFittingDocumentView.tsx
@@ -22,12 +22,8 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
}
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
- nativeWidth = () => returnVal(this.props.NativeWidth?.(),
- NumCast(this.layoutDoc?._nativeWidth || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeWidth"],
- (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[WidthSym]() : this.props.PanelWidth())))
- nativeHeight = () => returnVal(this.props.NativeHeight?.(),
- NumCast(this.layoutDoc?._nativeHeight || this.props.DataDoc?.[Doc.LayoutFieldKey(this.layoutDoc) + "-nativeHeight"],
- (this.freezeDimensions && this.layoutDoc ? this.layoutDoc[HeightSym]() : this.props.PanelHeight())))
+ nativeWidth = () => returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || this.props.PanelWidth());
+ nativeHeight = () => returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || this.props.PanelHeight());
@computed get scaling() {
const wscale = this.props.PanelWidth() / this.nativeWidth();
const hscale = this.props.PanelHeight() / this.nativeHeight();
@@ -42,7 +38,13 @@ export class ContentFittingDocumentView extends React.Component<DocumentViewProp
private PanelHeight = () => this.panelHeight;
@computed get panelWidth() { return this.nativeWidth() && !this.props.Document._fitWidth ? this.nativeWidth() * this.contentScaling() : this.props.PanelWidth(); }
- @computed get panelHeight() { return this.nativeHeight() && !this.props.Document._fitWidth ? this.nativeHeight() * this.contentScaling() : this.props.PanelHeight(); }
+ @computed get panelHeight() {
+ if (this.nativeHeight()) {
+ if (!this.props.Document._fitWidth) return this.nativeHeight() * this.contentScaling();
+ else return this.panelWidth / Doc.NativeAspect(this.layoutDoc, this.props.DataDoc, this.freezeDimensions) || 1;
+ }
+ return this.props.PanelHeight();
+ }
@computed get childXf() { return this.props.DataDoc ? 1 : 1 / this.contentScaling(); } // this is intended to detect when a document is being rendered inside itself as part of a template, but not as a leaf node where nativeWidth & height would apply.
private getTransform = () => this.props.dontCenter ?
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 9328fb96b..735aa669f 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,6 +1,11 @@
@import "../globalCssVariables.scss";
+.documentLinksButton-cont {
+ min-width: 20;
+ min-height: 20;
+ position: absolute;
+}
.documentLinksButton,
.documentLinksButton-endLink,
.documentLinksButton-startLink {
@@ -29,6 +34,7 @@
.documentLinksButton {
background-color: black;
+ font-weight: bold;
&:hover {
background: $main-accent;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index ddfb3cc34..95c007175 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -3,7 +3,6 @@ import { Tooltip } from "@material-ui/core";
import { action, computed, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { DocumentType } from "../../documents/DocumentTypes";
import { emptyFunction, setupMoveUpEvents, returnFalse, Utils, emptyPath } from "../../../Utils";
import { TraceMobx } from "../../../fields/util";
import { DocUtils, Docs } from "../../documents/Documents";
@@ -39,6 +38,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
@observable public static StartLinkView: DocumentView | undefined;
@observable public static AnnotationId: string | undefined;
@observable public static AnnotationUri: string | undefined;
+ @observable public static EditLink: DocumentView | undefined;
@observable public static invisibleWebDoc: Opt<Doc>;
public static invisibleWebRef = React.createRef<HTMLDivElement>();
@@ -209,94 +209,75 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
}
}));
- @observable
- public static EditLink: DocumentView | undefined;
@action clearLinks() {
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
}
- @computed
- get linkButton() {
- TraceMobx();
- const links = DocUtils.FilterDocs(Array.from(new Set<Doc>(this.props.links)), this.props.View.props.docFilters(), []);
-
- const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
- const buttonTitle = "Tap to view links";
- const title = this.props.InMenu ? menuTitle : buttonTitle;
-
-
- const startLink = <img
- style={{ width: "11px", height: "11px" }}
- id={"startLink-icon"}
- src={`/assets/${"startLink.png"}`} />;
-
- const endLink = <img
- style={{ width: "14px", height: "9px" }}
- id={"endLink-icon"}
- src={`/assets/${"endLink.png"}`} />;
+ @computed get filteredLinks() {
+ return DocUtils.FilterDocs(Array.from(new Set<Doc>(this.props.links)), this.props.View.props.docFilters(), []);
+ }
- const link = <img
- style={{ width: "22px", height: "16px" }}
- id={"link-icon"}
- src={`/assets/${"link.png"}`} />;
+ @computed get linkButtonInner() {
+ const btnDim = this.props.InMenu ? "20px" : "30px";
+ const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />;
- const linkButton = <div className="documentLinksButton-cont" ref={this._linkButton} style={{
- minWidth: 20, minHeight: 20, position: "absolute",
- left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3]
- }}>
- <div className={"documentLinksButton"} style={{
- backgroundColor: this.props.InMenu ? "" : "#add8e6",
- color: this.props.InMenu ? "white" : "black",
- width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px", fontWeight: "bold"
- }}
+ return <div className="documentLinksButton-cont" ref={this._linkButton}
+ style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }}
+ >
+ <div className={"documentLinksButton"}
onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick}
- // onPointerLeave={action(() => LinkDocPreview.LinkInfo = undefined)}
- // onPointerEnter={action(e => links.length && (LinkDocPreview.LinkInfo = {
- // addDocTab: this.props.View.props.addDocTab,
- // linkSrc: this.props.View.props.Document,
- // linkDoc: links[0],
- // Location: [e.clientX, e.clientY + 20]
- // }))}
- >
-
- {/* {this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
- <FontAwesomeIcon className="documentdecorations-icon" icon="hand-paper" size="sm" /> : links.length} */}
-
- {this.props.InMenu ? this.props.StartLink ? <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> :
- link : Array.from(links).length}
-
+ style={{
+ backgroundColor: this.props.InMenu ? "" : "#add8e6",
+ color: this.props.InMenu ? "white" : "black",
+ width: btnDim,
+ height: btnDim,
+ }} >
+ {this.props.InMenu ?
+ this.props.StartLink ?
+ <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" />
+ : link
+ : Array.from(this.filteredLinks).length}
</div>
{this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ?
<div className={"documentLinksButton-endLink"}
style={{
- width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px",
+ width: btnDim, height: btnDim,
backgroundColor: DocumentLinksButton.StartLink ? "" : "grey",
opacity: DocumentLinksButton.StartLink ? "" : "50%",
border: DocumentLinksButton.StartLink ? "" : "none",
cursor: DocumentLinksButton.StartLink ? "pointer" : "default"
}}
- onPointerDown={DocumentLinksButton.StartLink ? this.completeLink : emptyFunction}
- onClick={e => DocumentLinksButton.StartLink ? DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View) : emptyFunction} /> : (null)
+ onPointerDown={DocumentLinksButton.StartLink && this.completeLink}
+ onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)} />
+ : (null)
}
- {
- DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? <div className={"documentLinksButton-startLink"}
- style={{ width: this.props.InMenu ? "20px" : "30px", height: this.props.InMenu ? "20px" : "30px" }}
- onPointerDown={this.clearLinks} onClick={this.clearLinks}
- /> : (null)
+ {DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ?
+ <div className={"documentLinksButton-startLink"} onPointerDown={this.clearLinks} onClick={this.clearLinks} style={{ width: btnDim, height: btnDim }} />
+ : (null)
}
</div >;
+ }
+
+ @computed get linkButton() {
+ TraceMobx();
+
+ const menuTitle = this.props.StartLink ? "Drag or tap to start link" : "Tap to complete link";
+ const buttonTitle = "Tap to view links";
+ const title = this.props.InMenu ? menuTitle : buttonTitle;
- return (!Array.from(links).length) && !this.props.AlwaysOn ? (null) :
+ return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) :
this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
<Tooltip title={<><div className="dash-tooltip">{title}</div></>}>
- {linkButton}
- </Tooltip> : !!!DocumentLinksButton.EditLink && !this.props.InMenu ?
+ {this.linkButtonInner}
+ </Tooltip>
+ :
+ !DocumentLinksButton.EditLink && !this.props.InMenu ?
<Tooltip title={<><div className="dash-tooltip">{title}</div></>}>
- {linkButton}
- </Tooltip> :
- linkButton;
+ {this.linkButtonInner}
+ </Tooltip>
+ : this.linkButtonInner;
}
render() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e980322d5..77f63b457 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -42,7 +42,8 @@ import { RadialMenu } from './RadialMenu';
import { TaskCompletionBox } from './TaskCompletedBox';
import React = require("react");
-export type DocFocusFunc = () => boolean;
+export type DocAfterFocusFunc = (notFocused: boolean) => boolean;
+export type DocFocusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, dontCenter?: boolean, focused?: boolean) => void;
export interface DocumentViewProps {
ContainingCollectionView: Opt<CollectionView>;
@@ -82,7 +83,7 @@ export interface DocumentViewProps {
PanelHeight: () => number;
pointerEvents?: string;
contentsPointerEvents?: string;
- focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void;
+ focus: DocFocusFunc;
parentActive: (outsideReaction: boolean) => boolean;
whenActiveChanged: (isActive: boolean) => void;
bringToFront: (doc: Doc, sendToBack?: boolean) => void;
@@ -129,8 +130,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
@computed get topMost() { return this.props.renderDepth === 0; }
@computed get freezeDimensions() { return this.props.FreezeDimensions; }
- @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeWidth"], (this.freezeDimensions ? this.layoutDoc[WidthSym]() : 0))); }
- @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), NumCast(this.layoutDoc[(this.props.DataDoc ? this.LayoutFieldKey + "-" : "_") + "nativeHeight"], (this.freezeDimensions ? this.layoutDoc[HeightSym]() : 0))); }
+ @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
+ @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.dataDoc, this.freezeDimensions)); }
@computed get onClickHandler() { return this.props.onClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null)); }
@computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
@computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
@@ -325,17 +326,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
thisContainer: this.props.ContainingCollectionDoc,
shiftKey: e.shiftKey
}, console.log);
- func();
+ undoBatch(func)();
} else if (!Doc.IsSystem(this.props.Document)) {
if (this.props.Document.type === DocumentType.INK) {
InkStrokeProperties.Instance && (InkStrokeProperties.Instance._controlBtn = true);
} else {
UndoManager.RunInBatch(() => {
- let fullScreenDoc = this.props.Document;
- if (StrCast(this.props.Document.layoutKey) !== "layout_fullScreen" && this.props.Document.layout_fullScreen) {
- fullScreenDoc = Doc.MakeAlias(this.props.Document);
- fullScreenDoc.layoutKey = "layout_fullScreen";
- }
+ const fullScreenDoc = Cast(this.props.Document._fullScreenView, Doc, null) || this.props.Document;
this.props.addDocTab(fullScreenDoc, "add");
}, "double tap");
SelectionManager.DeselectAll();
@@ -385,7 +382,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
// if the target isn't onscreen, then it will open up the target in a tab, on the right, or in place
// depending on the followLinkLocation property of the source (or the link itself as a fallback);
public static followLinkClick = async (linkDoc: Opt<Doc>, sourceDoc: Doc, docView: {
- focus: (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: DocFocusFunc) => void,
+ focus: DocFocusFunc,
addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean,
ContainingCollectionDoc?: Doc
}, shiftKey: boolean, altKey: boolean) => {
@@ -423,7 +420,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this._downX = touch.clientX;
this._downY = touch.clientY;
if (!e.nativeEvent.cancelBubble) {
- if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) e.stopPropagation();
+ if ((this.active || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
this.removeMoveListeners();
this.addMoveListeners();
this.removeEndListeners();
@@ -438,7 +435,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (e.cancelBubble && this.active) {
this.removeMoveListeners();
}
- else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
+ else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
const touch = me.touchEvent.changedTouches.item(0);
if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
@@ -494,8 +491,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
const doc = Document(this.props.Document);
const layoutDoc = Document(Doc.Layout(this.props.Document));
- let nwidth = layoutDoc._nativeWidth || 0;
- let nheight = layoutDoc._nativeHeight || 0;
+ let nwidth = Doc.NativeWidth(layoutDoc);
+ let nheight = Doc.NativeHeight(layoutDoc);
const width = (layoutDoc._width || 0);
const height = (layoutDoc._height || (nheight / nwidth * width));
const scale = this.props.ScreenToLocalTransform().Scale * this.props.ContentScaling();
@@ -505,13 +502,13 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
doc.y = (doc.y || 0) + dY * (actualdH - height);
const fixedAspect = e.ctrlKey || (nwidth && nheight);
if (fixedAspect && (!nwidth || !nheight)) {
- layoutDoc._nativeWidth = nwidth = layoutDoc._width || 0;
- layoutDoc._nativeHeight = nheight = layoutDoc._height || 0;
+ Doc.SetNativeWidth(layoutDoc, nwidth = layoutDoc._width || 0);
+ Doc.SetNativeHeight(layoutDoc, nheight = layoutDoc._height || 0);
}
if (nwidth > 0 && nheight > 0) {
if (Math.abs(dW) > Math.abs(dH)) {
if (!fixedAspect) {
- layoutDoc._nativeWidth = actualdW / (layoutDoc._width || 1) * (layoutDoc._nativeWidth || 0);
+ Doc.SetNativeWidth(layoutDoc, actualdW / (layoutDoc._width || 1) * Doc.NativeWidth(layoutDoc));
}
layoutDoc._width = actualdW;
if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width;
@@ -519,7 +516,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
}
else {
if (!fixedAspect) {
- layoutDoc._nativeHeight = actualdH / (layoutDoc._height || 1) * (doc._nativeHeight || 0);
+ Doc.SetNativeHeight(layoutDoc, actualdH / (layoutDoc._height || 1) * Doc.NativeHeight(doc));
}
layoutDoc._height = actualdH;
if (fixedAspect && !layoutDoc._fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height;
@@ -554,7 +551,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if ((this.active || this.layoutDoc.onDragStart) &&
!e.ctrlKey &&
(e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
- !this.layoutDoc.inOverlay) {
+ !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
e.stopPropagation();
if (SelectionManager.IsSelected(this, true) && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
}
@@ -573,7 +570,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (e.cancelBubble && this.active) {
document.removeEventListener("pointermove", this.onPointerMove); // stop listening to pointerMove if something else has stopPropagated it (e.g., the MarqueeView)
}
- else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart) && !this.layoutDoc.lockedPosition && !this.layoutDoc.inOverlay) {
+ else if (!e.cancelBubble && (SelectionManager.IsSelected(this, true) || this.props.parentActive(true) || this.layoutDoc.onDragStart) && !this.layoutDoc.lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
document.removeEventListener("pointermove", this.onPointerMove);
@@ -754,8 +751,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
this.props.bringToFront(this.props.Document, true);
const wid = this.Document[WidthSym](); // change the nativewidth and height if the background is to be a collection that aggregates stuff that is added to it.
const hgt = this.Document[HeightSym]();
- this.props.Document[DataSym][this.LayoutFieldKey + "-nativeWidth"] = wid;
- this.props.Document[DataSym][this.LayoutFieldKey + "-nativeHeight"] = hgt;
+ Doc.SetNativeWidth(this.props.Document[DataSym], wid);
+ Doc.SetNativeHeight(this.props.Document[DataSym], hgt);
}
}
@@ -839,8 +836,8 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" });
onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" });
onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" });
+ !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
}
- !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
}
const funcs: ContextMenuProps[] = [];
@@ -889,7 +886,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
e?.stopPropagation(); // DocumentViews should stop propagation of this event
}
cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
- !this.isSelected(true) && SelectionManager.SelectDoc(this, false);
+ !this.isSelected(true) && setTimeout(() => SelectionManager.SelectDoc(this, false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear.
});
}
@@ -907,7 +904,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
const excluded = ["PresBox", /* "FormattedTextBox", */ "FontIconBox"]; // bcz: shifting the title for texst causes problems with collaborative use when some people see titles, and others don't
return !excluded.includes(StrCast(this.layoutDoc.layout));
}
- chromeHeight = () => this.showOverlappingTitle ? 1 : 25;
+ chromeHeight = () => this.showOverlappingTitle ? 0 : 25;
@computed get finalLayoutKey() {
if (typeof this.props.layoutKey === "string") {
@@ -920,7 +917,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
return this.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
}
childScaling = () => (this.layoutDoc._fitWidth ? this.props.PanelWidth() / this.nativeWidth : this.props.ContentScaling());
- @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, undefined]; }
+ @computed.struct get linkOffset() { return this.topMost ? [0, undefined, undefined, 10] : [-15, undefined, undefined, -20]; }
@computed get contents() {
TraceMobx();
return (<div className="documentView-contentsView" style={{ pointerEvents: this.props.contentsPointerEvents as any, borderRadius: "inherit", width: "100%", height: "100%" }}>
@@ -990,7 +987,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
anchorPanelHeight = () => this.props.PanelHeight() || 1;
@computed get directLinks() { TraceMobx(); return LinkManager.Instance.getAllDirectLinks(this.rootDoc); }
- @computed get allLinks() { TraceMobx(); return DocListCast(this.Document.links); }
+ @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); }
@computed get allAnchors() {
TraceMobx();
if (this.props.LayoutTemplateString?.includes("LinkAnchorBox")) return null;
@@ -1144,19 +1141,20 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
style={{
transformOrigin: this._animateScalingTo ? "center center" : undefined,
transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
- transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : this._animateScalingTo < 1 ? "transform 0.5s ease-in" : "transform 0.5s ease-out",
+ transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
pointerEvents: this.ignorePointerEvents ? "none" : undefined,
color: StrCast(this.layoutDoc.color, "inherit"),
outline: highlighting && !borderRounding ? `${highlightColors[fullDegree]} ${highlightStyles[fullDegree]} ${localScale}px` : "solid 0px",
- border: highlighting && borderRounding ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
- boxShadow: this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
- StrCast(this.props.Document._linkButtonShadow, "lightblue 0em 0em 1em") :
- this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
- undefined,
+ border: highlighting && borderRounding && highlightStyles[fullDegree] === "dashed" ? `${highlightStyles[fullDegree]} ${highlightColors[fullDegree]} ${localScale}px` : undefined,
+ boxShadow: highlighting && borderRounding && highlightStyles[fullDegree] !== "dashed" ? `0 0 0 ${localScale}px ${highlightColors[fullDegree]}` :
+ this.Document.isLinkButton && !this.props.dontRegisterView && this.props.forcedBackgroundColor?.(this.Document) !== "transparent" ?
+ StrCast(this.layoutDoc._linkButtonShadow, "lightblue 0em 0em 1em") :
+ this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" :
+ undefined,
background: finalColor,
opacity: finalOpacity,
fontFamily: StrCast(this.Document._fontFamily, "inherit"),
- fontSize: Cast(this.Document._fontSize, "string", null),
+ fontSize: !this.props.treeViewDoc ? Cast(this.Document._fontSize, "string", null) : undefined,
}}>
{this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <>
{this.innards}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 3a5b27b21..79947c023 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -11,6 +11,7 @@ import { CollectionView } from "../collections/CollectionView";
import { AudioBox } from "./AudioBox";
import { VideoBox } from "./VideoBox";
import { dropActionType } from "../../util/DragManager";
+import { DocAfterFocusFunc, DocFocusFunc } from "./DocumentView";
//
// these properties get assigned through the render() method of the DocumentView when it creates this node.
@@ -47,7 +48,7 @@ export interface FieldViewProps {
whenActiveChanged: (isActive: boolean) => void;
LayoutTemplateString?: string;
dontRegisterView?: boolean;
- focus: (doc: Doc) => void;
+ focus: DocFocusFunc;
presMultiSelect?: (doc: Doc) => void; //added for selecting multiple documents in a presentation
ignoreAutoHeight?: boolean;
PanelWidth: () => number;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index bc38a17eb..88dc3b241 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -76,14 +76,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
componentDidMount() {
this._pathDisposer = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
- ({ nativeSize, width }) => {
- if (this.props.NativeWidth?.() !== 0 || !this.layoutDoc._height) {
- this.layoutDoc._nativeWidth = nativeSize.nativeWidth;
- this.layoutDoc._nativeHeight = nativeSize.nativeHeight;
- this.layoutDoc._nativeOrientation = nativeSize.nativeOrientation;
+ action(({ nativeSize, width }) => {
+ if (!this.layoutDoc._height) {
this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
}
- },
+ }),
{ fireImmediately: true });
}
@@ -106,8 +103,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps, ImageD
const targetDoc = layoutDoc[DataSym];
if (targetDoc[targetField] instanceof ImageField) {
this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
- this.dataDoc[this.fieldKey + "-nativeWidth"] = NumCast(targetDoc[targetField + "-nativeWidth"]);
- this.dataDoc[this.fieldKey + "-nativeHeight"] = NumCast(targetDoc[targetField + "-nativeHeight"]);
+ Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc));
+ Doc.SetNativeWidth(this.dataDoc, Doc.NativeHeight(targetDoc));
e.stopPropagation();
}
}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 867be9735..d47942bd9 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -77,19 +77,16 @@ export class LinkDocPreview extends React.Component<Props> {
this.props.addDocTab(Docs.Create.WebDocument(this.props.href, { _fitWidth: true, title: this.props.href, _width: 200, _height: 400, useCors: true }), "add:right");
}
}
- width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)) - 16;
- height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225)) - 16;
+ width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
+ height = () => Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
@computed get targetDocView() {
return !this._targetDoc ?
- <div style={{
- pointerEvents: "all", maxWidth: 225, maxHeight: 225, width: "100%", height: "100%",
- overflow: "hidden"
- }}>
+ <div style={{ pointerEvents: "all", maxWidth: 225, maxHeight: 225, width: "100%", height: "100%", overflow: "hidden" }}>
<div style={{ width: "100%", height: "100%", textOverflow: "ellipsis", }} onPointerDown={this.pointerDown}>
{this._toolTipText}
</div>
- </div> :
-
+ </div>
+ :
<ContentFittingDocumentView
Document={this._targetDoc}
LibraryPath={emptyPath}
@@ -109,8 +106,8 @@ export class LinkDocPreview extends React.Component<Props> {
ContainingCollectionDoc={undefined}
ContainingCollectionView={undefined}
renderDepth={-1}
- PanelWidth={this.width} //Math.min(350, NumCast(target._width, 350))}
- PanelHeight={this.height} //Math.min(250, NumCast(target._height, 250))}
+ PanelWidth={this.width}
+ PanelHeight={this.height}
focus={emptyFunction}
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index e2bdd98cc..16cc9d27e 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -208,6 +208,7 @@
pointer-events: none;
.pdfViewerDash-text {
.textLayer {
+ display: none;
span {
user-select: none;
}
@@ -228,6 +229,7 @@
z-index: -1;
.pdfViewerDash-text {
.textLayer {
+ display: contents;
span {
user-select: text;
}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 79e00eed7..42b24f6f6 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -50,8 +50,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
constructor(props: any) {
super(props);
this._initialScale = this.props.ScreenToLocalTransform().Scale;
- const nw = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 927));
- const nh = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, 1200));
+ const nw = Doc.NativeWidth(this.Document, this.dataDoc) || 927;
+ const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
!this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
if (pdfUrl) {
@@ -97,8 +97,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
loaded = (nw: number, nh: number, np: number) => {
this.dataDoc[this.props.fieldKey + "-numPages"] = np;
- this.dataDoc[this.props.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = Math.max(NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"]), nw * 96 / 72);
- this.dataDoc[this.props.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = nh * 96 / 72;
+ Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), nw * 96 / 72));
+ Doc.SetNativeHeight(this.dataDoc, nh * 96 / 72);
+ this.layoutDoc._height = this.layoutDoc[WidthSym]() / (Doc.NativeAspect(this.dataDoc) || 1);
!this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
}
@@ -254,7 +255,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps, PdfDocum
const pdfUrl = Cast(this.dataDoc[this.props.fieldKey], PdfField);
return <div className={"pdfBox"} onContextMenu={this.specificContextMenu} style={{ height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ? NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined }}>
<div className="pdfBox-background"></div>
- <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={this.loaded}
+ <PDFViewer {...this.props} pdf={this._pdf!} url={pdfUrl!.url.pathname} active={this.props.active} loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
setPdfViewer={this.setPdfViewer} ContainingCollectionView={this.props.ContainingCollectionView}
renderDepth={this.props.renderDepth} PanelHeight={this.props.PanelHeight} PanelWidth={this.props.PanelWidth}
addDocTab={this.props.addDocTab} focus={this.props.focus} searchFilterDocs={this.props.searchFilterDocs}
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index 9a8b861ba..de2aee8fa 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -56,11 +56,14 @@ $light-background: #ececec;
letter-spacing: 0;
display: flex;
align-items: center;
+ justify-content: center;
transition: 0.5s;
}
.toolbar-button.active {
color: $light-blue;
+ background-color: white;
+ border-radius: 100%;
}
.toolbar-transitionButtons {
@@ -128,7 +131,6 @@ $light-background: #ececec;
position: relative;
display: inline;
font-family: Roboto;
- color: black;
z-index: 100;
transition: 0.7s;
@@ -509,6 +511,7 @@ $light-background: #ececec;
background-color: $light-background;
border: solid 1px rgba(0, 0, 0, 0.5);
display: flex;
+ color: black;
margin-top: 5px;
margin-bottom: 5px;
border-radius: 5px;
@@ -569,6 +572,7 @@ $light-background: #ececec;
border-radius: 5px;
font-size: 10;
height: 25;
+ color: black;
padding-left: 5px;
align-items: center;
margin-top: 5px;
@@ -997,6 +1001,7 @@ $light-background: #ececec;
transition: all 0.2s;
.presPanel-button-text {
+ cursor: pointer;
display: flex;
height: 20;
width: max-content;
@@ -1049,6 +1054,7 @@ $light-background: #ececec;
.presPanel-button:hover {
background-color: #5a5a5a;
+ transform: scale(1.2);
}
.presPanel-button-text:hover {
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 8e3679452..683cb938a 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -1,7 +1,7 @@
import React = require("react");
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
-import { action, computed, observable, runInAction } from "mobx";
+import { action, computed, observable, runInAction, ObservableMap, IReactionDisposer, reaction } from "mobx";
import { observer } from "mobx-react";
import { ColorState, SketchPicker } from "react-color";
import { Doc, DocCastAsync, DocListCast, DocListCastAsync } from "../../../fields/Doc";
@@ -11,7 +11,7 @@ import { List } from "../../../fields/List";
import { PrefetchProxy } from "../../../fields/Proxy";
import { listSpec, makeInterface } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
import { returnFalse, returnOne, returnZero } from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
@@ -19,7 +19,7 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { DocumentManager } from "../../util/DocumentManager";
import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
-import { undoBatch } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../util/UndoManager";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionView, CollectionViewType } from "../collections/CollectionView";
import { TabDocView } from "../collections/TabDocView";
@@ -28,7 +28,7 @@ import { AudioBox } from "./AudioBox";
import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
import { FieldView, FieldViewProps } from './FieldView';
import "./PresBox.scss";
-import { VideoBox } from "./VideoBox";
+import Color = require("color");
export enum PresMovement {
Zoom = "zoom",
@@ -38,12 +38,30 @@ export enum PresMovement {
}
export enum PresEffect {
- Fade = "fade",
- Flip = "flip",
- Rotate = "rotate",
- Bounce = "bounce",
- Roll = "roll",
- None = "none",
+ Fade = "Fade in",
+ Flip = "Flip",
+ Rotate = "Rotate",
+ Bounce = "Bounce",
+ Roll = "Roll",
+ None = "None",
+ Left = "left",
+ Right = "right",
+ Center = "center",
+ Top = "top",
+ Bottom = "bottom"
+}
+
+enum PresStatus {
+ Autoplay = "auto",
+ Manual = "manual",
+ Edit = "edit"
+}
+
+enum PresColor {
+ LightBlue = "#AEDDF8",
+ DarkBlue = "#5B9FDD",
+ LightBackground = "#ececec",
+ SlideBackground = "#d5dce2",
}
type PresBoxSchema = makeInterface<[typeof documentSchema]>;
@@ -53,35 +71,53 @@ const PresBoxDocument = makeInterface(documentSchema);
export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
- public static Instance: PresBox;
+ @observable public static Instance: PresBox;
@observable _isChildActive = false;
@observable _moveOnFromAudio: boolean = true;
@observable _presTimer!: NodeJS.Timeout;
+ @observable _presKeyEventsActive: boolean = false;
- @observable _selectedArray: Doc[] = [];
- @observable _sortedSelectedArray: Doc[] = [];
+ @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
@observable _eleArray: HTMLElement[] = [];
@observable _dragArray: HTMLElement[] = [];
+ @observable _pathBoolean: boolean = false;
+ @observable _expandBoolean: boolean = false;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
@observable private transitionTools: boolean = false;
@observable private newDocumentTools: boolean = false;
@observable private progressivizeTools: boolean = false;
- @observable private presentTools: boolean = false;
- @observable private pathBoolean: boolean = false;
@observable private openMovementDropdown: boolean = false;
@observable private openEffectDropdown: boolean = false;
+ @observable private presentTools: boolean = false;
@computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
+ @computed get tagDocs() {
+ const tagDocs: Doc[] = [];
+ for (const doc of this.childDocs) {
+ const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+ tagDocs.push(tagDoc);
+ }
+ return tagDocs;
+ }
@computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); }
- @computed get activeItem() { return Cast(this.childDocs[this.itemIndex], Doc, null); }
+ @computed get activeItem() { return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null); }
@computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+ @computed get scrollable(): boolean {
+ if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
+ else return false;
+ }
+ @computed get panable(): boolean {
+ if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
+ else return false;
+ }
@computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
constructor(props: any) {
super(props);
- if (Doc.UserDoc().activePresentation = this.rootDoc) PresBox.Instance = this;
+ if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
- title: "pres element template", backgroundColor: "transparent", _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
+ title: "pres element template", type: DocumentType.PRESELEMENT, backgroundColor: "transparent", _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
}));
// this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement
// this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent
@@ -94,139 +130,145 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get selectedDocumentView() {
if (SelectionManager.SelectedDocuments().length) return SelectionManager.SelectedDocuments()[0];
- if (PresBox.Instance && PresBox.Instance._selectedArray) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
- return undefined;
+ if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
}
@computed get isPres(): boolean {
- document.removeEventListener("keydown", this.keyEvents, true);
+ document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
if (this.selectedDoc?.type === DocumentType.PRES) {
- document.addEventListener("keydown", this.keyEvents, true);
+ document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+ document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
return true;
}
return false;
}
@computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+ @action
componentWillUnmount() {
- document.removeEventListener("keydown", this.keyEvents, true);
+ document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+ this._presKeyEventsActive = false;
+ this.resetPresentation();
// Turn of progressivize editors
this.turnOffEdit(true);
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
- componentDidMount = async () => {
+ @action
+ componentDidMount() {
this.rootDoc.presBox = this.rootDoc;
this.rootDoc._forceRenderEngine = "timeline";
this.rootDoc._replacedChrome = "replaced";
- this.layoutDoc.presStatus = "edit";
+ this.layoutDoc.presStatus = PresStatus.Edit;
this.layoutDoc._gridGap = 0;
this.layoutDoc._yMargin = 0;
this.turnOffEdit(true);
DocListCastAsync((Doc.UserDoc().myPresentations as Doc).data).then(pres =>
!pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myPresentations as Doc, "data", this.rootDoc));
+ this._disposers.selection = reaction(() => SelectionManager.SelectedDocuments(),
+ views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
}
- updateCurrentPresentation = () => {
- Doc.UserDoc().activePresentation = this.rootDoc;
+ @action
+ updateCurrentPresentation = (pres?: Doc) => {
+ if (pres) Doc.UserDoc().activePresentation = pres;
+ else Doc.UserDoc().activePresentation = this.rootDoc;
+ document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+ document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
+ this._presKeyEventsActive = true;
PresBox.Instance = this;
}
- /**
- * Called when the user moves to the next slide in the presentation trail.
- */
- @undoBatch
+ // There are still other internal frames and should go through all frames before going to next slide
+ nextInternalFrame = (targetDoc: Doc, activeItem: Doc) => {
+ const currentFrame = Cast(targetDoc?._currentFrame, "number", null);
+ const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
+ targetDoc._viewTransition = "all 1s";
+ setTimeout(() => targetDoc._viewTransition = undefined, 1010);
+ this.nextKeyframe(targetDoc, activeItem);
+ if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
+ else targetDoc.editing = true;
+ }
+
+ // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
+ nextAudioVideo = (targetDoc: Doc, activeItem: Doc) => {
+ if (targetDoc.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeItem.presStartTime));
+ // if (targetDoc.type === DocumentType.VID) { VideoBox.Instance.Play() };
+ activeItem.playNow = false;
+ }
+
+ // No more frames in current doc and next slide is defined, therefore move to next slide
+ nextSlide = (targetDoc: Doc, activeNext: Doc) => {
+ const nextSelected = this.itemIndex + 1;
+ this.gotoDocument(nextSelected);
+
+ // const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
+ // If next slide is audio / video 'Play automatically' then the next slide should be played
+ // if (activeNext && (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) && activeNext.playAuto) {
+ // console.log('play next automatically');
+ // if (targetNext.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeNext.presStartTime));
+ // // if (targetNext.type === DocumentType.VID) { VideoBox.Instance.Play() };
+ // } else if (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) { activeNext.playNow = true; console.log('play next after it is navigated to'); }
+ }
+
+ // Called when the user activates 'next' - to move to the next part of the pres. trail
@action
next = () => {
- this.updateCurrentPresentation();
const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null);
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
- const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
- const currentFrame = Cast(targetDoc._currentFrame, "number", null);
- const lastFrame = Cast(targetDoc.lastFrame, "number", null);
- const curFrame = NumCast(targetDoc._currentFrame);
+ const lastFrame = Cast(targetDoc?.lastFrame, "number", null);
+ const curFrame = NumCast(targetDoc?._currentFrame);
let internalFrames: boolean = false;
if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
- // Case 1: There are still other frames and should go through all frames before going to next slide
if (internalFrames && lastFrame !== undefined && curFrame < lastFrame) {
- targetDoc._viewTransition = "all 1s";
- setTimeout(() => targetDoc._viewTransition = undefined, 1010);
- // targetDoc._currentFrame = curFrame + 1;
- this.nextKeyframe(targetDoc, activeItem);
- if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
- else targetDoc.editing = true;
- // if (activeItem.zoomProgressivize) this.zoomProgressivizeNext(targetDoc);
- // Case 2: 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
- } else if ((targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && !activeItem.playAuto && activeItem.playNow && this.layoutDoc.presStatus !== 'auto') {
- if (targetDoc.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeItem.presStartTime));
- // if (targetDoc.type === DocumentType.VID) { VideoBox.Instance.Play() };
- activeItem.playNow = false;
- // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
+ // Case 1: There are still other frames and should go through all frames before going to next slide
+ this.nextInternalFrame(targetDoc, activeItem);
} else if (this.childDocs[this.itemIndex + 1] !== undefined) {
- if (activeNext.presPinView) setTimeout(() => this.selectPres(), 0);
- else this.selectPres();
- const nextSelected = this.itemIndex + 1;
- if (targetDoc.type === DocumentType.AUDIO) { if (AudioBox.Instance._ele) AudioBox.Instance.pause(); }
- // if (targetDoc.type === DocumentType.VID) { if (AudioBox.Instance._ele) VideoBox.Instance.Pause(); }
- const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
- // If next slide is audio / video 'Play automatically' then the next slide should be played
- if (activeNext && (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) && activeNext.playAuto) {
- console.log('play next automatically');
- if (targetNext.type === DocumentType.AUDIO) AudioBox.Instance.playFrom(NumCast(activeNext.presStartTime));
- // if (targetNext.type === DocumentType.VID) { VideoBox.Instance.Play() };
- } else if (targetNext.type === DocumentType.AUDIO || targetNext.type === DocumentType.VID) { activeNext.playNow = true; console.log('play next after it is navigated to'); }
- this.gotoDocument(nextSelected, this.itemIndex);
+ // Case 3: No more frames in current doc and next slide is defined, therefore move to next slide
+ this.nextSlide(targetDoc, activeNext);
} else if (this.childDocs[this.itemIndex + 1] === undefined && this.layoutDoc.presLoop) {
- this.gotoDocument(0, this.itemIndex);
+ // Case 4: Last slide and presLoop is toggled ON
+ this.gotoDocument(0);
}
+ // else if ((targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && !activeItem.playAuto && activeItem.playNow && this.layoutDoc.presStatus !== PresStatus.Autoplay) {
+ // // Case 2: 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
+ // this.nextAudioVideo(targetDoc, activeItem);
+ // }
}
- /**
- * Called when the user moves back
- * Design choice: If there are frames within the presentation, moving back will not
- * got back through the frames but instead directly to the next point in the presentation.
- */
- @undoBatch
+ // Called when the user activates 'back' - to move to the previous part of the pres. trail
@action
back = () => {
- this.updateCurrentPresentation();
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
const lastFrame = Cast(targetDoc.lastFrame, "number", null);
const curFrame = NumCast(targetDoc._currentFrame);
- if (prevItem.presPinView) setTimeout(() => this.selectPres(), 0);
- else this.selectPres();
+ let prevSelected = this.itemIndex;
if (lastFrame !== undefined && curFrame >= 1) {
+ // Case 1: There are still other frames and should go through all frames before going to previous slide
this.prevKeyframe(targetDoc, activeItem);
- } else if (activeItem) {
- let prevSelected = this.itemIndex;
+ } else if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) {
+ // Case 2: There are no other frames so it should go to the previous slide
prevSelected = Math.max(0, prevSelected - 1);
- this.gotoDocument(prevSelected, this.itemIndex);
+ this.gotoDocument(prevSelected);
if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame);
+ } else if (this.childDocs[this.itemIndex - 1] === undefined && this.layoutDoc.presLoop) {
+ // Case 3: Pres loop is on so it should go to the last slide
+ this.gotoDocument(this.childDocs.length - 1);
}
}
//The function that is called when a document is clicked or reached through next or back.
//it'll also execute the necessary actions if presentation is playing.
- public gotoDocument = action((index: number, fromDoc: number) => {
- this.updateCurrentPresentation();
+ public gotoDocument = action((index: number) => {
Doc.UnBrushAllDocs();
if (index >= 0 && index < this.childDocs.length) {
this.rootDoc._itemIndex = index;
const activeItem: Doc = this.activeItem;
const presTargetDoc = Cast(this.childDocs[this.itemIndex].presentationTargetDoc, Doc, null);
- if (activeItem.presPinView) {
- const bestTarget = DocumentManager.Instance.getFirstDocumentView(presTargetDoc)?.props.Document;
- bestTarget && runInAction(() => {
- if (activeItem.presMovement === PresMovement.Jump) {
- bestTarget._viewTransition = '0s';
- } else {
- bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 1s';
- setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 1010);
- }
- });
- } else {
+ if (presTargetDoc) {
presTargetDoc && runInAction(() => {
if (activeItem.presMovement === PresMovement.Jump) presTargetDoc.focusSpeed = 0;
else presTargetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
@@ -236,13 +278,35 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (presTargetDoc?.lastFrame !== undefined) {
presTargetDoc._currentFrame = 0;
}
- this._selectedArray = [this.childDocs[index]]; //Update selected array
- //Handles movement to element
- if (this.layoutDoc._viewType === "stacking") this.navigateToElement(this.childDocs[index]);
+ this._selectedArray.clear();
+ this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array
+ if (this.layoutDoc._viewType === "stacking") this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
this.onHideDocument(); //Handles hide after/before
}
});
+ _navTimer!: NodeJS.Timeout;
+ navigateToView = (targetDoc: Doc, activeItem: Doc) => {
+ clearTimeout(this._navTimer);
+ const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
+ bestTarget && runInAction(() => {
+ if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
+ bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+ bestTarget._scrollY = activeItem.presPinViewScroll;
+ } else if (bestTarget.type === DocumentType.COMPARISON) {
+ bestTarget._clipWidth = activeItem.presPinClipWidth;
+ } else if (bestTarget.type === DocumentType.VID) {
+ bestTarget._currentTimecode = activeItem.presPinTimecode;
+ } else {
+ bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+ bestTarget._panX = activeItem.presPinViewX;
+ bestTarget._panY = activeItem.presPinViewY;
+ bestTarget._viewScale = activeItem.presPinViewScale;
+ }
+ this._navTimer = setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
+ });
+ }
+
/**
* This method makes sure that cursor navigates to the element that
* has the option open and last in the group.
@@ -254,68 +318,55 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
navigateToElement = async (curDoc: Doc) => {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
- const srcContext = await DocCastAsync(targetDoc.context);
+ const srcContext = Cast(targetDoc.context, Doc, null);
const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
- const collectionDocView = presCollection ? await DocumentManager.Instance.getDocumentView(presCollection) : undefined;
+ const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined;
+ const includesDoc: boolean = DocListCast(presCollection?.data).includes(targetDoc);
+ const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext);
this.turnOffEdit();
- if (this.itemIndex >= 0) {
- if (srcContext && targetDoc) {
- this.layoutDoc.presCollection = srcContext;
- } else if (targetDoc) this.layoutDoc.presCollection = targetDoc;
+ // Handles the setting of presCollection
+ if (includesDoc) {
+ //Case 1: Pres collection should not change as it is already the same
+ } else if (tab !== undefined) {
+ // Case 2: Pres collection should update
+ this.layoutDoc.presCollection = srcContext;
}
- // if (collectionDocView) {
- // if (srcContext && srcContext !== presCollection) {
- // // Case 1: new srcContext inside of current collection so add a new tab to the current pres collection
- // collectionDocView.props.addDocTab(srcContext, "inPlace");
- // }
- // }
- this.updateCurrentPresentation();
- const docToJump = curDoc;
- const willZoom = false;
-
+ const presStatus = this.rootDoc.presStatus;
+ const selViewCache = Array.from(this._selectedArray.keys());
+ const dragViewCache = Array.from(this._dragArray);
+ const eleViewCache = Array.from(this._eleArray);
+ const self = this;
+ const resetSelection = action(() => {
+ const presDocView = DocumentManager.Instance.getDocumentView(self.rootDoc);
+ if (presDocView) SelectionManager.SelectDoc(presDocView, false);
+ self.rootDoc.presStatus = presStatus;
+ self._selectedArray.clear();
+ selViewCache.forEach(doc => self._selectedArray.set(doc, undefined));
+ self._dragArray.splice(0, self._dragArray.length, ...dragViewCache);
+ self._eleArray.splice(0, self._eleArray.length, ...eleViewCache);
+ });
+ const openInTab = () => {
+ collectionDocView ? collectionDocView.props.addDocTab(targetDoc, "") : this.props.addDocTab(targetDoc, ":left");
+ this.layoutDoc.presCollection = targetDoc;
+ // this still needs some fixing
+ setTimeout(resetSelection, 500);
+ };
// If openDocument is selected then it should open the document for the user
if (activeItem.openDocument) {
- collectionDocView ? collectionDocView.props.addDocTab(activeItem, "replace") : this.props.addDocTab(activeItem, "replace:left");
- } else
- //docToJump stayed same meaning, it was not in the group or was the last element in the group
- if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') {
- this.zoomProgressivizeNext(targetDoc);
- } else if (docToJump === curDoc) {
- //checking if curDoc has navigation open
- if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
- await DocumentManager.Instance.jumpToDocument(targetDoc, false, undefined, srcContext);
- } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
- //awaiting jump so that new scale can be found, since jumping is async
- await DocumentManager.Instance.jumpToDocument(targetDoc, true, undefined, srcContext);
- }
- } else {
- //awaiting jump so that new scale can be found, since jumping is async
- targetDoc && await DocumentManager.Instance.jumpToDocument(targetDoc, willZoom, undefined, srcContext);
- }
+ openInTab();
+ } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
+ await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection); // documents open in new tab instead of on right
+ } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
+ //awaiting jump so that new scale can be found, since jumping is async
+ await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection); // documents open in new tab instead of on right
+ }
// After navigating to the document, if it is added as a presPinView then it will
// adjust the pan and scale to that of the pinView when it was added.
- // TODO: Add option to remove presPinView
if (activeItem.presPinView) {
// if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
- const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
- bestTarget && runInAction(() => {
- if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
- bestTarget._scrollY = activeItem.presPinViewScroll;
- } else {
- bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
- bestTarget._panX = activeItem.presPinViewX;
- bestTarget._panY = activeItem.presPinViewY;
- bestTarget._viewScale = activeItem.presPinViewScale;
- }
- });
- //setTimeout(() => targetDoc._viewTransition = undefined, 1010);
+ this.navigateToView(targetDoc, activeItem);
}
- // If website and has presWebsite data associated then on click it should
- // go back to that specific website
// TODO: Add progressivize for navigating web (storing websites for given frames)
- if (targetDoc.presWebsiteData) {
- targetDoc.data = targetDoc.presWebsiteData;
- }
}
/**
@@ -363,7 +414,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
}
-
/**
* For 'Hide Before' and 'Hide After' buttons making sure that
* they are hidden each time the presentation is updated.
@@ -372,20 +422,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
onHideDocument = () => {
this.childDocs.forEach((doc, index) => {
const curDoc = Cast(doc, Doc, null);
- const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null);
+ const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
if (tagDoc) tagDoc.opacity = 1;
- if (curDoc.presHideTillShownButton) {
- if (index > this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (!curDoc.presHideAfterButton) {
- tagDoc.opacity = 1;
+ const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
+ const curInd: number = itemIndexes.indexOf(index);
+ if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1; }
+ else {
+ if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) { }
+ else if (curDoc.presHideBefore) {
+ if (index > this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (!curDoc.presHideAfter) {
+ tagDoc.opacity = 1;
+ }
}
- }
- if (curDoc.presHideAfterButton) {
- if (index < this.itemIndex) {
- tagDoc.opacity = 0;
- } else if (!curDoc.presHideTillShownButton) {
- tagDoc.opacity = 1;
+ if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== (itemIndexes.length - 1)) { }
+ else if (curDoc.presHideAfter) {
+ if (index < this.itemIndex) {
+ tagDoc.opacity = 0;
+ } else if (!curDoc.presHideBefore) {
+ tagDoc.opacity = 1;
+ }
}
}
});
@@ -394,7 +451,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc
- @undoBatch
@action
startAutoPres = (startSlide: number) => {
this.updateCurrentPresentation();
@@ -418,23 +474,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (i === this.childDocs.length - 1) {
setTimeout(() => {
clearTimeout(this._presTimer);
- if (this.layoutDoc.presStatus === 'auto' && !this.layoutDoc.presLoop) this.layoutDoc.presStatus = "manual";
+ if (this.layoutDoc.presStatus === 'auto' && !this.layoutDoc.presLoop) this.layoutDoc.presStatus = PresStatus.Manual;
else if (this.layoutDoc.presLoop) this.startAutoPres(0);
}, duration);
}
}
};
- this.layoutDoc.presStatus = "auto";
+ this.layoutDoc.presStatus = PresStatus.Autoplay;
this.startPresentation(startSlide);
- this.gotoDocument(startSlide, this.itemIndex);
+ this.gotoDocument(startSlide);
load();
}
@action
pauseAutoPres = () => {
- if (this.layoutDoc.presStatus === "auto") {
+ if (this.layoutDoc.presStatus === PresStatus.Autoplay) {
if (this._presTimer) clearTimeout(this._presTimer);
- this.layoutDoc.presStatus = "manual";
+ this.layoutDoc.presStatus = PresStatus.Manual;
this.layoutDoc.presLoop = false;
}
}
@@ -442,26 +498,32 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
//The function that resets the presentation by removing every action done by it. It also
//stops the presentaton.
resetPresentation = () => {
- this.updateCurrentPresentation();
this.rootDoc._itemIndex = 0;
+ this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null)).filter(doc => doc instanceof Doc).forEach(doc => {
+ try {
+ doc.opacity = 1;
+ } catch (e) {
+ console.log("REset presentation error: ", e);
+ }
+ });
+ ///for (const doc of this.childDocs) Cast(doc.presentationTargetDoc, Doc, null).opacity = 1;
}
@action togglePath = (srcContext: Doc, off?: boolean) => {
if (off) {
- this.pathBoolean = false;
+ this._pathBoolean = false;
srcContext.presPathView = false;
} else {
- this.pathBoolean = !this.pathBoolean;
- srcContext.presPathView = this.pathBoolean;
+ runInAction(() => this._pathBoolean = !this._pathBoolean);
+ srcContext.presPathView = this._pathBoolean;
}
}
- @undoBatch
@action toggleExpandMode = () => {
- this.rootDoc.expandBoolean = !this.rootDoc.expandBoolean;
+ runInAction(() => this._expandBoolean = !this._expandBoolean);
+ this.rootDoc.expandBoolean = this._expandBoolean;
this.childDocs.forEach((doc) => {
- if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true;
- else if (!this.rootDoc.expandBoolean) doc.presExpandInlineButton = false;
+ doc.presExpandInlineButton = this._expandBoolean;
});
}
@@ -474,10 +536,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.updateCurrentPresentation();
this.childDocs.map(doc => {
const tagDoc = doc.presentationTargetDoc as Doc;
- if (doc.presHideTillShownButton && this.childDocs.indexOf(doc) > startIndex) {
+ if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) {
tagDoc.opacity = 0;
}
- if (doc.presHideAfterButton && this.childDocs.indexOf(doc) < startIndex) {
+ if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) {
tagDoc.opacity = 0;
}
});
@@ -486,17 +548,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
/**
* The method called to open the presentation as a minimized view
*/
- @undoBatch
@action
updateMinimize = () => {
const docView = DocumentManager.Instance.getDocumentView(this.layoutDoc);
- if (this.layoutDoc.inOverlay) {
- this.layoutDoc.presStatus = 'edit';
+ if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+ this.layoutDoc.presStatus = PresStatus.Edit;
Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
CollectionDockingView.AddSplit(this.rootDoc, "right");
- this.layoutDoc.inOverlay = false;
} else if (this.layoutDoc.context && docView) {
- this.layoutDoc.presStatus = 'edit';
+ this.layoutDoc.presStatus = PresStatus.Edit;
clearTimeout(this._presTimer);
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
@@ -506,7 +566,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
docView.props.removeDocument?.(this.layoutDoc);
Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
} else {
- this.layoutDoc.presStatus = 'edit';
+ this.layoutDoc.presStatus = PresStatus.Edit;
clearTimeout(this._presTimer);
const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
this.rootDoc.x = pt[0] + (this.props.PanelWidth() - 250);
@@ -522,7 +582,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
* Called when the user changes the view type
* Either 'List' (stacking) or 'Slides' (carousel)
*/
- @undoBatch
+ // @undoBatch
viewChanged = action((e: React.ChangeEvent) => {
//@ts-ignore
const viewType = e.target.selectedOptions[0].value as CollectionViewType;
@@ -532,32 +592,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (viewType === CollectionViewType.Stacking) this.layoutDoc._gridGap = 0;
});
- /**
- * When the movement dropdown is changes
- */
- @undoBatch
- updateMovement = action((movement: any, activeItem: Doc, targetDoc: Doc) => {
- switch (movement) {
- case PresMovement.Zoom: //Pan and zoom
- activeItem.presMovement = PresMovement.Zoom;
- break;
- case PresMovement.Pan: //Pan
- activeItem.presMovement = PresMovement.Pan;
- break;
- case PresMovement.Jump: //Jump Cut
- activeItem.presJump = true;
- activeItem.presMovement = PresMovement.Jump;
- break;
- case PresMovement.None: default:
- activeItem.presMovement = PresMovement.None;
- break;
- }
- });
setMovementName = action((movement: any, activeItem: Doc): string => {
let output: string = 'none';
switch (movement) {
- case PresMovement.Zoom: output = 'Zoom'; break; //Pan and zoom
+ case PresMovement.Zoom: output = 'Pan & Zoom'; break; //Pan and zoom
case PresMovement.Pan: output = 'Pan'; break; //Pan
case PresMovement.Jump: output = 'Jump cut'; break; //Jump Cut
case PresMovement.None: output = 'None'; break; //None
@@ -571,29 +610,34 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
addDocumentFilter = (doc: Doc | Doc[]) => {
const docs = doc instanceof Doc ? [doc] : doc;
docs.forEach((doc, i) => {
- if (this.childDocs.includes(doc)) {
- if (docs.length === i + 1) return false;
- } else if (doc.type === DocumentType.LABEL) {
+ if (doc.type === DocumentType.LABEL) {
const audio = Cast(doc.annotationOn, Doc, null);
if (audio) {
- audio.aliasOf instanceof Doc;
audio.presStartTime = NumCast(doc.audioStart);
audio.presEndTime = NumCast(doc.audioEnd);
audio.presDuration = NumCast(doc.audioEnd) - NumCast(doc.audioStart);
TabDocView.PinDoc(audio, false, true);
- setTimeout(() => this.removeDocument(doc), 1);
+ setTimeout(() => this.removeDocument(doc), 0);
return false;
}
} else {
- doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
- !this.childDocs.includes(doc) && (doc.presMovement = PresMovement.Zoom);
- if (this.rootDoc.expandBoolean) doc.presExpandInlineButton = true;
+ if (!doc.aliasOf) {
+ const original = Doc.MakeAlias(doc);
+ TabDocView.PinDoc(original);
+ setTimeout(() => this.removeDocument(doc), 0);
+ return false;
+ } else {
+ if (!doc.presentationTargetDoc) doc.title = doc.title + " - Slide";
+ doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
+ doc.presMovement = PresMovement.Zoom;
+ if (this._expandBoolean) doc.presExpandInlineButton = true;
+ }
}
});
return true;
}
childLayoutTemplate = () => this.rootDoc._viewType !== CollectionViewType.Stacking ? undefined : this.presElement;
- removeDocument = (doc: Doc) => { Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc); };
+ removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.dataDoc, this.fieldKey, doc);
getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
panelHeight = () => this.props.PanelHeight() - 40;
active = (outsideReaction?: boolean) => ((Doc.GetSelectedTool() === InkTool.None && !this.layoutDoc._isBackground) &&
@@ -604,55 +648,67 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
*/
@action
sortArray = (): Doc[] => {
- return this.childDocs.filter(doc => this._selectedArray.includes(doc));
+ return this.childDocs.filter(doc => this._selectedArray.has(doc));
}
/**
* Method to get the list of selected items in the order in which they have been selected
*/
@computed get listOfSelected() {
- const list = this._selectedArray.map((doc: Doc, index: any) => {
+ const list = Array.from(this._selectedArray.keys()).map((doc: Doc, index: any) => {
const curDoc = Cast(doc, Doc, null);
- const tagDoc = Cast(curDoc.presentationTargetDoc!, Doc, null);
- if (tagDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>;
- else if (curDoc) return <div className="selectedList-items">{index + 1}. {curDoc.title}</div>;
+ const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
+ if (curDoc && curDoc === this.activeItem) return <div key={index} className="selectedList-items"><b>{index + 1}. {curDoc.title}</b></div>;
+ else if (tagDoc) return <div key={index} className="selectedList-items">{index + 1}. {curDoc.title}</div>;
+ else if (curDoc) return <div key={index} className="selectedList-items">{index + 1}. {curDoc.title}</div>;
});
return list;
}
@action
selectPres = () => {
- const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc)!;
- SelectionManager.SelectDoc(presDocView, false);
+ const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
+ presDocView && SelectionManager.SelectDoc(presDocView, false);
}
//Regular click
@action
selectElement = (doc: Doc) => {
- this.gotoDocument(this.childDocs.indexOf(doc), NumCast(this.itemIndex));
- if (doc.presPinView) setTimeout(() => this.selectPres(), 0);
- else this.selectPres();
+ const context = Cast(doc.context, Doc, null);
+ this.gotoDocument(this.childDocs.indexOf(doc));
+ if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
+ else this.updateCurrentPresentation(context);
}
//Command click
@action
multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
- if (!this._selectedArray.includes(doc)) {
- this._selectedArray.push(doc);
+ if (!this._selectedArray.has(doc)) {
+ this._selectedArray.set(doc, undefined);
this._eleArray.push(ref);
this._dragArray.push(drag);
+ } else {
+ this._selectedArray.delete(doc);
+ this.removeFromArray(this._eleArray, doc);
+ this.removeFromArray(this._dragArray, doc);
}
this.selectPres();
}
+ removeFromArray = (arr: any[], val: any) => {
+ const index: number = arr.indexOf(val);
+ const ret: any[] = arr.splice(index, 1);
+ arr = ret;
+ }
+
//Shift click
@action
shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => {
- this._selectedArray = [];
+ this._selectedArray.clear();
// const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
if (this.activeItem) {
for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) {
- this._selectedArray.push(this.childDocs[i]);
+ this._selectedArray.set(this.childDocs[i], undefined);
this._eleArray.push(ref);
this._dragArray.push(drag);
}
@@ -660,68 +716,100 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.selectPres();
}
+ //regular click
+ @action
+ regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean) => {
+ this._selectedArray.clear();
+ this._selectedArray.set(doc, undefined);
+ this._eleArray.splice(0, this._eleArray.length, ref);
+ this._dragArray.splice(0, this._dragArray.length, drag);
+ focus && this.selectElement(doc);
+ this.selectPres();
+ }
+
+ modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
+ if (cmdClick) this.multiSelect(doc, ref, drag);
+ else if (shiftClick) this.shiftSelect(doc, ref, drag);
+ else this.regularSelect(doc, ref, drag, focus);
+ }
+
+ static keyEventsWrapper = (e: KeyboardEvent) => {
+ PresBox.Instance.keyEvents(e);
+ }
+
// Key for when the presentaiton is active
- @undoBatch
- keyEvents = action((e: KeyboardEvent) => {
+ @action
+ keyEvents = (e: KeyboardEvent) => {
if (e.target instanceof HTMLInputElement) return;
let handled = false;
const anchorNode = document.activeElement as HTMLDivElement;
if (anchorNode && anchorNode.className?.includes("lm_title")) return;
- if (e.keyCode === 27) { // Escape key
- if (this.layoutDoc.inOverlay) { this.updateMinimize(); }
- else if (this.layoutDoc.presStatus === "edit") { this._selectedArray = []; this._eleArray = []; this._dragArray = []; }
- else this.layoutDoc.presStatus = "edit";
- if (this._presTimer) clearTimeout(this._presTimer);
- handled = true;
- } if ((e.metaKey || e.altKey) && e.keyCode === 65) { // Ctrl-A to select all
- if (this.layoutDoc.presStatus === "edit") {
- this._selectedArray = this.childDocs;
+ switch (e.key) {
+ case "Backspace":
+ if (this.layoutDoc.presStatus === "edit") {
+ undoBatch(action(() => {
+ for (const doc of Array.from(this._selectedArray.keys())) {
+ this.removeDocument(doc);
+ }
+ this._selectedArray.clear();
+ this._eleArray.length = 0;
+ this._dragArray.length = 0;
+ }))();
+ handled = true;
+ }
+ break;
+ case "Escape":
+ if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
+ else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
+ else this.layoutDoc.presStatus = "edit";
+ if (this._presTimer) clearTimeout(this._presTimer);
handled = true;
- }
- } if (e.keyCode === 37 || e.keyCode === 38) { // left(37) / a(65) / up(38) to go back
- this.back();
- if (this._presTimer) clearTimeout(this._presTimer);
- handled = true;
- } if (e.keyCode === 39 || e.keyCode === 40) { // right (39) / d(68) / down(40) to go to next
- this.next();
- if (this._presTimer) clearTimeout(this._presTimer);
- handled = true;
- } if (e.keyCode === 32) { // spacebar to 'present' or autoplay
- if (this.layoutDoc.presStatus !== "edit") this.startAutoPres(0);
- else this.next();
- handled = true;
- } if (e.keyCode === 8) { // delete selected items
- if (this.layoutDoc.presStatus === "edit") {
- this._selectedArray.forEach((doc, i) => this.removeDocument(doc));
- this._selectedArray = [];
- this._eleArray = [];
- this._dragArray = [];
+ break;
+ case "Down": case "ArrowDown":
+ case "Right": case "ArrowRight":
+ if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) { // TODO: update to work properly
+ this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1;
+ this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
+ } else {
+ this.next();
+ if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; }
+ }
handled = true;
- }
- } if (handled) {
+ break;
+ case "Up": case "ArrowUp":
+ case "Left": case "ArrowLeft":
+ if (e.shiftKey && this.itemIndex !== 0) { // TODO: update to work properly
+ this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1;
+ this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
+ } else {
+ this.back();
+ if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; }
+ }
+ handled = true;
+ break;
+ case "Spacebar": case " ":
+ if (this.layoutDoc.presStatus === PresStatus.Manual) this.startAutoPres(this.itemIndex);
+ else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
+ handled = true;
+ break;
+ case "a":
+ if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === "edit") {
+ this._selectedArray.clear();
+ this.childDocs.forEach(doc => this._selectedArray.set(doc, undefined));
+ handled = true;
+ }
+ default:
+ break;
+ }
+ if (handled) {
e.stopPropagation();
e.preventDefault();
- } if ((e.keyCode === 37 || e.keyCode === 38) && e.shiftKey) { // left(37) / a(65) / up(38) to go back
- if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) {
- const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]);
- if ((index - 1) > 0) this._selectedArray.push(this.childDocs[index - 1]);
- }
- handled = true;
- } if ((e.keyCode === 39 || e.keyCode === 40) && e.shiftKey) { // left(37) / a(65) / up(38) to go back
- if (this.layoutDoc.presStatus === "edit" && this._selectedArray.length > 0) {
- const index = this.childDocs.indexOf(this._selectedArray[this._selectedArray.length]);
- if ((index - 1) > 0) {
- this._selectedArray.push(this.childDocs[index - 1]);
- }
- }
- handled = true;
}
- });
+ }
/**
*
*/
- @undoBatch
@action
viewPaths = () => {
const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
@@ -730,28 +818,73 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
}
+ getAllIndexes = (arr: Doc[], val: Doc): number[] => {
+ const indexes = [];
+ for (let i = 0; i < arr.length; i++) {
+ arr[i] === val && indexes.push(i);
+ }
+ return indexes;
+ }
+
// Adds the index in the pres path graphically
@computed get order() {
const order: JSX.Element[] = [];
- this.childDocs.forEach((doc, index) => {
+ const docs: Doc[] = [];
+ const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
+ const dv = DocumentManager.Instance.getDocumentView(presCollection);
+ this.childDocs.filter(doc => Cast(doc.presentationTargetDoc, Doc, null)).forEach((doc, index) => {
const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
- const srcContext = Cast(tagDoc?.context, Doc, null);
+ const srcContext = Cast(tagDoc.context, Doc, null);
const width = NumCast(tagDoc._width) / 10;
const height = Math.max(NumCast(tagDoc._height) / 10, 15);
const edge = Math.max(width, height);
const fontSize = edge * 0.8;
- // Case A: Document is contained within the colleciton
- if (this.rootDoc.presCollection === srcContext) {
- order.push(
- <div className="pathOrder" style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}>
- <div className="pathOrder-frame">{index + 1}</div>
- </div>);
- // Case B: Document is not inside of the collection
- } else {
+ const gap = 2;
+ if (presCollection === srcContext) {
+ // Case A: Document is contained within the collection
+ if (docs.includes(tagDoc)) {
+ const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length;
+ docs.push(tagDoc);
+ order.push(
+ <div className="pathOrder"
+ key={tagDoc.id + 'pres' + index}
+ style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - (edge / 2)), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}
+ onClick={() => this.selectElement(doc)}>
+ <div className="pathOrder-frame">{index + 1}</div>
+ </div>);
+ } else {
+ docs.push(tagDoc);
+ order.push(
+ <div className="pathOrder"
+ key={tagDoc.id + 'pres' + index}
+ style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}
+ onClick={() => this.selectElement(doc)}>
+ <div className="pathOrder-frame">{index + 1}</div>
+ </div>);
+ }
+ } else if (doc.presPinView && presCollection === tagDoc && dv) {
+ // Case B: Document is presPinView and is presCollection
+ const scale: number = 1 / NumCast(doc.presPinViewScale);
+ const height: number = dv.props.PanelHeight() * scale;
+ const width: number = dv.props.PanelWidth() * scale;
+ const indWidth = width / 10;
+ const indHeight = Math.max(height / 10, 15);
+ const indEdge = Math.max(indWidth, indHeight);
+ const indFontSize = indEdge * 0.8;
+ const xLoc: number = NumCast(doc.presPinViewX) - (width / 2);
+ const yLoc: number = NumCast(doc.presPinViewY) - (height / 2);
+ docs.push(tagDoc);
order.push(
- <div className="pathOrder" style={{ top: 0, left: 0 }}>
- <div className="pathOrder-frame">{index + 1}</div>
- </div>);
+ <>
+ <div className="pathOrder"
+ key={tagDoc.id + 'pres' + index}
+ style={{ top: yLoc - (indEdge / 2), left: xLoc - (indEdge / 2), width: indEdge, height: indEdge, fontSize: indFontSize }}
+ onClick={() => this.selectElement(doc)}
+ >
+ <div className="pathOrder-frame">{index + 1}</div>
+ </div>
+ <div className="pathOrder-presPinView" style={{ top: yLoc, left: xLoc, width: width, height: height, borderWidth: indEdge / 10 }}></div>
+ </>);
}
});
return order;
@@ -767,17 +900,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
*/
@computed get paths() {
let pathPoints = "";
+ const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
this.childDocs.forEach((doc, index) => {
const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
const srcContext = Cast(tagDoc?.context, Doc, null);
- if (tagDoc && this.rootDoc.presCollection === srcContext) {
+ if (tagDoc && presCollection === srcContext) {
const n1x = NumCast(tagDoc.x) + (NumCast(tagDoc._width) / 2);
const n1y = NumCast(tagDoc.y) + (NumCast(tagDoc._height) / 2);
if (index = 0) pathPoints = n1x + "," + n1y;
else pathPoints = pathPoints + " " + n1x + "," + n1y;
- } else {
- if (index = 0) pathPoints = 0 + "," + 0;
- else pathPoints = pathPoints + " " + 0 + "," + 0;
+ } else if (doc.presPinView && presCollection === tagDoc) {
+ const n1x = NumCast(doc.presPinViewX);
+ const n1y = NumCast(doc.presPinViewY);
+ if (index = 0) pathPoints = n1x + "," + n1y;
+ else pathPoints = pathPoints + " " + n1x + "," + n1y;
}
});
return (<polyline
@@ -796,53 +932,134 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
/>);
}
- /**
- * The function that is called on click to turn fading document after presented option on/off.
- * It also makes sure that the option swithches from hide-after to this one, since both
- * can't coexist.
- */
- @action
- onFadeDocumentAfterPresentedClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- const activeItem: Doc = this.activeItem;
- const targetDoc: Doc = this.targetDoc;
- activeItem.presFadeButton = !activeItem.presFadeButton;
- if (!activeItem.presFadeButton) {
- if (targetDoc) {
- targetDoc.opacity = 1;
- }
- } else {
- activeItem.presHideAfterButton = false;
- if (this.rootDoc.presStatus !== "edit" && targetDoc) {
- targetDoc.opacity = 0.5;
- }
- }
- }
-
// Converts seconds to ms and updates presTransition
- @undoBatch
setTransitionTime = (number: String, change?: number) => {
let timeInMS = Number(number) * 1000;
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 10000) timeInMS = 10000;
- if (this.activeItem) this.activeItem.presTransition = timeInMS;
+ Array.from(this._selectedArray.keys()).forEach((doc) => doc.presTransition = timeInMS);
}
// Converts seconds to ms and updates presDuration
- @undoBatch
setDurationTime = (number: String, change?: number) => {
let timeInMS = Number(number) * 1000;
if (change) timeInMS += change;
if (timeInMS < 100) timeInMS = 100;
if (timeInMS > 20000) timeInMS = 20000;
- if (this.activeItem) this.activeItem.presDuration = timeInMS;
+ Array.from(this._selectedArray.keys()).forEach((doc) => doc.presDuration = timeInMS);
}
+ /**
+ * When the movement dropdown is changes
+ */
+ @undoBatch
+ updateMovement = action((movement: any, all?: boolean) => {
+ const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
+ array.forEach((doc) => {
+ switch (movement) {
+ case PresMovement.Zoom: //Pan and zoom
+ doc.presMovement = PresMovement.Zoom;
+ break;
+ case PresMovement.Pan: //Pan
+ doc.presMovement = PresMovement.Pan;
+ break;
+ case PresMovement.Jump: //Jump Cut
+ doc.presJump = true;
+ doc.presMovement = PresMovement.Jump;
+ break;
+ case PresMovement.None: default:
+ doc.presMovement = PresMovement.None;
+ break;
+ }
+ });
+ });
+
+ @undoBatch
+ @action
+ updateHideBefore = (activeItem: Doc) => {
+ activeItem.presHideBefore = !activeItem.presHideBefore;
+ Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideBefore = activeItem.presHideBefore);
+ }
+
+ @undoBatch
+ @action
+ updateHideAfter = (activeItem: Doc) => {
+ activeItem.presHideAfter = !activeItem.presHideAfter;
+ Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideAfter = activeItem.presHideAfter);
+ }
+
+ @undoBatch
+ @action
+ updateOpenDoc = (activeItem: Doc) => {
+ activeItem.openDocument = !activeItem.openDocument;
+ Array.from(this._selectedArray.keys()).forEach((doc) => {
+ doc.openDocument = activeItem.openDocument;
+ });
+ }
+
+ @undoBatch
+ @action
+ updateEffectDirection = (effect: any, all?: boolean) => {
+ const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
+ array.forEach((doc) => {
+ const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+ switch (effect) {
+ case PresEffect.Left:
+ tagDoc.presEffectDirection = PresEffect.Left;
+ break;
+ case PresEffect.Right:
+ tagDoc.presEffectDirection = PresEffect.Right;
+ break;
+ case PresEffect.Top:
+ tagDoc.presEffectDirection = PresEffect.Top;
+ break;
+ case PresEffect.Bottom:
+ tagDoc.presEffectDirection = PresEffect.Bottom;
+ break;
+ case PresEffect.Center: default:
+ tagDoc.presEffectDirection = PresEffect.Center;
+ break;
+ }
+ });
+ }
+
+ @undoBatch
+ @action
+ updateEffect = (effect: any, all?: boolean) => {
+ const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
+ array.forEach((doc) => {
+ const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+ switch (effect) {
+ case PresEffect.Bounce:
+ tagDoc.presEffect = PresEffect.Bounce;
+ break;
+ case PresEffect.Fade:
+ tagDoc.presEffect = PresEffect.Fade;
+ break;
+ case PresEffect.Flip:
+ tagDoc.presEffect = PresEffect.Flip;
+ break;
+ case PresEffect.Roll:
+ tagDoc.presEffect = PresEffect.Roll;
+ break;
+ case PresEffect.Rotate:
+ tagDoc.presEffect = PresEffect.Rotate;
+ break;
+ case PresEffect.None: default:
+ tagDoc.presEffect = PresEffect.None;
+ break;
+ }
+ });
+ }
+
+ _batch: UndoManager.Batch | undefined = undefined;
@computed get transitionDropdown() {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
+ const isPresCollection: boolean = (targetDoc === this.layoutDoc.presCollection);
+ const isPinWithView: boolean = BoolCast(activeItem.presPinView);
if (activeItem && targetDoc) {
const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5;
let duration = activeItem.presDuration ? NumCast(activeItem.presDuration) / 1000 : 2;
@@ -853,16 +1070,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = false; this.openEffectDropdown = false; })}>
<div className="ribbon-box">
Movement
- <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? 'solid 2px #5B9FDD' : 'solid 1px black' }}>
- {this.setMovementName(activeItem.presMovement, activeItem)}
- <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} />
- <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None, activeItem, targetDoc)}>None</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom, activeItem, targetDoc)}>Pan and Zoom</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan, activeItem, targetDoc)}>Pan</div>
- <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump, activeItem, targetDoc)}>Jump cut</div>
+ {isPresCollection || (isPresCollection && isPinWithView) ?
+ <div className="ribbon-property" style={{ marginLeft: 0, height: 25, textAlign: 'left', paddingLeft: 5, paddingRight: 5, fontSize: 10 }}>
+ {this.scrollable ? "Scroll to pinned view" : !isPinWithView ? "No movement" : "Pan & Zoom to pinned view"}
</div>
- </div>
+ :
+ <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}>
+ {this.setMovementName(activeItem.presMovement, activeItem)}
+ <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} />
+ <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>Pan</div>
+ <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>Jump cut</div>
+ </div>
+ </div>
+ }
<div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Transition Speed</div>
<div className="ribbon-property">
@@ -871,15 +1094,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
onChange={action((e) => this.setTransitionTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
- <div className="ribbon-propertyUpDownItem" onClick={() => this.setTransitionTime(String(transitionSpeed), 1000)}>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), 1000))}>
<FontAwesomeIcon icon={"caret-up"} />
</div>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.setTransitionTime(String(transitionSpeed), -1000)}>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), -1000))}>
<FontAwesomeIcon icon={"caret-down"} />
</div>
</div>
</div>
- <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed} className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`} id="toolbar-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setTransitionTime(e.target.value); }} />
+ <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed}
+ className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}
+ id="toolbar-slider"
+ onPointerDown={() => this._batch = UndoManager.StartBatch("presTransition")}
+ onPointerUp={() => this._batch?.end()}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+ e.stopPropagation();
+ this.setTransitionTime(e.target.value);
+ }} />
<div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
<div className="slider-text">Fast</div>
<div className="slider-text">Medium</div>
@@ -889,46 +1120,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Visibility {"&"} Duration
<div className="ribbon-doubleButton">
- <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideTillShownButton ? "active" : ""}`} onClick={undoBatch(() => activeItem.presHideTillShownButton = !activeItem.presHideTillShownButton)}>Hide before</div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfterButton ? "active" : ""}`} onClick={undoBatch(() => activeItem.presHideAfterButton = !activeItem.presHideAfterButton)}>Hide after</div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? "#aedef8" : "" }} onClick={undoBatch(() => activeItem.openDocument = !activeItem.openDocument)}>Open</div></Tooltip>
+ {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip>}
+ {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>}
+ <Tooltip title={<><div className="dash-tooltip">{"Open document in a new tab"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? PresColor.LightBlue : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Open</div></Tooltip>
</div>
<div className="ribbon-doubleButton" >
<div className="presBox-subheading">Slide Duration</div>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={duration}
- // onFocus={() => document.removeEventListener("keydown", this.keyEvents, true)}
onChange={action((e) => this.setDurationTime(e.target.value))} /> s
</div>
<div className="ribbon-propertyUpDown">
- <div className="ribbon-propertyUpDownItem" onClick={() => this.setDurationTime(String(duration), 1000)}>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
<FontAwesomeIcon icon={"caret-up"} />
</div>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.setDurationTime(String(duration), -1000)}>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
<FontAwesomeIcon icon={"caret-down"} />
</div>
</div>
</div>
- <input type="range" step="0.1" min="0.1" max="20" value={duration} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }} className={"toolbar-slider"} id="duration-slider" onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }} />
+ <input type="range" step="0.1" min="0.1" max="20" value={duration}
+ style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }}
+ className={"toolbar-slider"} id="duration-slider"
+ onPointerDown={() => { this._batch = UndoManager.StartBatch("presDuration"); }}
+ onPointerUp={() => { if (this._batch) this._batch.end(); }}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }}
+ />
<div className={"slider-headers"} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "grid" }}>
<div className="slider-text">Short</div>
<div className="slider-text">Medium</div>
<div className="slider-text">Long</div>
</div>
</div>
- <div className="ribbon-box">
+ {isPresCollection ? (null) : <div className="ribbon-box">
Effects
- <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? 'solid 2px #5B9FDD' : 'solid 1px black' }}>
+ <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}>
{effect}
- <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? '#5B9FDD' : 'black' }} icon={"angle-down"} />
+ <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} />
<div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}>
- <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'None')}>None</div>
- <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Fade')}>Fade In</div>
- <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Flip')}>Flip</div>
- <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Rotate')}>Rotate</div>
- <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Bounce')}>Bounce</div>
- <div className={'presBox-dropdownOption'} onPointerDown={e => e.stopPropagation()} onClick={undoBatch(() => targetDoc.presEffect = 'Roll')}>Roll</div>
+ <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.None || !targetDoc.presEffect ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.None)}>None</div>
+ <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Fade ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>Fade In</div>
+ <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Flip ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>Flip</div>
+ <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Rotate ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>Rotate</div>
+ <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Bounce ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>Bounce</div>
+ <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Roll ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>Roll</div>
</div>
</div>
<div className="ribbon-doubleButton" style={{ display: effect === 'None' ? "none" : "inline-flex" }}>
@@ -938,17 +1174,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
</div>
<div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "left" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={undoBatch(() => targetDoc.presEffectDirection = 'left')}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === "right" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={undoBatch(() => targetDoc.presEffectDirection = 'right')}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === "top" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={undoBatch(() => targetDoc.presEffectDirection = 'top')}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === "bottom" ? "#5a9edd" : "black", cursor: "pointer" }} onClick={undoBatch(() => targetDoc.presEffectDirection = 'bottom')}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
- <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection ? "solid 2px black" : "solid 2px #5a9edd", borderRadius: "100%", cursor: "pointer" }} onClick={undoBatch(() => targetDoc.presEffectDirection = false)}></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Left ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Right ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Top ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Bottom ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection === PresEffect.Center || !targetDoc.presEffectDirection ? `solid 2px ${PresColor.LightBlue}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip>
</div>
- </div>
+ </div>}
<div className="ribbon-final-box">
- <div className={this._selectedArray.length === 0 ? "ribbon-final-button" : "ribbon-final-button-hidden"} onClick={() => this.applyTo(this._selectedArray)}>
- Apply to selected
- </div>
<div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
Apply to all
</div>
@@ -975,19 +1208,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
applyTo = (array: Doc[]) => {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
+ this.updateMovement(activeItem.presMovement, true);
+ this.updateEffect(targetDoc.presEffect, true);
+ this.updateEffectDirection(targetDoc.presEffectDirection, true);
array.forEach((doc) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
if (tagDoc && targetDoc) {
curDoc.presTransition = activeItem.presTransition;
curDoc.presDuration = activeItem.presDuration;
- tagDoc.presEffect = targetDoc.presEffect;
- tagDoc.presEffectDirection = targetDoc.presEffectDirection;
- tagDoc.presMovement = targetDoc.presMovement;
- curDoc.presMovement = activeItem.presMovement;
- this.updateMovement(activeItem.presMovement, curDoc, tagDoc);
- curDoc.presHideTillShownButton = activeItem.presHideTillShownButton;
- curDoc.presHideAfterButton = activeItem.presHideAfterButton;
+ curDoc.presHideBefore = activeItem.presHideBefore;
+ curDoc.presHideAfter = activeItem.presHideAfter;
}
});
}
@@ -1000,12 +1231,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div>
<div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="ribbon-box">
- <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.playAuto ? "#aedef8" : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div>
- <div className="ribbon-toggle" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : "#aedef8" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div>
+ <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.AUDIO ? "inline-flex" : "none" }}>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.playAuto ? PresColor.LightBlue : "" }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play automatically</div>
+ <div className="ribbon-toggle" style={{ display: "flex", backgroundColor: activeItem.playAuto ? "" : PresColor.LightBlue }} onClick={() => activeItem.playAuto = !activeItem.playAuto}>Play on next</div>
</div>
- {targetDoc.type === DocumentType.VID ? <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presVidFullScreen ? "#aedef8" : "" }} onClick={() => activeItem.presVidFullScreen = !activeItem.presVidFullScreen}>Full screen</div> : (null)}
- {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ {/* {targetDoc.type === DocumentType.VID ? <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presVidFullScreen ? PresColor.LightBlue : "" }} onClick={() => activeItem.presVidFullScreen = !activeItem.presVidFullScreen}>Full screen</div> : (null)} */}
+ {targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
<div className="presBox-subheading">Start time</div>
<div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
<input className="presBox-input"
@@ -1014,7 +1245,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })} />
</div>
</div> : (null)}
- {targetDoc.type === DocumentType.VID || targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ {targetDoc.type === DocumentType.AUDIO ? <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
<div className="presBox-subheading">End time</div>
<div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
<input className="presBox-input"
@@ -1023,13 +1254,45 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presEndTime = Number(val); })} />
</div>
</div> : (null)}
- {targetDoc.type === DocumentType.COL ? 'Presentation Pin View' : (null)}
- <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.COL ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? "#aedef8" : "" }}
+ {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)}
+ <div className="ribbon-doubleButton">
+ <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? PresColor.LightBlue : "" }}
onClick={() => {
activeItem.presPinView = !activeItem.presPinView;
targetDoc.presPinView = activeItem.presPinView;
if (activeItem.presPinView) {
+ if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
+ const scroll = targetDoc._scrollTop;
+ activeItem.presPinView = true;
+ activeItem.presPinViewScroll = scroll;
+ } else if (targetDoc.type === DocumentType.VID) {
+ activeItem.presPinTimecode = targetDoc._currentTimecode;
+ } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
+ const x = targetDoc._panX;
+ const y = targetDoc._panY;
+ const scale = targetDoc._viewScale;
+ activeItem.presPinView = true;
+ activeItem.presPinViewX = x;
+ activeItem.presPinViewY = y;
+ activeItem.presPinViewScale = scale;
+ } else if (targetDoc.type === DocumentType.COMPARISON) {
+ const width = targetDoc._clipWidth;
+ activeItem.presPinClipWidth = width;
+ activeItem.presPinView = true;
+ }
+ }
+ }}>{presPinWithViewIcon}</div></Tooltip>
+ {activeItem.presPinView ? <Tooltip title={<><div className="dash-tooltip">{"Update the pinned view with the view of the selected document"}</div></>}><div className="ribbon-button"
+ onClick={() => {
+ if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
+ const scroll = targetDoc._scrollTop;
+ activeItem.presPinViewScroll = scroll;
+ } else if (targetDoc.type === DocumentType.VID) {
+ activeItem.presPinTimecode = targetDoc._currentTimecode;
+ } else if (targetDoc.type === DocumentType.COMPARISON) {
+ const clipWidth = targetDoc._clipWidth;
+ activeItem.presPinClipWidth = clipWidth;
+ } else {
const x = targetDoc._panX;
const y = targetDoc._panY;
const scale = targetDoc._viewScale;
@@ -1037,18 +1300,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
activeItem.presPinViewY = y;
activeItem.presPinViewScale = scale;
}
- }}>{presPinWithViewIcon}</div>
- {activeItem.presPinView ? <div className="ribbon-button"
- onClick={() => {
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- activeItem.presPinViewX = x;
- activeItem.presPinViewY = y;
- activeItem.presPinViewScale = scale;
- }}>Update</div> : (null)}
+ }}>Update</div></Tooltip> : (null)}
</div>
- <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
+ {this.panable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
<div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
<div className="presBox-subheading">Pan X</div>
<div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
@@ -1076,7 +1330,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
</div>
</div>
- </div>
+ </div> : (null)}
+ {this.scrollable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
+ <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Scroll</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presPinViewScroll)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScroll = Number(val); })} />
+ </div>
+ </div>
+ </div> : (null)}
{/* <div className="ribbon-doubleButton" style={{ display: targetDoc.type === DocumentType.WEB ? "inline-flex" : "none" }}>
<div className="ribbon-toggle" onClick={this.progressivizeText}>Store original website</div>
</div> */}
@@ -1092,15 +1357,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div>
<div className={'presBox-toolbar-dropdown'} style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
<div className="layout-container" style={{ height: 'max-content' }}>
- <div className="layout" style={{ border: this.layout === 'blank' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} />
- <div className="layout" style={{ border: this.layout === 'title' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}>
+ <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} />
+ <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}>
<div className="title">Title</div>
<div className="subtitle">Subtitle</div>
</div>
- <div className="layout" style={{ border: this.layout === 'header' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}>
+ <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}>
<div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
</div>
- <div className="layout" style={{ border: this.layout === 'content' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}>
+ <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}>
<div className="title" style={{ alignSelf: 'center' }}>Title</div>
<div className="content">Text goes here</div>
</div>
@@ -1132,26 +1397,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div className="ribbon-box">
Choose type:
<div className="ribbon-doubleButton">
- <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : "#aedef8" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
- <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "#aedef8" : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
+ <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : PresColor.LightBlue }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
+ <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? PresColor.LightBlue : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
</div>
</div>
<div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}>
Preset layouts:
<div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
- <div className="layout" style={{ border: this.layout === 'blank' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'blank')} />
- <div className="layout" style={{ border: this.layout === 'title' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'title')}>
+ <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'blank')} />
+ <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'title')}>
<div className="title">Title</div>
<div className="subtitle">Subtitle</div>
</div>
- <div className="layout" style={{ border: this.layout === 'header' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'header')}>
+ <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'header')}>
<div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
</div>
- <div className="layout" style={{ border: this.layout === 'content' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'content')}>
+ <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'content')}>
<div className="title" style={{ alignSelf: 'center' }}>Title</div>
<div className="content">Text goes here</div>
</div>
- <div className="layout" style={{ border: this.layout === 'twoColumns' ? 'solid 2px #5b9ddd' : '' }} onClick={action(() => this.layout = 'twoColumns')}>
+ <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'twoColumns')}>
<div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div>
<div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div>
<div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div>
@@ -1183,7 +1448,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (data && presData) {
data.push(doc);
TabDocView.PinDoc(doc, false);
- this.gotoDocument(this.childDocs.length, this.itemIndex);
+ this.gotoDocument(this.childDocs.length);
} else {
this.props.addDocTab(doc, "add:right");
}
@@ -1233,10 +1498,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get presentDropdown() {
return (
<div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
- <div className="dropdown-play-button" onClick={(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}>
+ <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}>
Minimize
</div>
- <div className="dropdown-play-button" onClick={(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}>
+ <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}>
Sidebar view
</div>
</div>
@@ -1244,7 +1509,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
// Case in which the document has keyframes to navigate to next key frame
- @undoBatch
@action
nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
@@ -1260,7 +1524,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
}
- @undoBatch
@action
prevKeyframe = (tagDoc: Doc, actItem: Doc): void => {
const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
@@ -1312,8 +1575,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{/* <div className="ribbon-box">
{this.stringType} selected
<div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeChild}>Contents</div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? "#aedef8" : "" }} onClick={this.editProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeChild}>Contents</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? PresColor.LightBlue : "" }} onClick={this.editProgressivize}>Edit</div>
</div>
<div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
<div className="presBox-subheading">Active text color</div>
@@ -1328,12 +1591,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</div>
{this.viewedColorPicker}
<div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeZoom}>Zoom</div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? "#aedef8" : "" }} onClick={this.editZoomProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeZoom}>Zoom</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.editZoomProgressivize}>Edit</div>
</div>
<div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
- <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? "#aedef8" : "" }} onClick={this.progressivizeScroll}>Scroll</div>
- <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? "#aedef8" : "" }} onClick={this.editScrollProgressivize}>Edit</div>
+ <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeScroll}>Scroll</div>
+ <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.editScrollProgressivize}>Edit</div>
</div>
</div> */}
<div className="ribbon-final-box">
@@ -1343,7 +1606,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc, activeItem); }}>
<FontAwesomeIcon icon={"caret-left"} size={"lg"} />
</div>
- <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.editing ? "white" : "black", backgroundColor: targetDoc.editing ? "#5B9FDD" : "#AEDDF8" }}
+ <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.editing ? "white" : "black", backgroundColor: targetDoc.editing ? PresColor.DarkBlue : PresColor.LightBlue }}
onClick={action(() => targetDoc.editing = !targetDoc.editing)} >
{NumCast(targetDoc._currentFrame)}
</div>
@@ -1357,7 +1620,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{this.frameListHeader}
{this.frameList}
</div>
- <div className="ribbon-toggle" style={{ height: 20, backgroundColor: "#AEDDF8" }} onClick={() => console.log(" TODO: play frames")}>Play</div>
+ <div className="ribbon-toggle" style={{ height: 20, backgroundColor: PresColor.LightBlue }} onClick={() => console.log(" TODO: play frames")}>Play</div>
</div>
</div>
</div>
@@ -1406,12 +1669,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
turnOffEdit = (paths?: boolean) => {
+ // Turn off paths
if (paths) {
- // Turn off paths
const srcContext = Cast(this.rootDoc.presCollection, Doc, null);
if (srcContext) this.togglePath(srcContext, true);
}
- // Turn off the progressivize editors for each
+ // Turn off the progressivize editors for each document
this.childDocs.forEach((doc) => {
doc.editSnapZoomProgressivize = false;
doc.editZoomProgressivize = false;
@@ -1573,7 +1836,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>);
}
tags.push(
- <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? "#aedff8" : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
+ <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? PresColor.LightBlue : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
<div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div>
<div className="progressivizeButton-frame">{doc.appearFrame}</div>
<div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div>
@@ -1582,7 +1845,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
return tags;
}
- @undoBatch
@action
nextAppearFrame = (doc: Doc, i: number): void => {
// const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
@@ -1595,7 +1857,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
this.updateOpacityList(doc["opacity-indexed"], NumCast(doc.appearFrame));
}
- @undoBatch
@action
prevAppearFrame = (doc: Doc, i: number): void => {
// const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
@@ -1656,30 +1917,42 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel";
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
+ const isMini: boolean = this.toolbarWidth <= 100;
+ const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
return (mode === CollectionViewType.Carousel3D) ? (null) : (
- <div id="toolbarContainer" className={'presBox-toolbar'} style={{ display: this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }}>
+ <div id="toolbarContainer" className={'presBox-toolbar'}>
{/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
<FontAwesomeIcon icon={"plus"} />
<FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} />
</div></Tooltip> */}
<Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}>
- <div style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3 }} className={`toolbar-button ${this.pathBoolean ? "active" : ""}`} onClick={this.childDocs.length > 1 ? this.viewPaths : undefined}>
+ <div style={{ opacity: this.childDocs.length > 1 ? 1 : 0.3, color: this._pathBoolean ? PresColor.DarkBlue : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 ? this.viewPaths : undefined}>
<FontAwesomeIcon icon={"exchange-alt"} />
</div>
</Tooltip>
- <div className="toolbar-divider" />
- <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
- <div className={`toolbar-button ${this.rootDoc.expandBoolean ? "active" : ""}`} onClick={this.toggleExpandMode}>
- {/* <FontAwesomeIcon icon={this.rootDoc.expandBoolean ? "eye-slash" : "eye"} /> */}
- <FontAwesomeIcon icon={"eye"} />
- </div>
- </Tooltip>
- <div className="toolbar-divider" />
- <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}>
- <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
- <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? '#AEDDF8' : 'white' }} />
- </div>
- </Tooltip>
+ {isMini ? (null) :
+ <>
+ <div className="toolbar-divider" />
+ {/* <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
+ <div className={"toolbar-button"}
+ style={{ color: this._expandBoolean ? PresColors.DarkBlue : 'white' }}
+ onClick={this.toggleExpandMode}>
+ <FontAwesomeIcon icon={"eye"} />
+ </div>
+ </Tooltip>
+ <div className="toolbar-divider" /> */}
+ <Tooltip title={<><div className="dash-tooltip">{presKeyEvents ? "Keys are active" : "Keys are not active - click anywhere on the presentation trail to activate keys"}</div></>}>
+ <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
+ <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? PresColor.DarkBlue : 'white' }} />
+ </div>
+ </Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}>
+ <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
+ <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? PresColor.DarkBlue : 'white' }} />
+ </div>
+ </Tooltip>
+ </>
+ }
</div>
);
}
@@ -1691,25 +1964,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
*/
@computed get topPanel() {
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
+ const isMini: boolean = this.toolbarWidth <= 100;
return (
<div className="presBox-buttons" style={{ display: this.rootDoc._chromeStatus === "disabled" ? "none" : undefined }}>
- <select className="presBox-viewPicker"
+ {isMini ? (null) : <select className="presBox-viewPicker"
style={{ display: this.layoutDoc.presStatus === "edit" ? "block" : "none" }}
onPointerDown={e => e.stopPropagation()}
onChange={this.viewChanged}
value={mode}>
<option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
<option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>
- </select>
- <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length > 0 ? 1 : 0.3 }}>
+ </select>}
+ <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
<span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}>
- <div className="presBox-button-left" onClick={() => (this.childDocs.length > 0) && (this.layoutDoc.presStatus = "manual")}>
+ <div className="presBox-button-left" onClick={undoBatch(() => (this.childDocs.length) && (this.layoutDoc.presStatus = "manual"))}>
<FontAwesomeIcon icon={"play-circle"} />
<div style={{ display: this.props.PanelWidth() > 200 ? "inline-flex" : "none" }}>&nbsp; Present</div>
</div>
- {(mode === CollectionViewType.Carousel3D) ? (null) : <div className={`presBox-button-right ${this.presentTools ? "active" : ""}`}
+ {(mode === CollectionViewType.Carousel3D || isMini) ? (null) : <div className={`presBox-button-right ${this.presentTools ? "active" : ""}`}
onClick={(action(() => {
- if (this.childDocs.length > 0) this.presentTools = !this.presentTools;
+ if (this.childDocs.length) this.presentTools = !this.presentTools;
}))}>
<FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"angle-down"} />
{this.presentDropdown}
@@ -1755,7 +2029,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get frameListHeader() {
return (<div className="frameList-header">
- Frames
+ &nbsp; Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : (null)}
<div className={"frameList-headerButtons"}>
<Tooltip title={<><div className="dash-tooltip">{"Add frame by example"}</div></>}><div className={"headerButton"} onClick={e => { e.stopPropagation(); this.newFrame(); }}>
<FontAwesomeIcon icon={"plus"} onPointerDown={e => e.stopPropagation()} />
@@ -1801,21 +2075,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
@computed get playButtons() {
+ const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
+ const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
// Case 1: There are still other frames and should go through all frames before going to next slide
return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
- <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
<div className="presPanel-divider"></div>
- <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
- <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
+ <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div>
+ <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? "pause" : "play"} /></div></Tooltip>
+ <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onClick={() => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-right"} /></div>
<div className="presPanel-divider"></div>
- <div className="presPanel-button-text" style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
+ <Tooltip title={<><div className="dash-tooltip">{"Click to return to 1st slide"}</div></>}><div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0)}><b>1</b></div></Tooltip>
+ <div
+ className="presPanel-button-text"
+ onClick={() => this.gotoDocument(0)}
+ style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
Slide {this.itemIndex + 1} / {this.childDocs.length}
{this.playButtonFrames}
</div>
<div className="presPanel-divider"></div>
- {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div>
- : <div className="presPanel-button" onClick={() => this.layoutDoc.presStatus = "edit"}>
+ {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }))}>EXIT</div>
+ : <div className="presPanel-button" onClick={undoBatch(action(() => this.layoutDoc.presStatus = "edit"))}>
<FontAwesomeIcon icon={"times"} />
</div>}
</div>);
@@ -1823,7 +2103,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
startOrPause = () => {
- if (this.layoutDoc.presStatus === "manual" || this.layoutDoc.presStatus === "edit") this.startAutoPres(this.itemIndex);
+ if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex);
else this.pauseAutoPres();
}
@@ -1833,25 +2113,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// needed to ensure that the childDocs are loaded for looking up fields
this.childDocs.slice();
const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
- return this.layoutDoc.inOverlay ?
+ const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+ const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
+ const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
+ return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
<div className="miniPres">
- <div className="presPanelOverlay" style={{ display: "inline-flex", height: 35, background: '#323232', top: 0, zIndex: 3000000 }}>
- <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? '#AEDDF8' : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+ <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + PresColor.DarkBlue : undefined }}>
+ <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
<div className="presPanel-divider"></div>
- <div className="presPanel-button" onClick={this.back}><FontAwesomeIcon icon={"arrow-left"} /></div>
- <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === "auto" ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
- <div className="presPanel-button" onClick={this.next}><FontAwesomeIcon icon={"arrow-right"} /></div>
+ <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div>
+ <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
+ <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onClick={() => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-right"} /></div>
<div className="presPanel-divider"></div>
+ <Tooltip title={<><div className="dash-tooltip">{"Click to return to 1st slide"}</div></>}><div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0)}><b>1</b></div></Tooltip>
<div className="presPanel-button-text">
Slide {this.itemIndex + 1} / {this.childDocs.length}
{this.playButtonFrames}
</div>
<div className="presPanel-divider"></div>
- <div className="presPanel-button-text" onClick={() => { this.updateMinimize(); this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }}>EXIT</div>
+ <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.updateMinimize(); this.layoutDoc.presStatus = PresStatus.Edit; clearTimeout(this._presTimer); }))}>EXIT</div>
</div>
</div>
:
- <div className="presBox-cont" style={{ minWidth: this.layoutDoc.inOverlay ? 240 : undefined }} >
+ <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.OverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
{this.topPanel}
{this.toolbar}
{this.newDocumentToolbarDropdown}
@@ -1878,7 +2162,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data);
- if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 30 : 26;
+ if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 35 : 31;
if (field === 'presStatus') return container.presStatus;
if (field === '_itemIndex') return container._itemIndex;
if (field === 'presBox') return container;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 16ce749bc..f467fef12 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
import { observer } from "mobx-react";
import * as rp from 'request-promise';
-import { Doc } from "../../../fields/Doc";
+import { Doc, WidthSym } from "../../../fields/Doc";
import { documentSchema } from "../../../fields/documentSchemas";
import { InkTool } from "../../../fields/InkField";
import { listSpec, makeInterface } from "../../../fields/Schema";
@@ -34,12 +34,12 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
videoLoad = () => {
const aspect = this.player!.videoWidth / this.player!.videoHeight;
- const nativeWidth = (this.layoutDoc._nativeWidth || 0);
- const nativeHeight = (this.layoutDoc._nativeHeight || 0);
+ const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+ const nativeHeight = Doc.NativeHeight(this.layoutDoc);
if (!nativeWidth || !nativeHeight) {
- if (!this.layoutDoc._nativeWidth) this.layoutDoc._nativeWidth = 400;
- this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / aspect;
- this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
+ if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
+ Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
+ this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
}
}
@@ -48,7 +48,7 @@ export class ScreenshotBox extends ViewBoxBaseComponent<FieldViewProps, Screensh
const height = NumCast(this.layoutDoc._height);
const canvas = document.createElement('canvas');
canvas.width = 640;
- canvas.height = 640 * NumCast(this.layoutDoc._nativeHeight) / NumCast(this.layoutDoc._nativeWidth, 1);
+ canvas.height = 640 / (Doc.NativeAspect(this.layoutDoc) || 1);
const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
if (ctx) {
ctx.rect(0, 0, canvas.width, canvas.height);
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 0c0854ac2..05714f665 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -1,5 +1,8 @@
.videoBox {
transform-origin: top left;
+ width: 100%;
+ height: 100%;
+ position: relative;
.videoBox-viewer {
opacity: 0.99; // hack! overcomes some kind of Chrome weirdness where buttons (e.g., snapshot) disappear at some point as the video is resized larger
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 998ddde9a..bc69a3954 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -6,7 +6,7 @@ import * as rp from 'request-promise';
import { Doc } from "../../../fields/Doc";
import { InkTool } from "../../../fields/InkField";
import { createSchema, makeInterface } from "../../../fields/Schema";
-import { Cast, StrCast } from "../../../fields/Types";
+import { Cast, StrCast, NumCast } from "../../../fields/Types";
import { VideoField } from "../../../fields/URLField";
import { Utils, emptyFunction, returnOne, returnZero, OmitKeys } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
@@ -20,6 +20,7 @@ import "./VideoBox.scss";
import { documentSchema } from "../../../fields/documentSchemas";
import { Networking } from "../../Network";
import { SnappingManager } from "../../util/SnappingManager";
+import { SelectionManager } from "../../util/SelectionManager";
const path = require('path');
export const timeSchema = createSchema({
@@ -51,30 +52,44 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
videoLoad = () => {
const aspect = this.player!.videoWidth / this.player!.videoHeight;
- this.layoutDoc._nativeWidth = this.player!.videoWidth;
- this.layoutDoc._nativeHeight = (this.layoutDoc._nativeWidth || 0) / aspect;
+ Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
+ Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
this.layoutDoc._height = (this.layoutDoc._width || 0) / aspect;
- this.dataDoc[this.fieldKey + "-" + "duration"] = this.player!.duration;
+ this.dataDoc[this.fieldKey + "-duration"] = this.player!.duration;
}
@action public Play = (update: boolean = true) => {
this._playing = true;
- update && this.player?.play();
- update && this._youtubePlayer?.playVideo();
- this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
+ try {
+ update && this.player?.play();
+ update && this._youtubePlayer?.playVideo();
+ this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
+ } catch (e) {
+ console.log("Video Play Exception:", e);
+ }
this.updateTimecode();
}
@action public Seek(time: number) {
- this._youtubePlayer?.seekTo(Math.round(time), true);
+ try {
+ this._youtubePlayer?.seekTo(Math.round(time), true);
+ } catch (e) {
+ console.log("Video Seek Exception:", e);
+ }
this.player && (this.player.currentTime = time);
}
@action public Pause = (update: boolean = true) => {
this._playing = false;
- update && this.player?.pause();
- update && this._youtubePlayer?.pauseVideo();
- this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
+ try {
+ update && this.player?.pause();
+ update && this._youtubePlayer?.pauseVideo();
+ this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
+ this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
+ } catch (e) {
+ console.log("Video Pause Exception:", e);
+ }
+ this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
this._playTimer = undefined;
this.updateTimecode();
}
@@ -82,7 +97,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@action public FullScreen() {
this._fullScreen = true;
this.player && this.player.requestFullscreen();
- this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+ try {
+ this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+ } catch (e) {
+ console.log("Video FullScreen Exception:", e);
+ }
}
choosePath(url: string) {
@@ -97,7 +116,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const height = (this.layoutDoc._height || 0);
const canvas = document.createElement('canvas');
canvas.width = 640;
- canvas.height = 640 * (this.layoutDoc._nativeHeight || 0) / (this.layoutDoc._nativeWidth || 1);
+ canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
if (ctx) {
// ctx.rect(0, 0, canvas.width, canvas.height);
@@ -129,25 +148,22 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
// if you want to preview the captured image,
const filename = path.basename(encodeURIComponent("snapshot" + StrCast(this.rootDoc.title).replace(/\..*$/, "") + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
- VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => {
- if (returnedFilename) {
- this.createRealSummaryLink(returnedFilename);
- }
- });
+ VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
+ returnedFilename && this.createRealSummaryLink(returnedFilename));
}
}
private createRealSummaryLink = (relative: string) => {
const url = this.choosePath(Utils.prepend(relative));
- const width = (this.layoutDoc._width || 0);
- const height = (this.layoutDoc._height || 0);
+ const width = this.layoutDoc._width || 0;
+ const height = this.layoutDoc._height || 0;
const imageSummary = Docs.Create.ImageDocument(url, {
- _nativeWidth: this.layoutDoc._nativeWidth, _nativeHeight: this.layoutDoc._nativeHeight,
+ _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0),
_width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-"
});
- Doc.GetProto(imageSummary)["data-nativeWidth"] = this.layoutDoc._nativeWidth;
- Doc.GetProto(imageSummary)["data-nativeHeight"] = this.layoutDoc._nativeHeight;
+ Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
+ Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
imageSummary.isLinkButton = true;
this.props.addDocument?.(imageSummary);
DocUtils.MakeLink({ doc: imageSummary }, { doc: this.rootDoc }, "video snapshot");
@@ -156,7 +172,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
@action
updateTimecode = () => {
this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
- this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime());
+ try {
+ this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
+ } catch (e) {
+ console.log("Video Timecode Exception:", e);
+ }
}
componentDidMount() {
@@ -164,11 +184,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
if (this.youtubeVideoId) {
const youtubeaspect = 400 / 315;
- const nativeWidth = (this.layoutDoc._nativeWidth || 0);
- const nativeHeight = (this.layoutDoc._nativeHeight || 0);
+ const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+ const nativeHeight = Doc.NativeHeight(this.layoutDoc);
if (!nativeWidth || !nativeHeight) {
- if (!this.layoutDoc._nativeWidth) this.layoutDoc._nativeWidth = 600;
- this.layoutDoc._nativeHeight = (this.layoutDoc._nativeWidth || 0) / youtubeaspect;
+ if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
+ Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
this.layoutDoc._height = (this.layoutDoc._width || 0) / youtubeaspect;
}
}
@@ -258,7 +278,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
}
else this._youtubeContentCreated = false;
- const iframe = e.target;
+ this.loadYouTube(e.target);
+ }
+ private loadYouTube = (iframe: any) => {
let started = true;
const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
if (started && event.data === YT.PlayerState.PLAYING) {
@@ -278,13 +300,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
() => !this.props.Document.isAnnotating && Doc.GetSelectedTool() === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
(interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
};
- this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
- events: {
- 'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
- 'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
- }
- });
-
+ if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
+ else {
+ (YT as any)?.ready(() => {
+ this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
+ events: {
+ 'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
+ 'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
+ }
+ });
+ });
+ }
}
private get uIButtons() {
const curTime = (this.layoutDoc._currentTimecode || 0);
@@ -346,26 +372,33 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
const style = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
- onLoad={this.youtubeIframeLoaded} className={`${style}`} width={(this.layoutDoc._nativeWidth || 640)} height={(this.layoutDoc._nativeHeight || 390)}
+ onPointerLeave={this.updateTimecode}
+ onLoad={this.youtubeIframeLoaded} className={`${style}`} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._showControls ? 1 : 0}`} />;
}
@action.bound
addDocumentWithTimestamp(doc: Doc | Doc[]): boolean {
const docs = doc instanceof Doc ? [doc] : doc;
- docs.forEach(doc => {
- const curTime = (this.layoutDoc._currentTimecode || -1);
- curTime !== -1 && (doc.displayTimecode = curTime);
- });
+ const curTime = NumCast(this.layoutDoc._currentTimecode);
+ docs.forEach(doc => doc.displayTimecode = curTime);
return this.addDocument(doc);
}
+ @computed get contentScaling() { return this.props.ContentScaling(); }
contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
render() {
return (<div className="videoBox" onContextMenu={this.specificContextMenu}
- style={{ transform: `scale(${this.props.ContentScaling()})`, width: `${100 / this.props.ContentScaling()}%`, height: `${100 / this.props.ContentScaling()}%` }} >
+ style={{
+ transform: this.props.PanelWidth() ? undefined : `scale(${this.contentScaling})`,
+ width: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
+ height: this.props.PanelWidth() ? undefined : `${100 / this.contentScaling}%`,
+ pointerEvents: this.layoutDoc._isBackground ? "none" : undefined,
+ borderRadius: `${Number(StrCast(this.layoutDoc.borderRounding).replace("px", "")) / this.contentScaling}px`
+ }} >
<div className="videoBox-viewer" >
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+ forceScaling={true}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
annotationsKey={this.annotationKey}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 66dc3cdcc..80e2d3ce2 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,4 +1,3 @@
-import { faMousePointer, faPen, faStickyNote } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
@@ -44,6 +43,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
static _annotationStyle: any = addStyleSheet();
+ public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _startX: number = 0;
private _startY: number = 0;
@@ -52,39 +52,52 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
@observable private _marqueeWidth: number = 0;
@observable private _marqueeHeight: number = 0;
@observable private _marqueeing: boolean = false;
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
- get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; }
- set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; }
@observable private _url: string = "hello";
@observable private _pressX: number = 0;
@observable private _pressY: number = 0;
+ @observable private _iframe: HTMLIFrameElement | null = null;
@observable private _savedAnnotations: Dictionary<number, HTMLDivElement[]> = new Dictionary<number, HTMLDivElement[]>();
private _selectionReactionDisposer?: IReactionDisposer;
private _scrollReactionDisposer?: IReactionDisposer;
+ private _scrollTopReactionDisposer?: IReactionDisposer;
private _moveReactionDisposer?: IReactionDisposer;
- private _keyInput = React.createRef<HTMLInputElement>();
private _longPressSecondsHack?: NodeJS.Timeout;
private _outerRef = React.createRef<HTMLDivElement>();
- private _iframeRef = React.createRef<HTMLIFrameElement>();
private _iframeIndicatorRef = React.createRef<HTMLDivElement>();
private _iframeDragRef = React.createRef<HTMLDivElement>();
private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void);
+ get scrollHeight() { return this.webpage?.scrollHeight || 1000; }
+ get _collapsed() { return StrCast(this.layoutDoc._chromeStatus) !== "enabled"; }
+ set _collapsed(value) { this.layoutDoc._chromeStatus = !value ? "enabled" : "disabled"; }
+ get webpage() { return this._iframe?.contentDocument?.children[0]; }
constructor(props: any) {
super(props);
- this.dataDoc[this.fieldKey + "-nativeWidth"] = this.Document._nativeWidth = NumCast(this.dataDoc[this.props.fieldKey + "-nativeWidth"], NumCast(this.Document._nativeWidth, 850));
- this.dataDoc[this.fieldKey + "-nativeHeight"] = this.Document._nativeHeight = NumCast(this.dataDoc[this.props.fieldKey + "-nativeHeight"], NumCast(this.Document._nativeHeight, this.Document[HeightSym]() / this.Document[WidthSym]() * 850));
+ if (this.dataDoc[this.fieldKey] instanceof WebField) {
+ Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850);
+ Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850);
+ }
}
iframeLoaded = action((e: any) => {
- const iframe = this._iframeRef.current;
- if (iframe && iframe.contentDocument) {
+ const iframe = this._iframe;
+ if (iframe?.contentDocument) {
iframe.setAttribute("enable-annotation", "true");
- 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.addEventListener("click", undoBatch(action(e => {
+ let href = "";
+ for (let ele = e.target; ele; ele = ele.parentElement) {
+ href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
+ }
+ if (href) {
+ this._url = href.replace(Utils.prepend(""), Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.origin);
+ this.submitURL();
+ }
+ })));
+ iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
+ if (this.webpage) {
+ this.webpage.scrollTop = NumCast(this.layoutDoc._scrollTop);
+ this.webpage.scrollLeft = NumCast(this.layoutDoc._scrollLeft);
+ }
}
this._scrollReactionDisposer?.();
this._scrollReactionDisposer = reaction(() => ({ scrollY: this.layoutDoc._scrollY, scrollX: this.layoutDoc._scrollX }),
@@ -93,17 +106,30 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
const duration = durationStr ? Number(durationStr[1]) : 1000;
if (scrollY !== undefined) {
- setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollY || 0)), delay);
- setTimeout(() => { this.layoutDoc._scrollTop = scrollY; this.layoutDoc._scrollY = undefined; }, duration + delay);
+ this._forceSmoothScrollUpdate = true;
+ this.layoutDoc._scrollY = undefined;
+ setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollY || 0), () => this.layoutDoc._scrollTop = scrollY), delay);
}
if (scrollX !== undefined) {
- setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollX || 0)), delay);
- setTimeout(() => { this.layoutDoc._scrollLeft = scrollX; this.layoutDoc._scrollX = undefined; }, duration + delay);
+ this._forceSmoothScrollUpdate = true;
+ this.layoutDoc._scrollX = undefined;
+ setTimeout(() => this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollX || 0), () => this.layoutDoc._scrollLeft = scrollX), delay);
}
},
{ fireImmediately: true }
);
+ this._scrollTopReactionDisposer = reaction(() => this.layoutDoc._scrollTop,
+ scrollTop => {
+ const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]*)ms/);
+ const duration = durationStr ? Number(durationStr[1]) : 1000;
+ if (scrollTop !== this._outerRef.current?.scrollTop && scrollTop !== undefined && this._forceSmoothScrollUpdate) {
+ this._outerRef.current && smoothScroll(duration, this._outerRef.current, Math.abs(scrollTop || 0), () => this._forceSmoothScrollUpdate = true);
+ } else this._forceSmoothScrollUpdate = true;
+ },
+ { fireImmediately: true }
+ );
});
+ _forceSmoothScrollUpdate = true;
updateScroll = (x: Opt<number>, y: Opt<number>) => {
if (y !== undefined) {
@@ -117,16 +143,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func;
- iframedown = (e: PointerEvent) => {
- this._setPreviewCursor?.(e.screenX, e.screenY, false);
- }
- iframeScrolled = (e: any) => {
- if (e.target?.children) {
- e.target.children[0].scrollLeft = 0;
- 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;
+ iframeWheel = (e: any) => {
+ if (this._forceSmoothScrollUpdate && e.target?.children) {
+ this.webpage && setTimeout(action(() => {
+ this.webpage!.scrollLeft = 0;
+ const scrollTop = this.webpage!.scrollTop;
+ const scrollLeft = this.webpage!.scrollLeft;
+ this._outerRef.current!.scrollTop = scrollTop;
+ this._outerRef.current!.scrollLeft = scrollLeft;
+ if (this.layoutDoc._scrollTop !== scrollTop) {
+ this._forceSmoothScrollUpdate = false;
+ this.layoutDoc._scrollTop = scrollTop;
+ }
+ if (this.layoutDoc._scrollLeft !== scrollLeft) {
+ this._forceSmoothScrollUpdate = false;
+ this.layoutDoc._scrollLeft = scrollLeft;
+ }
+ }));
}
}
async componentDidMount() {
@@ -151,13 +184,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
const field = Cast(this.rootDoc[this.props.fieldKey], WebField);
if (field?.url.href.indexOf("youtube") !== -1) {
const youtubeaspect = 400 / 315;
- const nativeWidth = NumCast(this.layoutDoc._nativeWidth);
- const nativeHeight = NumCast(this.layoutDoc._nativeHeight);
+ const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+ const nativeHeight = Doc.NativeHeight(this.layoutDoc);
if (field) {
if (!nativeWidth || !nativeHeight || Math.abs(nativeWidth / nativeHeight - youtubeaspect) > 0.05) {
- if (!nativeWidth) this.layoutDoc._nativeWidth = 600;
- this.layoutDoc._nativeHeight = NumCast(this.layoutDoc._nativeWidth) / youtubeaspect;
- this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
+ if (!nativeWidth) Doc.SetNativeWidth(this.layoutDoc, 600);
+ Doc.SetNativeHeight(this.layoutDoc, (nativeWidth || 600) / youtubeaspect);
+ this.layoutDoc._height = this.layoutDoc[WidthSym]() / youtubeaspect;
}
} // else it's an HTMLfield
} else if (field?.url) {
@@ -171,21 +204,18 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
componentWillUnmount() {
this._moveReactionDisposer?.();
this._selectionReactionDisposer?.();
+ this._scrollTopReactionDisposer?.();
this._scrollReactionDisposer?.();
document.removeEventListener("pointerup", this.onLongPressUp);
document.removeEventListener("pointermove", this.onLongPressMove);
- this._iframeRef.current?.contentDocument?.removeEventListener('pointerdown', this.iframedown);
- this._iframeRef.current?.contentDocument?.removeEventListener('scroll', this.iframeScrolled);
- }
-
- @action
- onURLChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._url = e.target.value;
+ this._iframe?.removeEventListener('wheel', this.iframeWheel);
}
onUrlDragover = (e: React.DragEvent) => {
e.preventDefault();
}
+
+ @undoBatch
@action
onUrlDrop = (e: React.DragEvent) => {
const { dataTransfer } = e;
@@ -297,9 +327,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
let pressedBound: ClientRect | undefined;
let selectedText: string = "";
let pressedImg: boolean = false;
- if (this._iframeRef.current) {
- const B = this._iframeRef.current.getBoundingClientRect();
- const iframeDoc = this._iframeRef.current.contentDocument;
+ if (this._iframe) {
+ const B = this._iframe.getBoundingClientRect();
+ const iframeDoc = this._iframe.contentDocument;
if (B && iframeDoc) {
// TODO: this only works when scale = 1 as it is currently only inteded for mobile upload
const element = iframeDoc.elementFromPoint(this._pressX - B.left, this._pressY - B.top);
@@ -388,17 +418,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
}
-
- @undoBatch
- @action
- toggleNativeDimensions = () => {
- Doc.toggleNativeDimensions(this.layoutDoc, this.props.ContentScaling(), this.props.NativeWidth?.() || 0, this.props.NativeHeight?.() || 0);
- }
specificContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
const funcs: ContextMenuProps[] = [];
funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
- funcs.push({ description: (this.layoutDoc[this.fieldKey + "-contentWidth"] ? "Unfreeze" : "Freeze") + " Content Width", event: () => this.layoutDoc[this.fieldKey + "-contentWidth"] = this.layoutDoc[this.fieldKey + "-contentWidth"] ? undefined : NumCast(this.layoutDoc._nativeWidth), icon: "snowflake" });
+ funcs.push({ description: (this.layoutDoc[this.fieldKey + "-contentWidth"] ? "Unfreeze" : "Freeze") + " Content Width", event: () => this.layoutDoc[this.fieldKey + "-contentWidth"] = this.layoutDoc[this.fieldKey + "-contentWidth"] ? undefined : Doc.NativeWidth(this.layoutDoc), icon: "snowflake" });
cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
}
@@ -416,11 +440,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
const url = this.layoutDoc.useCors ? Utils.CorsProxy(field.url.href) : field.url.href;
// view = <iframe className="webBox-iframe" src={url} onLoad={e => { e.currentTarget.before((e.currentTarget.contentDocument?.body || e.currentTarget.contentDocument)?.children[0]!); e.currentTarget.remove(); }}
- view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={url} onLoad={this.iframeLoaded}
+ view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded}
// the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
} else {
- view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={this._iframeRef} src={"https://crossorigin.me/https://cs.brown.edu"} />;
+ view = <iframe className="webBox-iframe" enable-annotation={"true"} ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
}
return view;
}
@@ -468,7 +492,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
@computed get annotationLayer() {
TraceMobx();
- return <div className="webBox-annotationLayer" style={{ height: NumCast(this.Document._nativeHeight) }} ref={this._annotationLayer}>
+ return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
{this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
<Annotation {...this.props} showInfo={emptyFunction} focus={this.props.focus} dataDoc={this.dataDoc} fieldKey={this.props.fieldKey} anno={anno} key={`${anno[Id]}-annotation`} />)
}
@@ -542,10 +566,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
}
else if (this._mainCont.current) {
// set marquee x and y positions to the spatially transformed position
+ const nheight = Doc.NativeHeight(this.Document) || 1;
+ const nwidth = Doc.NativeWidth(this.Document) || 1;
const boundingRect = this._mainCont.current.getBoundingClientRect();
- const boundingHeight = (this.Document._nativeHeight || 1) / (this.Document._nativeWidth || 1) * boundingRect.width;
- this._startX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1);
- this._startY = (e.clientY - boundingRect.top) / boundingHeight * (this.Document._nativeHeight || 1);
+ const boundingHeight = nheight / nwidth * boundingRect.width;
+ this._startX = (e.clientX - boundingRect.left) / boundingRect.width * nwidth;
+ this._startY = (e.clientY - boundingRect.top) / boundingHeight * nheight;
this._marqueeHeight = this._marqueeWidth = 0;
this._marqueeing = true;
}
@@ -560,9 +586,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
if (this._marqueeing && this._mainCont.current) {
// transform positions and find the width and height to set the marquee to
const boundingRect = this._mainCont.current.getBoundingClientRect();
- const boundingHeight = (this.Document._nativeHeight || 1) / (this.Document._nativeWidth || 1) * boundingRect.width;
- const curX = (e.clientX - boundingRect.left) / boundingRect.width * (this.Document._nativeWidth || 1);
- const curY = (e.clientY - boundingRect.top) / boundingHeight * (this.Document._nativeHeight || 1);
+ const boundingHeight = (Doc.NativeHeight(this.Document) || 1) / (Doc.NativeWidth(this.Document) || 1) * boundingRect.width;
+ const curX = (e.clientX - boundingRect.left) / boundingRect.width * (Doc.NativeWidth(this.Document) || 1);
+ const curY = (e.clientY - boundingRect.top) / boundingHeight * (Doc.NativeHeight(this.Document) || 1);
this._marqueeWidth = curX - this._startX;
this._marqueeHeight = curY - this._startY;
this._marqueeX = Math.min(this._startX, this._startX + this._marqueeWidth);
@@ -619,23 +645,24 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
marqueeX = () => this._marqueeX;
marqueeY = () => this._marqueeY;
marqueeing = () => this._marqueeing;
- visibleHeiht = () => {
+ visibleHeight = () => {
if (this._mainCont.current) {
const boundingRect = this._mainCont.current.getBoundingClientRect();
- const scalin = (this.Document._nativeWidth || 0) / boundingRect.width;
- return Math.min(boundingRect.height * scalin, this.props.PanelHeight() * scalin);
+ const scaling = (Doc.NativeWidth(this.Document) || 0) / boundingRect.width;
+ return Math.min(boundingRect.height * scaling, this.props.PanelHeight() * scaling);
}
return this.props.PanelHeight();
}
scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop));
render() {
+ const scaling = Number.isFinite(this.props.ContentScaling()) ? this.props.ContentScaling() || 1 : 1;
return (<div className="webBox" ref={this._mainCont} >
<div className={`webBox-container`}
style={{
position: undefined,
- transform: `scale(${this.props.ContentScaling()})`,
- width: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
- height: Number.isFinite(this.props.ContentScaling()) ? `${100 / this.props.ContentScaling()}% ` : "100%",
+ transform: `scale(${scaling})`,
+ width: `${100 / scaling}% `,
+ height: `${100 / scaling}% `,
pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
}}
onContextMenu={this.specificContextMenu}>
@@ -643,33 +670,36 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps, WebDocum
{this.content}
<div className={"webBox-outerContent"} ref={this._outerRef}
style={{
- width: Number.isFinite(this.props.ContentScaling()) ? `${Math.max(100, 100 / this.props.ContentScaling())}% ` : "100%",
+ width: `${Math.max(100, 100 / scaling)}% `,
pointerEvents: this.layoutDoc.isAnnotating && !this.layoutDoc._isBackground ? "all" : "none"
}}
- onWheel={e => e.stopPropagation()}
- onPointerDown={this.onMarqueeDown}
- onScroll={e => {
- const iframe = this._iframeRef?.current?.contentDocument;
- const outerFrame = this._outerRef.current;
- if (iframe && outerFrame) {
- if (iframe.children[0].scrollTop !== outerFrame.scrollTop) {
- iframe.children[0].scrollTop = outerFrame.scrollTop;
- }
- if (iframe.children[0].scrollLeft !== outerFrame.scrollLeft) {
- iframe.children[0].scrollLeft = outerFrame.scrollLeft;
- }
+ onWheel={e => {
+ const target = this._outerRef.current;
+ if (this._forceSmoothScrollUpdate && target && this.webpage) {
+ setTimeout(action(() => {
+ target.scrollLeft = 0;
+ const scrollTop = target.scrollTop;
+ const scrollLeft = target.scrollLeft;
+ this.webpage!.scrollTop = scrollTop;
+ this.webpage!.scrollLeft = scrollLeft;
+ if (this.layoutDoc._scrollTop !== scrollTop) this.layoutDoc._scrollTop = scrollTop;
+ if (this.layoutDoc._scrollLeft !== scrollLeft) this.layoutDoc._scrollLeft = scrollLeft;
+ }));
}
- //this._outerRef.current!.scrollTop !== this._scrollTop && (this._outerRef.current!.scrollTop = this._scrollTop)
- }}>
+ e.stopPropagation();
+ }}
+ onPointerDown={this.onMarqueeDown}
+ onScroll={e => e.stopPropagation()}
+ >
<div className={"webBox-innerContent"} style={{
- height: NumCast(this.layoutDoc.scrollHeight),
+ height: NumCast(this.scrollHeight, 50),
pointerEvents: this.layoutDoc._isBackground ? "none" : undefined
}}>
<CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
PanelHeight={this.props.PanelHeight}
PanelWidth={this.props.PanelWidth}
annotationsKey={this.annotationKey}
- VisibleHeight={this.visibleHeiht}
+ VisibleHeight={this.visibleHeight}
focus={this.props.focus}
setPreviewCursor={this.setPreviewCursor}
isSelected={this.props.isSelected}
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 123946dea..7cd92b8b9 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -82,7 +82,7 @@ export class DashDocView extends React.Component<IDashDocView> {
const { scale, translateX, translateY } = Utils.GetScreenTransform(outerElement);
return new Transform(-translateX, -translateY, 1).scale(1 / this.contentScaling() / scale);
}
- contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
+ contentScaling = () => Doc.NativeWidth(this._dashDoc) > 0 ? this._dashDoc![WidthSym]() / Doc.NativeWidth(this._dashDoc) : 1;
outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 0d92d7062..b75cc230f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -22,8 +22,7 @@
border-style: solid;
overflow-y: auto;
overflow-x: hidden;
- color: initial;
- max-height: 100%;
+ color: inherit;
display: flex;
flex-direction: row;
transition: opacity 1s;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 903bbaaa3..fe38939c5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -11,7 +11,7 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
import { ReplaceStep } from 'prosemirror-transform';
import { EditorView } from "prosemirror-view";
import { DateField } from '../../../../fields/DateField';
-import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclEdit, AclAdmin, UpdatingFromServer } from "../../../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, AclEdit, AclAdmin, UpdatingFromServer, ForceServerWrite } from "../../../../fields/Doc";
import { documentSchema } from '../../../../fields/documentSchemas';
import applyDevTools = require("prosemirror-dev-tools");
import { removeMarkWithAttrs } from "./prosemirrorPatches";
@@ -663,7 +663,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const optionItems = options && "subitems" in options ? options.subitems : [];
!Doc.UserDoc().noviceMode && optionItems.push({ description: !this.Document._singleLine ? "Make Single Line" : "Make Multi Line", event: () => this.layoutDoc._singleLine = !this.layoutDoc._singleLine, icon: "expand-arrows-alt" });
optionItems.push({ description: `${this.Document._autoHeight ? "Lock" : "Auto"} Height`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
- optionItems.push({ description: `${!this.layoutDoc._nativeWidth || !this.layoutDoc._nativeHeight ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" });
+ optionItems.push({ description: `${!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Lock" : "Unlock"} Aspect`, event: this.toggleNativeDimensions, icon: "snowflake" });
!options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
this._downX = this._downY = Number.NaN;
}
@@ -805,13 +805,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
tr = tr.addMark(pos, pos + node.nodeSize, link);
}
});
- this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
+ this.dataDoc[ForceServerWrite] = this.dataDoc[UpdatingFromServer] = true; // need to allow permissions for adding links to readonly/augment only documents
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
- this.dataDoc[UpdatingFromServer] = false;
+ this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
}
}
componentDidMount() {
this._cachedLinks = DocListCast(this.Document.links);
+ this._disposers.sidebarheight = reaction(
+ () => ({ annoHeight: NumCast(this.rootDoc[this.annotationKey + "-height"]), textHeight: NumCast(this.rootDoc[this.fieldKey + "-height"]) }),
+ ({ annoHeight, textHeight }) => {
+ this.layoutDoc._autoHeight && (this.rootDoc._height = Math.max(annoHeight, textHeight));
+ });
this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
newLinks => {
this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l));
@@ -1454,10 +1459,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
public endUndoTypingBatch() {
const wasUndoing = this._undoTyping;
- if (this._undoTyping) {
- this._undoTyping.end();
- this._undoTyping = undefined;
- }
+ this._undoTyping?.end();
+ this._undoTyping = undefined;
return wasUndoing;
}
public static LiveTextUndo: UndoManager.Batch | undefined;
@@ -1470,8 +1473,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
FormattedTextBox.LiveTextUndo?.end();
FormattedTextBox.LiveTextUndo = undefined;
- // move the richtextmenu offscreen
- //if (!RichTextMenu.Instance.Pinned) RichTextMenu.Instance.delayHide();
}
_lastTimedMark: Mark | undefined = undefined;
@@ -1544,18 +1545,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
}, 10);
} else {
try {
- const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", ""));
+ const boxHeight = Number(getComputedStyle(this._boxRef.current!).height.replace("px", "")) * NumCast(this.Document._viewScale, 1);
const outer = this.rootDoc[HeightSym]() - boxHeight - (this.props.ChromeHeight ? this.props.ChromeHeight() : 0);
- const finalHeight = newHeight + Math.max(0, outer);
- const maxsidebar = !this.sidebarWidth() ? 0 : Array.from(this._boxRef.current!.getElementsByClassName("collectionStackingViewFieldColumn")).reduce((prev, ele) => Math.max(prev, Number(getComputedStyle(ele).height.replace("px", ""))), 0);
- if (this.rootDoc._height !== finalHeight && finalHeight > maxsidebar) {
- this.rootDoc._height = finalHeight;
- this.layoutDoc._nativeHeight = nh ? scrollHeight : undefined;
- }
- this.rootDoc[this.fieldKey + "-height"] = finalHeight;
+ this.rootDoc[this.fieldKey + "-height"] = newHeight + Math.max(0, outer);
} catch (e) { console.log("Error in tryUpdateHeight"); }
}
- } else this.rootDoc[this.fieldKey + "-height"] = 0;
+ } //else this.rootDoc[this.fieldKey + "-height"] = 0;
}
@computed get audioHandle() {
@@ -1574,10 +1569,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
/>;
}
+ sidebarContentScaling = () => this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1);
@computed get sidebarCollection() {
const fitToBox = this.props.Document._fitToBox;
const collectionProps = {
...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit,
+ NativeWidth: returnZero,
+ NativeHeight: returnZero,
PanelHeight: this.props.PanelHeight,
PanelWidth: this.sidebarWidth,
xMargin: 0,
@@ -1591,7 +1589,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
isSelected: this.props.isSelected,
select: emptyFunction,
active: this.annotationsActive,
- ContentScaling: returnOne,
+ ContentScaling: this.sidebarContentScaling,
whenActiveChanged: this.whenActiveChanged,
removeDocument: this.removeDocument,
moveDocument: this.moveDocument,
@@ -1610,7 +1608,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
@computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
- sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0);
+ sidebarScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth()) / this.props.ContentScaling(), 0).scale(1 / NumCast(this.layoutDoc._viewScale, 1));
@computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
render() {
TraceMobx();
@@ -1626,12 +1624,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
const padding = Math.max(margins + ((selected && !this.layoutDoc._singleLine) || minimal ? -selPad : 0), 0);
const selclass = selected && !this.layoutDoc._singleLine && margins >= 10 ? "-selected" : "";
return (
- <div className={"formattedTextBox-cont"} ref={this._boxRef}
+ <div className="formattedTextBox-cont" ref={this._boxRef}
style={{
transform: `scale(${scale})`,
transformOrigin: "top left",
width: `${100 / scale}%`,
height: `calc(${100 / scale}% - ${this.props.ChromeHeight?.() || 0}px)`,
+ overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
}}>
<div className={`formattedTextBox-cont`} ref={this._ref}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
index 32038d1ee..3e5a40084 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx
@@ -320,8 +320,8 @@ export class FormattedTextBoxComment {
whenActiveChanged={returnFalse}
bringToFront={returnFalse}
ContentScaling={returnOne}
- NativeWidth={target._nativeWidth ? (() => NumCast(target._nativeWidth)) : undefined}
- NativeHeight={target._natvieHeight ? (() => NumCast(target._nativeHeight)) : undefined}
+ NativeWidth={Doc.NativeWidth(target) ? (() => Doc.NativeWidth(target)) : undefined}
+ NativeHeight={Doc.NativeHeight(target) ? (() => Doc.NativeHeight(target)) : undefined}
/>
</div>
</div>;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 2700c508b..cf9b03308 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -31,6 +31,7 @@ const { toggleMark } = require("prosemirror-commands");
export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
static Instance: RichTextMenu;
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
+ private _linkToRef = React.createRef<HTMLInputElement>();
@observable public view?: EditorView;
public editorProps: FieldViewProps & FormattedTextBoxProps | undefined;
@@ -154,6 +155,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@action
public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any) {
+ if (this._linkToRef.current?.getBoundingClientRect().width) {
+ return;
+ }
this.view = view;
if (!view || !view.hasFocus()) {
return;
@@ -792,7 +796,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const self = this;
function onLinkChange(e: React.ChangeEvent<HTMLInputElement>) {
- self.TextView.endUndoTypingBatch();
+ self.TextView?.endUndoTypingBatch();
UndoManager.RunInBatch(() => self.setCurrentLink(e.target.value), "link change");
}
@@ -807,7 +811,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const dropdownContent =
<div className="dropdown link-menu">
<p>Linked to:</p>
- <input value={link} placeholder="Enter URL" onChange={onLinkChange} />
+ <input value={link} ref={this._linkToRef} placeholder="Enter URL" onChange={onLinkChange} />
<button className="make-button" onPointerDown={e => this.makeLinkToURL(link, "add:right")}>Apply hyperlink</button>
<div className="divider"></div>
<button className="remove-button" onPointerDown={e => this.deleteLink()}>Remove link</button>
diff --git a/src/client/views/nodes/formattedText/RichTextSchema.tsx b/src/client/views/nodes/formattedText/RichTextSchema.tsx
index 1767a04de..40c1d1cac 100644
--- a/src/client/views/nodes/formattedText/RichTextSchema.tsx
+++ b/src/client/views/nodes/formattedText/RichTextSchema.tsx
@@ -31,7 +31,7 @@ export class DashDocView {
}
//moved
- contentScaling = () => NumCast(this._dashDoc!._nativeWidth) > 0 ? this._dashDoc![WidthSym]() / NumCast(this._dashDoc!._nativeWidth) : 1;
+ contentScaling = () => Doc.NativeWidth(this._dashDoc) > 0 ? this._dashDoc![WidthSym]() / Doc.NativeWidth(this._dashDoc) : 1;
//moved
outerFocus = (target: Doc) => this._textBox.props.focus(this._textBox.props.Document); // ideally, this would scroll to show the focus target