aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts43
-rw-r--r--src/client/util/CurrentUserUtils.ts2
-rw-r--r--src/client/views/.DS_Storebin10244 -> 10244 bytes
-rw-r--r--src/client/views/Main.tsx7
-rw-r--r--src/client/views/MainView.tsx13
-rw-r--r--src/client/views/PropertiesView.tsx14
-rw-r--r--src/client/views/collections/TabDocView.tsx11
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx1
-rw-r--r--src/client/views/nodes/AudioBox.tsx51
-rw-r--r--src/client/views/nodes/PresBox.scss126
-rw-r--r--src/client/views/nodes/PresBox.tsx617
-rw-r--r--src/client/views/nodes/VideoBox.tsx51
-rw-r--r--src/client/views/presentationview/PresElementBox.scss39
-rw-r--r--src/client/views/presentationview/PresElementBox.tsx47
14 files changed, 775 insertions, 247 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index daacca51d..1a00c0bfb 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -549,6 +549,49 @@ export function simulateMouseClick(element: Element | null | undefined, x: numbe
}));
}
+export function lightOrDark(color: any) {
+
+ // Variables for red, green, blue values
+ var r, g, b, hsp;
+
+ // Check the format of the color, HEX or RGB?
+ if (color.match(/^rgb/)) {
+
+ // If RGB --> store the red, green, blue values in separate variables
+ color = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/);
+
+ r = color[1];
+ g = color[2];
+ b = color[3];
+ }
+ else {
+
+ // If hex --> Convert it to RGB: http://gist.github.com/983661
+ color = +("0x" + color.slice(1).replace(
+ color.length < 5 && /./g, '$&$&'));
+
+ r = color >> 16;
+ g = color >> 8 & 255;
+ b = color & 255;
+ }
+
+ // HSP (Highly Sensitive Poo) equation from http://alienryderflex.com/hsp.html
+ hsp = Math.sqrt(
+ 0.299 * (r * r) +
+ 0.587 * (g * g) +
+ 0.114 * (b * b)
+ );
+
+ // Using the HSP value, determine whether the color is light or dark
+ if (hsp > 127.5) {
+ return 'light';
+ }
+ else {
+
+ return 'dark';
+ }
+}
+
export function setupMoveUpEvents(
target: object,
e: React.PointerEvent,
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 99dfbaf1c..213bc3d88 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -63,7 +63,7 @@ export class CurrentUserUtils {
[this.ficon({
ignoreClick: true,
icon: "mobile",
- backgroundColor: "rgba(0,0,0,0)"
+ backgroundColor: "transparent"
}),
this.mobileTextContainer({},
[this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]);
diff --git a/src/client/views/.DS_Store b/src/client/views/.DS_Store
index c6f3afa14..60b336584 100644
--- a/src/client/views/.DS_Store
+++ b/src/client/views/.DS_Store
Binary files differ
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index c256d2ebb..34bfa8e77 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -24,5 +24,12 @@ AssignAllExtensions();
event.preventDefault();
}
}, true);
+ const startload = (document as any).startLoad;
+ const loading = Date.now() - (startload ? Number(startload) : (Date.now() - 3000));
+ console.log("Load Time = " + loading);
+ var d = new Date();
+ d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000));
+ var expires = "expires=" + d.toUTCString();
+ document.cookie = `loadtime=${loading};${expires};path=/`;
ReactDOM.render(<MainView />, document.getElementById('root'));
})(); \ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 032d4d3bb..747bde64d 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -86,6 +86,17 @@ export class MainView extends React.Component {
componentDidMount() {
document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
+ const ele = document.getElementById("loader");
+ const prog = document.getElementById("dash-progress");
+ if (ele && prog) {
+ // remove from DOM
+ setTimeout(() => {
+ clearTimeout();
+ prog.style.transition = "1s";
+ prog.style.width = "100%";
+ }, 0);
+ setTimeout(() => ele.outerHTML = '', 1000);
+ }
new InkStrokeProperties();
this._sidebarContent.proto = undefined;
DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_delayAutoHeight", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollY", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's
@@ -141,7 +152,7 @@ export class MainView extends React.Component {
fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
- fa.faFileAudio, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
+ fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote,
fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 245e612b3..3c77bc309 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -992,7 +992,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-title" style={{ width: this.props.width }}>
Presentation
</div>
- <div className="propertiesView-name">
+ <div className="propertiesView-name" style={{ borderBottom: 0 }}>
{this.editableTitle}
<div className="propertiesView-presSelected">
<div className="propertiesView-selectedCount">
@@ -1007,7 +1007,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title"
onPointerDown={action(() => { this.openPresTransitions = !this.openPresTransitions; })}
style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}>
- &nbsp; <FontAwesomeIcon icon={"rocket"} /> &nbsp; Transitions
+ &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={"rocket"} /> &nbsp; Transitions
<div className="propertiesView-presTrails-title-icon">
<FontAwesomeIcon icon={this.openPresTransitions ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
@@ -1020,7 +1020,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
<div className="propertiesView-presTrails-title"
onPointerDown={action(() => this.openPresProgressivize = !this.openPresProgressivize)}
style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>
- &nbsp; <FontAwesomeIcon icon={"tasks"} /> &nbsp; Progressivize
+ &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={"tasks"} /> &nbsp; Progressivize
<div className="propertiesView-presTrails-title-icon">
<FontAwesomeIcon icon={this.openPresProgressivize ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
@@ -1029,19 +1029,19 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
{PresBox.Instance.progressivizeDropdown}
</div> : null}
</div>} */}
- {/* {!selectedItem || (!scrollable && !pannable) ? (null) : <div className="propertiesView-presTrails">
+ {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? (null) : <div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
onPointerDown={action(() => { this.openSlideOptions = !this.openSlideOptions; })}
style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}>
- &nbsp; <FontAwesomeIcon icon={"cog"} /> &nbsp; {scrollable ? "Scroll options" : "Pan options"}
+ &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={type === DocumentType.AUDIO ? "file-audio" : "file-video"} /> &nbsp; {type === DocumentType.AUDIO ? "Audio Options" : "Video Options"}
<div className="propertiesView-presTrails-title-icon">
<FontAwesomeIcon icon={this.openSlideOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
</div>
</div>
{this.openSlideOptions ? <div className="propertiesView-presTrails-content">
- {PresBox.Instance.optionsDropdown}
+ {PresBox.Instance.mediaOptionsDropdown}
</div> : null}
- </div>} */}
+ </div>}
{/* <div className="propertiesView-presTrails">
<div className="propertiesView-presTrails-title"
onPointerDown={action(() => { this.openAddSlide = !this.openAddSlide; })}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 82b6d8605..b7e81d7dc 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -166,14 +166,17 @@ export class TabDocView extends React.Component<TabDocViewProps> {
pinDoc.presentationTargetDoc = doc;
pinDoc.title = doc.title + " - Slide";
pinDoc.presMovement = PresMovement.Zoom;
+ pinDoc.groupWithUp = false;
pinDoc.context = curPres;
const presArray: Doc[] = PresBox.Instance?.sortArray();
const size: number = PresBox.Instance?._selectedArray.size;
const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
- if (pinDoc.type === "audio" && !audioRange) {
+ if (!audioRange && (pinDoc.type === DocumentType.AUDIO || pinDoc.type === DocumentType.VID)) {
+ pinDoc.mediaStart = "manual";
+ pinDoc.mediaStop = "manual";
pinDoc.presStartTime = 0;
- pinDoc.presEndTime = doc.duration;
+ pinDoc.presEndTime = pinDoc.type === DocumentType.AUDIO ? doc.duration : NumCast(doc["data-duration"]);
}
if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
const dview = CollectionDockingView.Instance.props.Document;
@@ -442,8 +445,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
let docColor = StrCast(doc?._backgroundColor, StrCast(doc?.backgroundColor));
if (!docColor) {
switch (doc?.type) {
- case DocumentType.PRESELEMENT: docColor = TabDocView.darkScheme ? "dimgrey" : ""; break;
- case DocumentType.PRES: docColor = TabDocView.darkScheme ? "#3e3e3e" : "black"; break;
+ case DocumentType.PRESELEMENT: docColor = TabDocView.darkScheme ? "" : ""; break;
+ case DocumentType.PRES: docColor = TabDocView.darkScheme ? "#3e3e3e" : "white"; break;
case DocumentType.FONTICON: docColor = "black"; break;
case DocumentType.RTF: docColor = TabDocView.darkScheme ? "#2d2d2d" : "#f1efeb";
case DocumentType.LABEL:
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 0eb05500c..c4d51f986 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -404,6 +404,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const pinDoc = Doc.MakeAlias(doc);
pinDoc.presentationTargetDoc = doc;
pinDoc.presMovement = PresMovement.Zoom;
+ pinDoc.groupWithUp = false;
pinDoc.context = curPres;
pinDoc.title = doc.title + " - Slide";
const presArray: Doc[] = PresBox.Instance?.sortArray();
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 6323a6100..8570b35af 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -27,6 +27,8 @@ import Waveform from "react-audio-waveform";
import axios from "axios";
import { SnappingManager } from "../../util/SnappingManager";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { LinkDocPreview } from "./LinkDocPreview";
+import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
declare class MediaRecorder {
// whatever MediaRecorder has
@@ -46,9 +48,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
static RangeScript: ScriptField;
static LabelScript: ScriptField;
- _linkPlayDisposer: IReactionDisposer | undefined;
- _reactionDisposer: IReactionDisposer | undefined;
- _scrubbingDisposer: IReactionDisposer | undefined;
+ // _linkPlayDisposer: IReactionDisposer | undefined;
+ // _reactionDisposer: IReactionDisposer | undefined;
+ // _scrubbingDisposer: IReactionDisposer | undefined;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
_ele: HTMLAudioElement | null = null;
_recorder: any;
_recordStart = 0;
@@ -106,9 +109,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
}
componentWillUnmount() {
- this._reactionDisposer?.();
- this._linkPlayDisposer?.();
- this._scrubbingDisposer?.();
+ this._disposers.reaction?.();
+ this._disposers.linkPlay?.();
+ this._disposers.scrubbing?.();
+ this._disposers.audioStart?.();
}
@action
@@ -118,7 +122,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
}
this.audioState = this.path ? "paused" : undefined;
- this._linkPlayDisposer = reaction(() => this.layoutDoc.scrollToLinkID,
+ this._disposers.linkPlay = reaction(() => this.layoutDoc.scrollToLinkID,
scrollLinkId => {
if (scrollLinkId) {
DocListCast(this.dataDoc.links).filter(l => l[Id] === scrollLinkId).map(l => {
@@ -130,7 +134,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
}, { fireImmediately: true });
// for play when link is selected
- this._reactionDisposer = reaction(() => SelectionManager.SelectedDocuments(),
+ this._disposers.reaction = reaction(() => SelectionManager.SelectedDocuments(),
selected => {
const sel = selected.length ? selected[0].props.Document : undefined;
let link;
@@ -145,7 +149,36 @@ export class AudioBox extends ViewBoxAnnotatableComponent<FieldViewProps, AudioD
this.layoutDoc.playOnSelect && this.recordingStart && !sel && this.pause();
}
});
- this._scrubbingDisposer = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime));
+ this._disposers.scrubbing = reaction(() => AudioBox._scrubTime, (time) => this.layoutDoc.playOnSelect && this.playFromTime(AudioBox._scrubTime));
+
+ this._disposers.audioStart = reaction(
+ () => this.Document._audioStart,
+ (audioStart) => {
+ if (audioStart !== undefined) {
+ if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
+ const delay = this._audioRef.current ? 0 : 250; // wait for mainCont and try again to play
+ const startTime: number = NumCast(this.Document._audioStart);
+ setTimeout(() => this._audioRef.current && this.playFrom(startTime), delay);
+ setTimeout(() => { this.Document._currentTimecode = startTime; this.Document._audioStart = undefined; }, 10 + delay);
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
+
+ this._disposers.audioStop = reaction(
+ () => this.Document._audioStop,
+ (audioStop) => {
+ if (audioStop !== undefined) {
+ if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
+ const delay = this._audioRef.current ? 0 : 250; // wait for mainCont and try again to play
+ setTimeout(() => this._audioRef.current && this.pause(), delay);
+ setTimeout(() => { this.Document._audioStop = undefined; }, 10 + delay);
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
}
playLink = (doc: Doc) => {
diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/PresBox.scss
index de2aee8fa..1ba86232b 100644
--- a/src/client/views/nodes/PresBox.scss
+++ b/src/client/views/nodes/PresBox.scss
@@ -1,6 +1,7 @@
$light-blue: #AEDDF8;
$dark-blue: #5B9FDD;
$light-background: #ececec;
+$dark-grey: #656565;
.presBox-cont {
cursor: auto;
@@ -127,6 +128,32 @@ $light-background: #ececec;
opacity: 0.8;
}
+.presBox-radioButtons {
+ font-size: 10px;
+ font-weight: 200;
+ // background-color: rgba(0, 0, 0, 0.1);
+
+ .checkbox-container {
+ margin-left: 10px;
+ display: inline-flex;
+ width: 100%;
+ height: 20px;
+ align-items: center;
+ }
+
+ .checkbox-dropdown {
+ display: flex;
+ width: 100%;
+ align-items: flex-end;
+ gap: 5px;
+
+ .presBox-viewPicker {
+ width: calc(100% - 120px);
+ min-width: 30px;
+ }
+ }
+}
+
.presBox-ribbon {
position: relative;
display: inline;
@@ -209,6 +236,42 @@ $light-background: #ececec;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
+
+ .multiThumb-slider {
+ display: grid;
+ background-color: $light-background;
+ height: 10px;
+ border-radius: 10px;
+ overflow: hidden;
+
+ .toolbar-slider {
+ margin-top: 0px;
+ background: none;
+ -webkit-appearance: none;
+ pointer-events: none;
+ }
+
+ .toolbar-slider.start::-webkit-slider-thumb {
+ width: 10px;
+ pointer-events: auto;
+ -webkit-appearance: none;
+ height: 10px;
+ cursor: ew-resize;
+ background: $dark-blue;
+ box-shadow: -100vw 0 0 100vw $light-background;
+ }
+
+ .toolbar-slider.end::-webkit-slider-thumb {
+ width: 10px;
+ pointer-events: auto;
+ -webkit-appearance: none;
+ height: 10px;
+ cursor: ew-resize;
+ background: $dark-blue;
+ box-shadow: -100vw 0 0 100vw $light-blue;
+ }
+ }
+
.toolbar-slider {
margin-top: 5px;
position: relative;
@@ -219,7 +282,7 @@ $light-background: #ececec;
height: 10px;
border-radius: 10px;
-webkit-appearance: none;
- background-color: #ececec;
+ background-color: $light-background;
}
.toolbar-slider:focus {
@@ -234,14 +297,45 @@ $light-background: #ececec;
.toolbar-slider::-webkit-slider-thumb {
width: 10px;
+ pointer-events: auto;
-webkit-appearance: none;
height: 10px;
cursor: ew-resize;
- background: #5b9ddd;
- box-shadow: -100vw 0 0 100vw #aedef8;
+ background: $dark-blue;
+ box-shadow: -100vw 0 0 100vw $light-blue;
+ }
+
+ .presBox-checkbox {
+ -webkit-appearance: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ margin: 0;
+ margin-right: 3px;
+ border-radius: 100%;
+ height: 15px;
+ width: 15px;
+ min-width: 15px;
+ cursor: pointer;
+ background: $light-background;
+ }
+
+ .presBox-checkbox:focus {
+ outline: none;
+ }
+
+ .presBox-checkbox:hover {
+ background: #c0c0c0;
+ }
+
+ .presBox-checkbox:checked {
+ background: $light-blue;
}
}
+
+
.slider-headers {
position: relative;
display: grid;
@@ -253,6 +347,20 @@ $light-background: #ececec;
font-weight: 100;
margin-top: 3px;
font-size: 10px;
+
+ .slider-block {
+ width: 63px;
+ border-radius: 5px;
+ text-align: center;
+ margin-bottom: 8px;
+ margin-top: 8px;
+ }
+
+ .slider-number {
+ border-radius: 3px;
+ width: 30px;
+ margin: auto;
+ }
}
.slider-value {
@@ -420,20 +528,20 @@ $light-background: #ececec;
background-color: #ececec;
border: 1px solid #9f9f9f;
grid-template-rows: max-content;
-
+
.frameList-header {
display: grid;
width: 100%;
height: 20px;
background-color: #9f9f9f;
-
+
.frameList-headerButtons {
display: flex;
grid-column: 7;
width: 60px;
justify-self: right;
justify-content: flex-end;
-
+
.headerButton {
cursor: pointer;
position: relative;
@@ -452,7 +560,7 @@ $light-background: #ececec;
transition: 0.2s;
margin-right: 3px;
}
-
+
.headerButton:hover {
background-color: rgba(0, 0, 0, 1);
transform: scale(1.15);
@@ -552,7 +660,7 @@ $light-background: #ececec;
position: relative;
font-size: 13;
padding-bottom: 10px;
- border-bottom: solid 1px darkgrey;
+ border-bottom: solid 1px $dark-grey;
.presBox-dropdown:hover {
border: solid 1px $dark-blue;
@@ -1061,7 +1169,7 @@ $light-background: #ececec;
background-color: #5a5a5a;
}
-
+
}
// .miniPres {
diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx
index 455ee87e1..a4f79571e 100644
--- a/src/client/views/nodes/PresBox.tsx
+++ b/src/client/views/nodes/PresBox.tsx
@@ -24,11 +24,11 @@ import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionView, CollectionViewType } from "../collections/CollectionView";
import { TabDocView } from "../collections/TabDocView";
import { ViewBoxBaseComponent } from "../DocComponent";
-import { AudioBox } from "./AudioBox";
import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
import { FieldView, FieldViewProps } from './FieldView';
import "./PresBox.scss";
import Color = require("color");
+import { clear } from "console";
export enum PresMovement {
Zoom = "zoom",
@@ -57,7 +57,7 @@ enum PresStatus {
Edit = "edit"
}
-enum PresColor {
+export enum PresColor {
LightBlue = "#AEDDF8",
DarkBlue = "#5B9FDD",
LightBackground = "#ececec",
@@ -117,7 +117,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
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", type: DocumentType.PRESELEMENT, backgroundColor: "transparent", _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
+ title: "pres element template", type: DocumentType.PRESELEMENT, _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
@@ -189,25 +189,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
else targetDoc.editing = true;
}
+ _mediaTimer!: [NodeJS.Timeout, Doc];
// '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;
+ startTempMedia = (targetDoc: Doc, activeItem: Doc) => {
+ const duration: number = NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime);
+ if (targetDoc.type === DocumentType.AUDIO) {
+ if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
+ targetDoc._audioStart = NumCast(activeItem.presStartTime);
+ this._mediaTimer = [setTimeout(() => targetDoc._audioStop = true, duration * 1000), targetDoc];
+ } else if (targetDoc.type === DocumentType.VID) {
+ if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
+ targetDoc._videoStop = true;
+ setTimeout(() => targetDoc._currentTimecode = NumCast(activeItem.presStartTime), 10);
+ setTimeout(() => targetDoc._videoStart = true, 20);
+ this._mediaTimer = [setTimeout(() => targetDoc._videoStop = true, (duration * 1000) + 20), targetDoc];
+ }
}
- // 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);
+ stopTempMedia = (targetDoc: Doc) => {
+ if (targetDoc.type === DocumentType.AUDIO) {
+ if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
+ targetDoc._audioStop = true;
+ } else if (targetDoc.type === DocumentType.VID) {
+ if (this._mediaTimer && this._mediaTimer[1] === targetDoc) clearTimeout(this._mediaTimer[0]);
+ targetDoc._videoStop = true;
+ }
+ }
- // 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'); }
+ // No more frames in current doc and next slide is defined, therefore move to next slide
+ nextSlide = (activeNext: Doc) => {
+ const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
+ let nextSelected = this.itemIndex + 1;
+ this.gotoDocument(nextSelected, this.activeItem);
+ for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) {
+ if (!this.childDocs[nextSelected].groupWithUp) {
+ break;
+ } else {
+ console.log("Title: " + this.childDocs[nextSelected].title);
+ this.gotoDocument(nextSelected, this.activeItem, true);
+ }
+ }
}
// Called when the user activates 'next' - to move to the next part of the pres. trail
@@ -224,16 +245,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
// 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) {
- // 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) {
- // Case 4: Last slide and presLoop is toggled ON
- this.gotoDocument(0);
+ // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide
+ this.nextSlide(activeNext);
+ } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
+ // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode
+ this.gotoDocument(0, this.activeItem);
}
- // 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 activates 'back' - to move to the previous part of the pres. trail
@@ -246,41 +263,59 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
const lastFrame = Cast(targetDoc.lastFrame, "number", null);
const curFrame = NumCast(targetDoc._currentFrame);
let prevSelected = this.itemIndex;
+ // Functionality for group with up
+ let didZoom = activeItem.presMovement;
+ for (; !didZoom && prevSelected > 0 && this.childDocs[prevSelected].groupButton; prevSelected--) {
+ didZoom = this.childDocs[prevSelected].presMovement;
+ }
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 && 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.gotoDocument(prevSelected, activeItem);
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);
+ this.gotoDocument(this.childDocs.length - 1, activeItem);
}
}
//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) => {
+ public gotoDocument = action((index: number, from?: Doc, group?: boolean) => {
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 (presTargetDoc) {
- presTargetDoc && runInAction(() => {
- if (activeItem.presMovement === PresMovement.Jump) presTargetDoc.focusSpeed = 0;
- else presTargetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+ const targetDoc: Doc = this.targetDoc;
+ if (from && from.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
+ const mediaDocList = DocListCast(from.mediaStopTriggerList);
+ mediaDocList.forEach((doc) => {
+ this.stopTempMedia(Cast(doc.presentationTargetDoc, Doc, null));
});
- setTimeout(() => presTargetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
}
- if (presTargetDoc?.lastFrame !== undefined) {
- presTargetDoc._currentFrame = 0;
+ if (from && from.mediaStop === "auto" && this.layoutDoc.presStatus !== PresStatus.Edit) {
+ this.stopTempMedia(Cast(from.presentationTargetDoc, Doc, null));
}
- this._selectedArray.clear();
+ // If next slide is audio / video 'Play automatically' then the next slide should be played
+ if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && (activeItem.mediaStart === "auto")) {
+ this.startTempMedia(targetDoc, activeItem);
+ }
+ if (targetDoc) {
+ targetDoc && runInAction(() => {
+ if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
+ else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+ });
+ setTimeout(() => targetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+ }
+ if (targetDoc?.lastFrame !== undefined) {
+ targetDoc._currentFrame = 0;
+ }
+ if (!group) 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
+ if (this.layoutDoc._viewType === "stacking" && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
this.onHideDocument(); //Handles hide after/before
}
});
@@ -482,7 +517,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
};
this.layoutDoc.presStatus = PresStatus.Autoplay;
this.startPresentation(startSlide);
- this.gotoDocument(startSlide);
+ this.gotoDocument(startSlide, activeItem);
load();
}
@@ -503,10 +538,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
try {
doc.opacity = 1;
} catch (e) {
- console.log("REset presentation error: ", 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) => {
@@ -592,6 +626,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (viewType === CollectionViewType.Stacking) this.layoutDoc._gridGap = 0;
});
+ /**
+ * Called when the user changes the view type
+ * Either 'List' (stacking) or 'Slides' (carousel)
+ */
+ // @undoBatch
+ mediaStopChanged = action((e: React.ChangeEvent) => {
+ const activeItem: Doc = this.activeItem;
+ //@ts-ignore
+ const stopDoc = e.target.selectedOptions[0].value as string;
+ const stopDocIndex: number = Number(stopDoc[0]);
+ activeItem.mediaStopDoc = stopDocIndex;
+ if (this.childDocs[stopDocIndex - 1].mediaStopTriggerList) {
+ const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList);
+ list.push(activeItem);
+ // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;
+ console.log(list);
+ } else {
+ this.childDocs[stopDocIndex - 1].mediaStopTriggerList = new List<Doc>();
+ const list = DocListCast(this.childDocs[stopDocIndex - 1].mediaStopTriggerList);
+ list.push(activeItem);
+ // this.childDocs[stopDocIndex - 1].mediaStopTriggerList = list;
+ console.log(list);
+ }
+ });
+
setMovementName = action((movement: any, activeItem: Doc): string => {
let output: string = 'none';
@@ -610,9 +669,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
addDocumentFilter = (doc: Doc | Doc[]) => {
const docs = doc instanceof Doc ? [doc] : doc;
docs.forEach((doc, i) => {
+ if (doc.presentationTargetDoc) return true;
if (doc.type === DocumentType.LABEL) {
const audio = Cast(doc.annotationOn, Doc, null);
if (audio) {
+ audio.mediaStart = "manual";
+ audio.mediaStop = "manual";
audio.presStartTime = NumCast(doc.audioStart);
audio.presEndTime = NumCast(doc.audioEnd);
audio.presDuration = NumCast(doc.audioEnd) - NumCast(doc.audioStart);
@@ -675,7 +737,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@action
selectElement = (doc: Doc) => {
const context = Cast(doc.context, Doc, null);
- this.gotoDocument(this.childDocs.indexOf(doc));
+ this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
else this.updateCurrentPresentation(context);
}
@@ -1058,6 +1120,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
@computed get transitionDropdown() {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
+ const type = targetDoc.type;
const isPresCollection: boolean = (targetDoc === this.layoutDoc.presCollection);
const isPinWithView: boolean = BoolCast(activeItem.presPinView);
if (activeItem && targetDoc) {
@@ -1087,7 +1150,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</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="presBox-subheading">Movement Speed</div>
<div className="ribbon-property">
<input className="presBox-input"
type="number" value={transitionSpeed}
@@ -1124,34 +1187,36 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
{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}
- onChange={action((e) => this.setDurationTime(e.target.value))} /> s
+ {(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <>
+ <div className="ribbon-doubleButton" >
+ <div className="presBox-subheading">Slide Duration</div>
+ <div className="ribbon-property">
+ <input className="presBox-input"
+ type="number" value={duration}
+ onChange={action((e) => this.setDurationTime(e.target.value))} /> s
</div>
- <div className="ribbon-propertyUpDown">
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
- <FontAwesomeIcon icon={"caret-up"} />
- </div>
- <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
- <FontAwesomeIcon icon={"caret-down"} />
+ <div className="ribbon-propertyUpDown">
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
+ <FontAwesomeIcon icon={"caret-up"} />
+ </div>
+ <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
+ <FontAwesomeIcon icon={"caret-down"} />
+ </div>
</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"
- 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>
+ <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>
{isPresCollection ? (null) : <div className="ribbon-box">
Effects
@@ -1222,129 +1287,297 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
}
});
}
- @computed get optionsDropdown() {
+
+ @computed get presPinViewOptionsDropdown() {
const activeItem: Doc = this.activeItem;
const targetDoc: Doc = this.targetDoc;
const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 16, filter: 'invert(1)' }} />;
+ return (
+ <>
+ {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;
+ activeItem.presPinViewX = x;
+ activeItem.presPinViewY = y;
+ activeItem.presPinViewScale = scale;
+ }
+ }}>Update</div></Tooltip> : (null)}
+ </div>
+ </>
+ );
+ }
+
+ @computed get panOptionsDropdown() {
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ return (
+ <>
+ {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 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presPinViewX)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
+ </div>
+ </div>
+ <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Pan Y</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presPinViewY)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
+ </div>
+ </div>
+ <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+ <div className="presBox-subheading">Scale</div>
+ <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'left', width: 50 }}
+ type="number" value={NumCast(activeItem.presPinViewScale)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
+ </div>
+ </div>
+ </div> : (null)}
+ </>
+ );
+ }
+
+ @computed get scrollOptionsDropdown() {
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ return (
+ <>
+ {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)}
+ </>
+ );
+ }
+
+ @computed get mediaStopSlides() {
+ const activeItem: Doc = this.activeItem;
+ const list = this.childDocs.map((doc, i) => {
+ if (i > this.itemIndex) {
+ return (
+ <option>{i + 1}. {doc.title}</option>
+ );
+ }
+ });
+ return (
+ list
+ );
+ }
+
+ @computed get mediaOptionsDropdown() {
+ const activeItem: Doc = this.activeItem;
+ const targetDoc: Doc = this.targetDoc;
+ const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc);
+ const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + ". " + this.childDocs[mediaStopDocInd - 1].title : "";
if (activeItem && targetDoc) {
return (
<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.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 ? 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"
- style={{ textAlign: 'left', width: 50 }}
- type="number" value={NumCast(activeItem.presStartTime)}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })} />
- </div>
- </div> : (null)}
- {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"
- style={{ textAlign: 'left', width: 50 }}
- type="number" value={NumCast(activeItem.presEndTime)}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presEndTime = Number(val); })} />
+ <div>
+ <div className="ribbon-box">
+ Start {"&"} End Time
+ <div className={"slider-headers"}>
+ <div className="slider-block" >
+ <div className="slider-text" style={{ fontWeight: 500 }}>
+ Start time (s)
+ </div>
+ <div id={"startTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
+ type="number" value={NumCast(activeItem.presStartTime)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })}
+ />
+ </div>
+ </div>
+ <div className="slider-block">
+ <div className="slider-text" style={{ fontWeight: 500 }}>
+ Duration (s)
+ </div>
+ <div className="slider-number" style={{ backgroundColor: PresColor.LightBlue }}>
+ {Math.round((NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime)) * 10) / 10}
+ </div>
+ </div>
+ <div className="slider-block">
+ <div className="slider-text" style={{ fontWeight: 500 }}>
+ End time (s)
+ </div>
+ <div id={"endTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}>
+ <input className="presBox-input"
+ style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
+ type="number" value={NumCast(activeItem.presEndTime)}
+ onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presEndTime = Number(e.target.value); })}
+ />
+ </div>
+ </div>
</div>
- </div> : (null)}
- {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;
+ <div className="multiThumb-slider">
+ <input type="range" step="0.1" min="0" max={activeItem.type === DocumentType.AUDIO ? Math.round(NumCast(activeItem.duration) * 10) / 10 : Math.round(NumCast(activeItem["data-duration"]) * 10) / 10} value={NumCast(activeItem.presEndTime)}
+ style={{ gridColumn: 1, gridRow: 1 }}
+ className={`toolbar-slider ${"end"}`}
+ id="toolbar-slider"
+ onPointerDown={() => {
+ this._batch = UndoManager.StartBatch("presEndTime");
+ const endBlock = document.getElementById("endTime");
+ if (endBlock) {
+ endBlock.style.color = PresColor.LightBackground;
+ endBlock.style.backgroundColor = PresColor.DarkBlue;
+ }
+ }}
+ onPointerUp={() => {
+ this._batch?.end();
+ const endBlock = document.getElementById("endTime");
+ if (endBlock) {
+ endBlock.style.color = "black";
+ endBlock.style.backgroundColor = PresColor.LightBackground;
+ }
+ }}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+ e.stopPropagation();
+ activeItem.presEndTime = Number(e.target.value);
+ }} />
+ <input type="range" step="0.1" min="0" max={activeItem.type === DocumentType.AUDIO ? Math.round(NumCast(activeItem.duration) * 10) / 10 : Math.round(NumCast(activeItem["data-duration"]) * 10) / 10} value={NumCast(activeItem.presStartTime)}
+ style={{ gridColumn: 1, gridRow: 1 }}
+ className={`toolbar-slider ${"start"}`}
+ id="toolbar-slider"
+ onPointerDown={() => {
+ this._batch = UndoManager.StartBatch("presStartTime");
+ const startBlock = document.getElementById("startTime");
+ if (startBlock) {
+ startBlock.style.color = PresColor.LightBackground;
+ startBlock.style.backgroundColor = PresColor.DarkBlue;
}
- }
- }}>{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;
- activeItem.presPinViewX = x;
- activeItem.presPinViewY = y;
- activeItem.presPinViewScale = scale;
- }
- }}>Update</div></Tooltip> : (null)}
+ }}
+ onPointerUp={() => {
+ this._batch?.end();
+ const startBlock = document.getElementById("startTime");
+ if (startBlock) {
+ startBlock.style.color = "black";
+ startBlock.style.backgroundColor = PresColor.LightBackground;
+ }
+ }}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+ e.stopPropagation();
+ activeItem.presStartTime = Number(e.target.value);
+ }} />
+ </div>
+ <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
+ <div className="slider-text">0 s</div>
+ <div className="slider-text"></div>
+ <div className="slider-text">{activeItem.type === DocumentType.AUDIO ? Math.round(NumCast(activeItem.duration) * 10) / 10 : Math.round(NumCast(activeItem["data-duration"]) * 10) / 10} s</div>
+ </div>
</div>
- {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 }}>
- <input className="presBox-input"
- style={{ textAlign: 'left', width: 50 }}
- type="number" value={NumCast(activeItem.presPinViewX)}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
+ <div className="ribbon-final-box">
+ Playback
+ <div className="presBox-subheading">Start playing:</div>
+ <div className="presBox-radioButtons">
+ <div className="checkbox-container">
+ <input className="presBox-checkbox"
+ type="checkbox"
+ onChange={() => activeItem.mediaStart = "manual"}
+ checked={activeItem.mediaStart === "manual"}
+ />
+ <div>On click</div>
</div>
- </div>
- <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
- <div className="presBox-subheading">Pan Y</div>
- <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
- <input className="presBox-input"
- style={{ textAlign: 'left', width: 50 }}
- type="number" value={NumCast(activeItem.presPinViewY)}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
+ <div className="checkbox-container">
+ <input className="presBox-checkbox"
+ type="checkbox"
+ onChange={() => activeItem.mediaStart = "auto"}
+ checked={activeItem.mediaStart === "auto"}
+ />
+ <div>Automatically</div>
</div>
</div>
- <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
- <div className="presBox-subheading">Scale</div>
- <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
- <input className="presBox-input"
- style={{ textAlign: 'left', width: 50 }}
- type="number" value={NumCast(activeItem.presPinViewScale)}
- onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
+ <div className="presBox-subheading">Stop playing:</div>
+ <div className="presBox-radioButtons">
+ <div className="checkbox-container">
+ <input className="presBox-checkbox"
+ type="checkbox"
+ onChange={() => activeItem.mediaStop = "manual"}
+ checked={activeItem.mediaStop === "manual"}
+ />
+ <div>At audio end time</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 className="checkbox-container">
+ <input className="presBox-checkbox"
+ type="checkbox"
+ onChange={() => activeItem.mediaStop = "auto"}
+ checked={activeItem.mediaStop === "auto"}
+ />
+ <div>On slide change</div>
+ </div>
+ {/* <div className="checkbox-container">
+ <input className="presBox-checkbox"
+ type="checkbox"
+ onChange={() => activeItem.mediaStop = "afterSlide"}
+ checked={activeItem.mediaStop === "afterSlide"}
+ />
+ <div className="checkbox-dropdown">
+ After chosen slide
+ <select className="presBox-viewPicker"
+ style={{ opacity: activeItem.mediaStop === "afterSlide" && this.itemIndex !== this.childDocs.length - 1 ? 1 : 0.3 }}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={this.mediaStopChanged}
+ value={mediaStopDocStr}>
+ {this.mediaStopSlides}
+ </select>
</div>
+ </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> */}
+ </div>
</div>
</div>
</div >
@@ -1448,7 +1681,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
if (data && presData) {
data.push(doc);
TabDocView.PinDoc(doc, false);
- this.gotoDocument(this.childDocs.length);
+ this.gotoDocument(this.childDocs.length, this.activeItem);
} else {
this.props.addDocTab(doc, "add:right");
}
@@ -1498,11 +1731,11 @@ 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={undoBatch(action(() => { this.updateMinimize(); this.turnOffEdit(true); }))}>
- Minimize
+ <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.updateMinimize(); this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }))}>
+ Mini-player
</div>
- <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); }))}>
- Sidebar view
+ <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }))}>
+ Sidebar player
</div>
</div>
);
@@ -1572,7 +1805,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
return (
<div>
<div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
- {/* <div className="ribbon-box">
+ <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 ? PresColor.LightBlue : "" }} onClick={this.progressivizeChild}>Contents</div>
@@ -1598,7 +1831,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<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>
<div className="ribbon-final-box">
Frames
<div className="ribbon-doubleButton">
@@ -1926,7 +2159,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<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, color: this._pathBoolean ? PresColor.DarkBlue : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 ? this.viewPaths : undefined}>
+ <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? PresColor.DarkBlue : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
<FontAwesomeIcon icon={"exchange-alt"} />
</div>
</Tooltip>
@@ -1977,7 +2210,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
</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={undoBatch(() => (this.childDocs.length) && (this.layoutDoc.presStatus = "manual"))}>
+ <div className="presBox-button-left"
+ onClick={undoBatch(() => {
+ if (this.childDocs.length) {
+ this.layoutDoc.presStatus = "manual";
+ this.gotoDocument(this.itemIndex, this.activeItem);
+ }
+ })}>
<FontAwesomeIcon icon={"play-circle"} />
<div style={{ display: this.props.PanelWidth() > 200 ? "inline-flex" : "none" }}>&nbsp; Present</div>
</div>
@@ -2085,10 +2324,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<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>
- <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>
+ <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, this.activeItem)}><b>1</b></div></Tooltip>
<div
className="presPanel-button-text"
- onClick={() => this.gotoDocument(0)}
+ onClick={() => this.gotoDocument(0, this.activeItem)}
style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
Slide {this.itemIndex + 1} / {this.childDocs.length}
{this.playButtonFrames}
@@ -2125,7 +2364,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
<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>
+ <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, this.activeItem)}><b>1</b></div></Tooltip>
<div className="presPanel-button-text">
Slide {this.itemIndex + 1} / {this.childDocs.length}
{this.playButtonFrames}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 8ce76a347..998ca839d 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -21,6 +21,8 @@ import { documentSchema } from "../../../fields/documentSchemas";
import { Networking } from "../../Network";
import { SnappingManager } from "../../util/SnappingManager";
import { SelectionManager } from "../../util/SelectionManager";
+import { LinkDocPreview } from "./LinkDocPreview";
+import { FormattedTextBoxComment } from "./formattedText/FormattedTextBoxComment";
const path = require('path');
export const timeSchema = createSchema({
@@ -32,8 +34,9 @@ const VideoDocument = makeInterface(documentSchema, timeSchema);
@observer
export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoDocument>(VideoDocument) {
static _youtubeIframeCounter: number = 0;
- private _reactionDisposer?: IReactionDisposer;
- private _youtubeReactionDisposer?: IReactionDisposer;
+ // private _reactionDisposer?: IReactionDisposer;
+ // private _youtubeReactionDisposer?: IReactionDisposer;
+ private _disposers: { [name: string]: IReactionDisposer } = {};
private _youtubePlayer: YT.Player | undefined = undefined;
private _videoRef: HTMLVideoElement | null = null;
private _youtubeIframeId: number = -1;
@@ -181,7 +184,32 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
componentDidMount() {
if (this.props.setVideoBox) this.props.setVideoBox(this);
-
+ this._disposers.videoStart = reaction(
+ () => this.Document._videoStart,
+ (videoStart) => {
+ if (videoStart !== undefined) {
+ if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
+ const delay = this.player ? 0 : 250; // wait for mainCont and try again to play
+ setTimeout(() => this.player && this.Play(), delay);
+ setTimeout(() => { this.Document._videoStart = undefined; }, 10 + delay);
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
+ this._disposers.videoStop = reaction(
+ () => this.Document._videoStop,
+ (videoStop) => {
+ if (videoStop !== undefined) {
+ if (this.props.renderDepth !== -1 && !LinkDocPreview.TargetDoc && !FormattedTextBoxComment.linkDoc) {
+ const delay = this.player ? 0 : 250; // wait for mainCont and try again to play
+ setTimeout(() => this.player && this.Pause(), delay);
+ setTimeout(() => { this.Document._videoStop = undefined; }, 10 + delay);
+ }
+ }
+ },
+ { fireImmediately: true }
+ );
if (this.youtubeVideoId) {
const youtubeaspect = 400 / 315;
const nativeWidth = Doc.NativeWidth(this.layoutDoc);
@@ -196,8 +224,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
componentWillUnmount() {
this.Pause();
- this._reactionDisposer?.();
- this._youtubeReactionDisposer?.();
+ this._disposers.reactionDisposer?.();
+ this._disposers.youtubeReactionDisposer?.();
+ this._disposers.videoStart?.();
}
@action
@@ -207,8 +236,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
this._videoRef!.ontimeupdate = this.updateTimecode;
// @ts-ignore
vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
- this._reactionDisposer?.();
- this._reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
+ this._disposers.reactionDisposer?.();
+ this._disposers.reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
}
}
@@ -293,10 +322,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps, VideoD
if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
});
const onYoutubePlayerReady = (event: any) => {
- this._reactionDisposer?.();
- this._youtubeReactionDisposer?.();
- this._reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0)));
- this._youtubeReactionDisposer = reaction(
+ this._disposers.reactionDisposer?.();
+ this._disposers.youtubeReactionDisposer?.();
+ this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0)));
+ this._disposers.youtubeReactionDisposer = reaction(
() => !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 });
};
diff --git a/src/client/views/presentationview/PresElementBox.scss b/src/client/views/presentationview/PresElementBox.scss
index 73a08b6de..1ad4b820e 100644
--- a/src/client/views/presentationview/PresElementBox.scss
+++ b/src/client/views/presentationview/PresElementBox.scss
@@ -126,14 +126,14 @@ $slide-active: #5B9FDD;
display: flex;
grid-column: 7;
grid-row: 1/3;
- width: 60px;
+ width: max-content;
justify-self: right;
justify-content: flex-end;
.slideButton {
cursor: pointer;
position: relative;
- border-radius: 100%;
+ border-radius: 100px;
z-index: 300;
width: 18px;
height: 18px;
@@ -156,7 +156,40 @@ $slide-active: #5B9FDD;
}
}
-
+// .presItem-slide:hover {
+// .presItem-slideButtons {
+// display: flex;
+// grid-column: 7;
+// grid-row: 1/3;
+// width: max-content;
+// justify-self: right;
+// justify-content: flex-end;
+
+// .slideButton {
+// cursor: pointer;
+// position: relative;
+// border-radius: 100px;
+// z-index: 300;
+// width: 18px;
+// height: 18px;
+// display: flex;
+// font-size: 12px;
+// justify-self: center;
+// align-self: center;
+// background-color: rgba(0, 0, 0, 0.5);
+// color: white;
+// justify-content: center;
+// align-items: center;
+// transition: 0.2s;
+// margin-right: 3px;
+// }
+
+// .slideButton:hover {
+// background-color: rgba(0, 0, 0, 1);
+// transform: scale(1.2);
+// }
+// }
+// }
.presItem-slide.active {
box-shadow: 0 0 0px 1.5px $dark-blue;
diff --git a/src/client/views/presentationview/PresElementBox.tsx b/src/client/views/presentationview/PresElementBox.tsx
index 899897a6d..80faf1a69 100644
--- a/src/client/views/presentationview/PresElementBox.tsx
+++ b/src/client/views/presentationview/PresElementBox.tsx
@@ -6,14 +6,14 @@ import { documentSchema } from '../../../fields/documentSchemas';
import { Id } from "../../../fields/FieldSymbols";
import { createSchema, makeInterface } from '../../../fields/Schema';
import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, setupMoveUpEvents } from "../../../Utils";
+import { emptyFunction, emptyPath, returnFalse, returnTrue, returnOne, setupMoveUpEvents, lightOrDark } from "../../../Utils";
import { Transform } from "../../util/Transform";
import { ViewBoxBaseComponent } from '../DocComponent';
import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import "./PresElementBox.scss";
import React = require("react");
-import { PresBox, PresMovement } from "../nodes/PresBox";
+import { PresBox, PresColor, PresMovement } from "../nodes/PresBox";
import { DocumentType } from "../../documents/DocumentTypes";
import { Tooltip } from "@material-ui/core";
import { DragManager } from "../../util/DragManager";
@@ -75,7 +75,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
embedHeight = (): number => 97;
// embedWidth = () => this.props.PanelWidth();
// embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight);
- embedWidth = (): number => this.props.PanelWidth() - 30;
+ embedWidth = (): number => this.props.PanelWidth() - 35;
/**
* The function that is responsible for rendering a preview or not for this
* presentation element.
@@ -117,7 +117,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
@computed get duration() {
let durationInS: number;
- if (this.rootDoc.type === DocumentType.AUDIO) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; }
+ if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; }
else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
else durationInS = 2;
return "D: " + durationInS + "s";
@@ -284,12 +284,19 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
const toolbarWidth: number = this.toolbarWidth;
const showMore: boolean = this.toolbarWidth >= 300;
const miniView: boolean = this.toolbarWidth <= 110;
+ const presBox: Doc = this.presBox; //presBox
+ const presBoxColor: string = StrCast(presBox._backgroundColor);
+ const presColorBool: boolean = presBoxColor ? (presBoxColor !== "white" && presBoxColor !== "transparent") : false;
const targetDoc: Doc = this.targetDoc;
const activeItem: Doc = this.rootDoc;
return (
- <div className={`presItem-container`} key={this.props.Document[Id] + this.indexInPres}
+ <div className={`presItem-container`}
+ key={this.props.Document[Id] + this.indexInPres}
ref={this._itemRef}
- style={{ backgroundColor: isSelected ? "#AEDDF8" : "rgba(0,0,0,0)", opacity: this._dragging ? 0.3 : 1 }}
+ style={{
+ backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? "#AEDDF8" : "transparent",
+ opacity: this._dragging ? 0.3 : 1
+ }}
onClick={e => {
e.stopPropagation();
e.preventDefault();
@@ -305,15 +312,21 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
onPointerUp={this.headerUp}
>
{miniView ?
+ // when width is LESS than 110 px
<div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
{`${this.indexInPres + 1}.`}
</div>
:
+ // when width is MORE than 110 px
<div className="presItem-number">
{`${this.indexInPres + 1}.`}
</div>}
- {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`} style={{ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "backgroundColor", this.props.layerProvider) }}>
- <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 185) : toolbarWidth - 95, cursor: isSelected ? 'text' : 'grab' }}>
+ {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
+ style={{
+ backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props.renderDepth, "color", this.props.layerProvider),
+ boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
+ }}>
+ <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
<EditableView
ref={this._titleRef}
editing={!isSelected ? false : undefined}
@@ -331,13 +344,21 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
onClick={() => this.updateView(targetDoc, activeItem)}
style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
</Tooltip>
- {/* <Tooltip title={<><div className="dash-tooltip">{"Group with up"}</div></>}>
+ {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
<div className="slideButton"
onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
- style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>
- <FontAwesomeIcon icon={""} onPointerDown={e => e.stopPropagation()} />
+ style={{
+ zIndex: 1000 - this.indexInPres,
+ fontWeight: 700,
+ backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : PresColor.DarkBlue : undefined,
+ height: activeItem.groupWithUp ? 53 : 18,
+ transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
+ }}>
+ <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
+ <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
+ </div>
</div>
- </Tooltip> */}
+ </Tooltip>}
<Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
<FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
</div></Tooltip>
@@ -347,7 +368,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDoc
<FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
</div></Tooltip>
</div>
- <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 185) : toolbarWidth - 95 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div>
+ <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div>
{this.renderEmbeddedInline}
</div>}
</div >);