aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/trails/PresBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/trails/PresBox.tsx')
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx1132
1 files changed, 523 insertions, 609 deletions
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 7448fa898..9ab5fb1bd 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,12 +1,12 @@
+import { Button, Dropdown, DropdownType, IconButton, Size, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import Slider from '@mui/material/Slider';
-import { Button, Dropdown, DropdownType, IconButton, Toggle, ToggleType, Type } from 'browndash-components';
+import _ from 'lodash';
import { IReactionDisposer, ObservableSet, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { AiOutlineSend } from 'react-icons/ai';
-import { BiMicrophone } from 'react-icons/bi';
import { FaArrowDown, FaArrowLeft, FaArrowRight, FaArrowUp } from 'react-icons/fa';
import ReactLoading from 'react-loading';
import ReactTextareaAutosize from 'react-textarea-autosize';
@@ -14,7 +14,7 @@ import { StopEvent, lightOrDark, returnFalse, returnOne, setupMoveUpEvents } fro
import { emptyFunction, stringHash } from '../../../../Utils';
import { Doc, DocListCast, Field, FieldResult, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { Animation, DocData, TransitionTimer } from '../../../../fields/DocSymbols';
-import { Copy } from '../../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../../fields/FieldSymbols';
import { InkField } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { ObjectField } from '../../../../fields/ObjectField';
@@ -25,12 +25,12 @@ import { DocServer } from '../../../DocServer';
import { getSlideTransitionSuggestions, gptSlideProperties, gptTrailSlideCustomization } from '../../../apis/gpt/PresCustomization';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
-import { DictationManager } from '../../../util/DictationManager';
import { dropActionType } from '../../../util/DropActionTypes';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SerializationHelper } from '../../../util/SerializationHelper';
import { SnappingManager } from '../../../util/SnappingManager';
import { UndoManager, undoBatch, undoable } from '../../../util/UndoManager';
+import { DictationButton } from '../../DictationButton';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { pinDataTypes as dataTypes } from '../../PinFuncs';
import { CollectionView } from '../../collections/CollectionView';
@@ -40,7 +40,7 @@ import { CollectionFreeFormPannableContents } from '../../collections/collection
import { Colors } from '../../global/globalEnums';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
-import { FocusViewOptions } from '../FocusViewOptions';
+import { FocusEffectDelay, FocusViewOptions } from '../FocusViewOptions';
import { OpenWhere, OpenWhereMod } from '../OpenWhere';
import { ScriptingBox } from '../ScriptingBox';
import CubicBezierEditor, { EaseFuncToPoints, TIMING_DEFAULT_MAPPINGS } from './CubicBezierEditor';
@@ -60,6 +60,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
static navigateToDocScript: ScriptField;
+ public static PanelName = 'PRESBOX'; // name of dockingview tab where presentations get added
+
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
@@ -71,10 +73,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
private _disposers: { [name: string]: IReactionDisposer } = {};
public selectedArray = new ObservableSet<Doc>();
+ public slideToModify: Doc | null = null;
_batch: UndoManager.Batch | undefined = undefined; // undo batch for dragging sliders which generate multiple scene edit events as the cursor moves
_keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed. Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation.
_unmounting = false; // flag that view is unmounting used to block RemFromMap from deleting things
_presTimer: NodeJS.Timeout | undefined;
+ _animationDictation: DictationButton | null = null;
+ _slideDictation: DictationButton | null = null;
// eslint-disable-next-line no-use-before-define
@observable public static Instance: PresBox;
@@ -96,14 +101,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@observable _presKeyEvents: boolean = false;
@observable _forceKeyEvents: boolean = false;
+ @observable _showAIGallery = false;
+ @observable _showPreview = true;
+ @observable _easeDropdownVal = 'ease';
+
// GPT
- private _inputref: HTMLTextAreaElement | null = null;
- private _inputref2: HTMLTextAreaElement | null = null;
- @observable chatActive: boolean = false;
- @observable chatInput: string = '';
- public slideToModify: Doc | null = null;
- @observable isRecording: boolean = false;
- @observable isLoading: boolean = false;
+ @observable _chatActive: boolean = false;
+ @observable _animationChat: string = '';
+ @observable _chatInput: string = '';
+ @observable _isLoading: boolean = false;
@observable generatedAnimations: AnimationSettings[] = [
// default presets
@@ -137,54 +143,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
},
];
- @action
- setGeneratedAnimations = (settings: AnimationSettings[]) => {
- this.generatedAnimations = settings;
- };
-
- @observable animationChat: string = '';
-
- @action
- setChatInput = (input: string) => {
- this.chatInput = input;
- };
-
- @action
- setAnimationChat = (input: string) => {
- this.animationChat = input;
- };
-
- @action
- setIsLoading = (isLoading: boolean) => {
- this.isLoading = isLoading;
- };
-
- @action
- public setIsRecording = (isRecording: boolean) => {
- this.isRecording = isRecording;
- };
-
- @observable showBezierEditor = false;
- @action setBezierEditorVisibility = (visible: boolean) => {
- this.showBezierEditor = visible;
- };
- @observable showSpringEditor = true;
- @action setSpringEditorVisibility = (visible: boolean) => {
- this.showSpringEditor = visible;
- };
-
- // Easing function variables
-
- @observable easeDropdownVal = 'ease';
-
- @action setBezierControlPoints = (newPoints: { p1: number[]; p2: number[] }) => {
+ setGeneratedAnimations = action((input: AnimationSettings[]) => { this.generatedAnimations = input; }) // prettier-ignore
+ setChatInput = action((input: string) => { this._chatInput = input; }); // prettier-ignore
+ setAnimationChat = action((input: string) => { this._animationChat = input; }); // prettier-ignore
+ setIsLoading = action((input?: boolean) => { this._isLoading = !!input; }); // prettier-ignore
+ setShowAIGalleryVisibilty = action((visible: boolean) => { this._showAIGallery = visible; }); // prettier-ignore
+ setBezierControlPoints = action((newPoints: { p1: number[]; p2: number[] }) => {
this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`);
- };
+ });
+
+ @computed get showEaseFunctions() {
+ return ![PresMovement.None, PresMovement.Jump, ''].includes(StrCast(this.activeItem?.presentation_movement) as PresMovement);
+ }
@computed
get currCPoints() {
- const strPoints = this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease';
- return EaseFuncToPoints(strPoints);
+ return EaseFuncToPoints(this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease');
}
@computed
@@ -220,25 +194,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if ([DocumentType.PDF, DocumentType.WEB, DocumentType.RTF].includes(this.targetDoc.type as DocumentType) || this.targetDoc._type_collection === CollectionViewType.Stacking) return true;
return false;
}
- @computed get panable() {
- if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._type_collection === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
- return false;
- }
@computed get selectedDocumentView() {
if (DocumentView.Selected().length) return DocumentView.Selected()[0];
if (this.selectedArray.size) return DocumentView.getDocumentView(this.Document);
return undefined;
}
- @computed get isPres() {
- return this.selectedDoc === this.Document;
- }
- @computed get selectedDoc() {
- return this.selectedDocumentView?.Document;
- }
- isActiveItemTarget = (layoutDoc: Doc) => this.activeItem?.presentation_targetDoc === layoutDoc;
- clearSelectedArray = () => this.selectedArray.clear();
- addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
- removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));
componentWillUnmount() {
this._unmounting = true;
@@ -255,7 +215,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
() => this.pauseAutoPres()
);
this._disposers.keyboard = reaction(
- () => this.selectedDoc,
+ () => this.selectedDocumentView?.Document,
selected => {
document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
(this._presKeyEvents = selected === this.Document) && document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
@@ -292,13 +252,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
}
+ clearSelectedArray = () => this.selectedArray.clear();
+ addToSelectedArray = action((doc: Doc) => this.selectedArray.add(doc));
+ removeFromSelectedArray = action((doc: Doc) => this.selectedArray.delete(doc));
+
@action
updateCurrentPresentation = (pres?: Doc) => {
Doc.ActivePresentation = pres ?? this.Document;
PresBox.Instance = this;
};
- _mediaTimer!: [NodeJS.Timeout, Doc];
// 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
startTempMedia = (targetDoc: Doc, activeItem: Doc) => {
const duration: number = NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart);
@@ -316,84 +279,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
};
- // Recording for GPT customization
-
- recordDictation = () => {
- this.setIsRecording(true);
- this.setChatInput('');
- DictationManager.Controls.listen({
- interimHandler: this.setDictationContent,
- continuous: { indefinite: false },
- }).then(results => {
- if (results && [DictationManager.Controls.Infringed].includes(results)) {
- DictationManager.Controls.stop();
- }
- });
- };
- stopDictation = () => {
- this.setIsRecording(false);
- DictationManager.Controls.stop();
- };
-
- setDictationContent = (value: string) => {
- console.log('Dictation value', value);
- this.setChatInput(value);
- };
+ setDictationContent = (value: string) => this.setChatInput(value);
- @action
- customizeAnimations = async () => {
+ customizeAnimations = action(() => {
this.setIsLoading(true);
- try {
- const res = await getSlideTransitionSuggestions(this.animationChat);
- if (typeof res === 'string') {
- const resObj = JSON.parse(res);
- console.log('Parsed GPT Result ', resObj);
- this.setGeneratedAnimations(resObj as AnimationSettings[]);
- }
- } catch (err) {
- console.error(err);
- }
- this.setIsLoading(false);
- };
+ getSlideTransitionSuggestions(this._animationChat)
+ .then(res => this.setGeneratedAnimations(JSON.parse(res) as AnimationSettings[]))
+ .catch(err => console.error(err))
+ .finally(this.setIsLoading);
+ });
- @action
- customizeWithGPT = async (input: string) => {
+ customizeWithGPT = action((input: string) => {
// const testInput = 'change title to Customized Slide, transition for 2.3s with fade in effect';
- this.setIsRecording(false);
this.setIsLoading(true);
-
- const currSlideProperties: { [key: string]: FieldResult } = {};
- gptSlideProperties.forEach(key => {
- if (this.activeItem[key]) {
- currSlideProperties[key] = this.activeItem[key];
- }
- // default values
- else if (key === 'presentation_transition') {
- currSlideProperties[key] = 500;
- } else if (key === 'config_zoom') {
- currSlideProperties[key] = 1.0;
- }
- });
- console.log('current slide props ', currSlideProperties);
-
- try {
- const res = await gptTrailSlideCustomization(input, currSlideProperties);
- if (typeof res === 'string') {
- const resObj = JSON.parse(res);
- console.log('Parsed GPT Result ', resObj);
- // eslint-disable-next-line no-restricted-syntax
- for (const key in resObj) {
- if (resObj[key]) {
- console.log('typeof property', typeof resObj[key]);
- this.activeItem[key] = resObj[key];
- }
- }
- }
- } catch (err) {
- console.error(err);
- }
- this.setIsLoading(false);
- };
+ const slideDefaults: { [key: string]: FieldResult } = { presentation_transition: 500, config_zoom: 1 };
+ const currSlideProperties = gptSlideProperties.reduce(
+ (prev, key) => { prev[key] = Field.toString(this.activeItem[key]) ?? prev[key]; return prev; },
+ slideDefaults); // prettier-ignore
+
+ gptTrailSlideCustomization(input, JSON.stringify(currSlideProperties))
+ .then(res =>
+ (Object.entries(JSON.parse(res)) as string[][]).forEach(([key, val]) => {
+ this.activeItem[key] = (+val).toString() === val ? +val : (val ?? this.activeItem[key]);
+ })
+ )
+ .catch(e => console.error(e))
+ .finally(this.setIsLoading);
+ });
// TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time
// TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions
@@ -452,27 +364,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const progressiveReveal = (first: boolean) => {
const presIndexed = Cast(this.activeItem?.presentation_indexed, 'number', null);
if (presIndexed !== undefined) {
- const targetRenderedDoc = PresBox.targetRenderedDoc(this.activeItem);
- targetRenderedDoc._dataTransition = 'all 1s';
- targetRenderedDoc.opacity = 1;
- setTimeout(() => {
- targetRenderedDoc._dataTransition = 'inherit';
- }, 1000);
const listItems = this.progressivizedItems(this.activeItem);
- if (listItems && presIndexed < listItems.length) {
+ const listItemDoc = listItems?.[presIndexed];
+ if (listItems && listItemDoc) {
if (!first) {
- const listItemDoc = listItems[presIndexed];
- const targetView = listItems && DocumentView.getFirstDocumentView(listItemDoc);
+ const presBulletTiming = 500; // bcz: hardwired for now
Doc.linkFollowUnhighlight();
- Doc.HighlightDoc(listItemDoc);
+ Doc.linkFollowHighlight(listItemDoc);
listItemDoc.presentation_effect = this.activeItem.presBulletEffect;
- listItemDoc.presentation_transition = 500;
- targetView?.setAnimEffect(listItemDoc, 500);
- if (targetView && this.activeItem.presBulletExpand) {
- targetView.setAnimateScaling(1.2, 400);
- Doc.AddUnHighlightWatcher(() => targetView?.setAnimateScaling(0, undefined));
- }
+ listItemDoc.presentation_transition = presBulletTiming;
listItemDoc.opacity = undefined;
+
+ const targetView = DocumentView.getFirstDocumentView(listItemDoc);
+ if (targetView) {
+ targetView.setAnimEffect(listItemDoc, presBulletTiming);
+ if (this.activeItem.presBulletExpand) {
+ targetView.setAnimateScaling(1.2, presBulletTiming * 0.8);
+ Doc.AddUnHighlightWatcher(() => targetView.setAnimateScaling(0, undefined));
+ }
+ }
this.activeItem.presentation_indexed = presIndexed + 1;
}
return true;
@@ -547,8 +457,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (!group) this.clearSelectedArray();
this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); // Update selected array
this.turnOffEdit();
- this.navigateToActiveItem(finished); // Handles movement to element only when presentationTrail is list
- this.doHideBeforeAfter(); // Handles hide after/before
+ this.navigateToActiveItem((options: FocusViewOptions) => {
+ setTimeout(this.doHideBeforeAfter, FocusEffectDelay(options)); // Handles hide after/before
+ finished?.();
+ }); // Handles movement to element only when presentationTrail is list
}
});
static pinDataTypes(target?: Doc): dataTypes {
@@ -562,11 +474,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const datarange = [DocumentType.FUNCPLOT].includes(targetType);
const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG, DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined;
const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined;
- const typeCollection = targetType === DocumentType.COL;
+ const collectionType = targetType === DocumentType.COL;
const filters = true;
const pivot = true;
const dataannos = false;
- return { scrollable, pannable, inkable, type_collection: typeCollection, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
+ return { scrollable, pannable, inkable, collectionType, pivot, map, filters, temporal, clippable, dataview, datarange, poslayoutview, dataannos };
}
@action
@@ -574,7 +486,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/* empty */
};
@action
- // eslint-disable-next-line default-param-last
static restoreTargetDocView(bestTargetView: Opt<DocumentView>, activeItem: Doc, transTime: number, pinDocLayout: boolean = BoolCast(activeItem.config_pinLayout), pinDataTypes?: dataTypes, targetDoc?: Doc) {
const bestTarget = bestTargetView?.Document ?? (targetDoc?.layout_unrendered ? DocCast(targetDoc?.annotationOn) : targetDoc);
if (!bestTarget) return undefined;
@@ -700,15 +611,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
changed = true;
}
}
- if ((pinDataTypes?.type_collection && activeItem.config_viewType !== undefined) || (!pinDataTypes && activeItem.config_viewType !== undefined)) {
- if (bestTarget._type_collection !== activeItem.config_viewType) {
- bestTarget._type_collection = activeItem.config_viewType;
+ if ((pinDataTypes?.collectionType && activeItem.config_card_curDoc !== undefined) || (!pinDataTypes && activeItem.config_card_curDoc !== undefined)) {
+ if (bestTarget._card_curDoc !== activeItem.config_card_curDoc) {
+ bestTarget._card_curDoc = activeItem.config_card_curDoc;
+ changed = true;
+ }
+ }
+ if ((pinDataTypes?.collectionType && activeItem.config_carousel_index !== undefined) || (!pinDataTypes && activeItem.config_carousel_index !== undefined)) {
+ if (bestTarget._carousel_index !== activeItem.config_carousel_index) {
+ bestTarget._carousel_index = activeItem.config_carousel_index;
+ changed = true;
+ }
+ }
+ if ((pinDataTypes?.collectionType && activeItem.config_type_collection !== undefined) || (!pinDataTypes && activeItem.config_type_collection !== undefined)) {
+ if (bestTarget._type_collection !== activeItem.config_type_collection) {
+ bestTarget._type_collection = activeItem.config_type_collection;
changed = true;
}
}
if ((pinDataTypes?.filters && activeItem.config_docFilters !== undefined) || (!pinDataTypes && activeItem.config_docFilters !== undefined)) {
- if (bestTarget.childFilters !== activeItem.config_docFilters) {
+ if (!_.isEqual(Array.from(StrListCast(bestTarget.childFilters)), Array.from(StrListCast(activeItem.config_docFilters)))) {
bestTarget.childFilters = ObjectField.MakeCopy(activeItem.config_docFilters as ObjectField) || new List<string>([]);
changed = true;
}
@@ -773,11 +696,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
});
setTimeout(
() =>
- Array.from(transitioned).forEach(
- action(doc => {
- doc._dataTransition = undefined;
- })
- ),
+ Array.from(transitioned).forEach(doc => {
+ doc._dataTransition = undefined;
+ }),
transTime + 10
);
}
@@ -815,16 +736,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
* a new tab. If presCollection is undefined it will open the document
* on the right.
*/
- navigateToActiveItem = (afterNav?: () => void) => {
+ navigateToActiveItem = (afterNav?: (options: FocusViewOptions) => void) => {
const { activeItem, targetDoc } = this;
- const finished = () => {
- afterNav?.();
+ const finished = (options: FocusViewOptions) => {
+ afterNav?.(options);
targetDoc[Animation] = undefined;
};
const selViewCache = Array.from(this.selectedArray);
const dragViewCache = Array.from(this._dragArray);
const eleViewCache = Array.from(this._eleArray);
- const resetSelection = action(() => {
+ const resetSelection = action((options: FocusViewOptions) => {
if (!this._props.isSelected()) {
const presDocView = DocumentView.getDocumentView(this.Document);
if (presDocView) DocumentView.SelectView(presDocView, false);
@@ -833,14 +754,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._dragArray.splice(0, this._dragArray.length, ...dragViewCache);
this._eleArray.splice(0, this._eleArray.length, ...eleViewCache);
}
- finished();
+ finished(options);
});
PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection);
};
- public static PanelName = 'PRESBOX';
-
- static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) {
+ static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: (options: FocusViewOptions) => void) {
if (activeItem.presentation_movement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) {
(DocumentView.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.();
return;
@@ -875,9 +794,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (!DocumentView.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) {
DocumentView.SetLightboxDoc(undefined);
}
- DocumentView.showDocument(targetDoc, options, finished);
+ DocumentView.showDocument(targetDoc, options, () => finished?.(options));
});
- } else finished?.();
+ } else finished?.(options);
}
/**
@@ -889,8 +808,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.childDocs.forEach((doc, index) => {
const curDoc = Cast(doc, Doc, null);
const tagDoc = PresBox.targetRenderedDoc(curDoc);
- const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, curDoc);
- let opacity: Opt<number> = index === this.itemIndex ? 1 : undefined;
+ const itemIndexes = this.getAllIndexes(this.tagDocs, curDoc);
+ let opacity = index === this.itemIndex ? 1 : undefined;
if (curDoc.presentation_hide) {
if (index !== this.itemIndex) {
opacity = 1;
@@ -902,9 +821,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
opacity = 0;
} else if (index === this.itemIndex || !curDoc.presentation_hideAfter) {
opacity = 1;
- setTimeout(() => {
- tagDoc._dataTransition = undefined;
- }, 1000);
}
}
const hidingIndAft =
@@ -1134,7 +1050,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return false;
}
} else if (doc.type !== DocumentType.PRES) {
- // eslint-disable-next-line operator-assignment
if (!doc.presentation_targetDoc) doc.title = doc.title + ' - Slide';
doc.presentation_targetDoc = doc.createdFrom ?? doc; // dropped document will be a new embedding of an embedded document somewhere else.
doc.presentation_movement = PresMovement.Zoom;
@@ -1166,8 +1081,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const tagDoc = Cast(curDoc.presentation_targetDoc, Doc, null);
if (curDoc && curDoc === this.activeItem)
return (
- // eslint-disable-next-line react/no-array-index-key
- <div key={index} className="selectedList-items">
+ <div key={doc[Id]} className="selectedList-items">
<b>
{index + 1}. {StrCast(curDoc.title)})
</b>
@@ -1175,15 +1089,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
);
if (tagDoc)
return (
- // eslint-disable-next-line react/no-array-index-key
- <div key={index} className="selectedList-items">
+ <div key={doc[Id]} className="selectedList-items">
{index + 1}. {StrCast(curDoc.title)}
</div>
);
if (curDoc)
return (
- // eslint-disable-next-line react/no-array-index-key
- <div key={index} className="selectedList-items">
+ <div key={doc[Id]} className="selectedList-items">
{index + 1}. {StrCast(curDoc.title)}
</div>
);
@@ -1368,7 +1280,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
const tagDoc = PresBox.targetRenderedDoc(doc);
const srcContext = Cast(tagDoc.embedContainer, Doc, null);
const labelCreator = (top: number, left: number, edge: number, fontSize: number) => (
- // eslint-disable-next-line react/no-array-index-key
<div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top, left, width: edge, height: edge, fontSize }} onClick={() => this.selectElement(doc)}>
<div className="pathOrder-frame">{index + 1}</div>
</div>
@@ -1619,7 +1530,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.updateEffect(this.activeItem.presentation_effect as PresEffect, false, true);
this.updateEffect(this.activeItem.presBulletEffect as PresEffect, true, true);
this.updateEffectDirection(this.activeItem.presentation_effectDirection as PresEffectDirection, true);
- // eslint-disable-next-line camelcase
const { presentation_transition: pt, presentation_duration: pd, presentation_hideBefore: ph, presentation_hideAfter: pa } = this.activeItem;
array.forEach(curDoc => {
curDoc.presentation_transition = pt;
@@ -1682,20 +1592,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</Tooltip>
</div>
{[DocumentType.AUDIO, DocumentType.VID].includes(targetType as DocumentType) ? null : (
- <>
- <div className="ribbon-doubleButton">
- <div className="presBox-subheading">Slide Duration</div>
- <div className="ribbon-property" style={{ border: `solid 1px ${SnappingManager.userColor}` }}>
- <input className="presBox-input" type="number" readOnly value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s
+ <div className="ribbon-doubleButton">
+ <Tooltip title={<div>How long to view the slide before transitioning to the next slide</div>}>
+ <div className="presBox-subheading">DURATION</div>
+ </Tooltip>
+ <div className="presBox-subheading-slider">
+ {PresBox.inputter('0.1', '0.1', '10', duration, targetType !== DocumentType.AUDIO, this.updateDurationTime)}
+ <div className="slider-headers">
+ <div className="slider-text">Short</div>
+ <div className="slider-text">Long</div>
</div>
</div>
- {PresBox.inputter('0.1', '0.1', '20', duration, targetType !== DocumentType.AUDIO, this.updateDurationTime)}
- <div className="slider-headers" style={{ display: targetType === DocumentType.AUDIO ? 'none' : 'grid' }}>
- <div className="slider-text">Short</div>
- <div className="slider-text">Medium</div>
- <div className="slider-text">Long</div>
+ <div className="ribbon-property" style={{ border: `solid 1px ${SnappingManager.userColor}`, display: 'flex', maxWidth: 60, width: '100%' }}>
+ <input className="presBox-inputNumber" type="number" value={duration} onChange={action(e => this.updateDurationTime(e.target.value))} />
+ <span>s</span>
</div>
- </>
+ </div>
)}
</div>
</div>
@@ -1703,28 +1615,62 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
return undefined;
}
- @computed get progressivizeDropdown() {
+
+ @computed get mediaDropdown() {
const { activeItem } = this;
if (activeItem && this.targetDoc) {
- const effect = activeItem.presBulletEffect ? activeItem.presBulletEffect : PresMovement.None;
- const bulletEffect = (presEffect: PresEffect) => (
- <div
- className={`presBox-dropdownOption ${activeItem.presentation_effect === presEffect || (presEffect === PresEffect.None && !activeItem.presentation_effect) ? 'active' : ''}`}
- onPointerDown={StopEvent}
- onClick={() => this.updateEffect(presEffect, true)}>
- {presEffect}
+ return (
+ <div className="presBox-option-block">
+ <div className="presBox-ribbon presbox-toggles">
+ <Tooltip title={<div className="dash-tooltip">Should first bullet be progressively disclosed or does it appear with slide.</div>}>
+ <div
+ className={`ribbon-toggle ${BoolCast(activeItem.presentation_playAudio) ? 'active' : ''}`}
+ style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
+ color: SnappingManager.userColor,
+ background: BoolCast(activeItem.presentation_playAudio) ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
+ }}
+ onClick={() => {
+ activeItem.presentation_playAudio = !BoolCast(activeItem.presentation_playAudio);
+ }}>
+ Play Audio Annotation
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">Should first bullet be progressively disclosed or does it appear with slide.</div>}>
+ <div
+ className={`ribbon-toggle ${BoolCast(activeItem.presentation_zoomText) ? 'active' : ''}`}
+ style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
+ color: SnappingManager.userColor,
+ background: BoolCast(activeItem.presentation_zoomText) ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
+ }}
+ onClick={() => {
+ activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText);
+ }}>
+ Zoom Text Selections
+ </div>
+ </Tooltip>
+ </div>
</div>
);
+ }
+ return null;
+ }
+ @computed get progressivizeDropdown() {
+ const { activeItem } = this;
+ if (activeItem && this.targetDoc) {
return (
<div className="presBox-option-block">
- <div className="presBox-ribbon">
- <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Progressivize Collection</div>
- <input
- className="presBox-checkbox"
- style={{ margin: 10, border: `solid 1px ${SnappingManager.userColor}` }}
- type="checkbox"
- onChange={() => {
+ <div className="presBox-toggles presBox-ribbon">
+ <Tooltip title={<div className="dash-tooltip">whether progressivization is active for this slide</div>}>
+ <div
+ className={`ribbon-toggle ${Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? 'active' : ''}`}
+ style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
+ color: SnappingManager.userColor,
+ background: Cast(activeItem.presentation_indexed, 'number', null) !== undefined ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
+ }}
+ onClick={() => {
activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
@@ -1737,62 +1683,51 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
+ }}>
+ Enable
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">Should first bullet be progressively disclosed or does it appear with slide.</div>}>
+ <div
+ className={`ribbon-toggle ${!NumCast(activeItem.presentation_indexedStart) ? 'active' : ''}`}
+ style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
+ color: SnappingManager.userColor,
+ background: !NumCast(activeItem.presentation_indexedStart) ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
}}
- checked={Cast(activeItem.presentation_indexed, 'number', null) !== undefined}
- />
- </div>
- <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Progressivize First Bullet</div>
- <input
- className="presBox-checkbox"
- style={{ margin: 10, border: `solid 1px ${SnappingManager.userColor}` }}
- type="checkbox"
- onChange={() => {
+ onClick={() => {
activeItem.presentation_indexedStart = activeItem.presentation_indexedStart ? 0 : 1;
- }}
- checked={!NumCast(activeItem.presentation_indexedStart)}
- />
- </div>
- <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Expand Current Bullet</div>
- <input
- className="presBox-checkbox"
- style={{ margin: 10, border: `solid 1px ${SnappingManager.userColor}` }}
- type="checkbox"
- onChange={() => {
- activeItem.presBulletExpand = !activeItem.presBulletExpand;
- }}
- checked={BoolCast(activeItem.presBulletExpand)}
- />
- </div>
-
- <div className="ribbon-box">
- Bullet Effect
+ }}>
+ All Bullets
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">Whether the active bullet expands when active.</div>}>
<div
- className="presBox-dropdown"
- onClick={action(e => {
- e.stopPropagation();
- this._openBulletEffectDropdown = !this._openBulletEffectDropdown;
- })}
+ className={`ribbon-toggle ${BoolCast(activeItem.presBulletExpand) ? 'active' : ''}`}
style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
color: SnappingManager.userColor,
- background: SnappingManager.userVariantColor,
- borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5,
- border: this._openBulletEffectDropdown ? `solid 2px ${SnappingManager.userVariantColor}` : `solid 1px ${SnappingManager.userColor}`,
+ background: BoolCast(activeItem.presBulletExpand) ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
+ }}
+ onClick={() => {
+ activeItem.presBulletExpand = !activeItem.presBulletExpand;
}}>
- {effect?.toString()}
- <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openBulletEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon="angle-down" />
- <div
- className="presBox-dropdownOptions"
- style={{ display: this._openBulletEffectDropdown ? 'grid' : 'none', color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}
- onPointerDown={e => e.stopPropagation()}>
- {Object.values(PresEffect)
- .filter(v => isNaN(Number(v)))
- .map(pEffect => bulletEffect(pEffect))}
- </div>
+ Expand Active
</div>
- </div>
+ </Tooltip>
</div>
+ <Dropdown
+ color={SnappingManager.userColor}
+ formLabel="Effect"
+ toolTip="Animation effect to use when bullet activates"
+ formLabelPlacement="left"
+ closeOnSelect
+ items={Object.values(PresEffect).map(v => ({ text: v.toString(), val: v }))}
+ selectedVal={StrCast(activeItem.presBulletEffect, PresMovement.None)}
+ setSelectedVal={val => this.updateEffect(val as PresEffect, true)}
+ dropdownType={DropdownType.SELECT}
+ type={Type.TERT}
+ />
</div>
);
}
@@ -1803,23 +1738,95 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return <div />;
}
+ /**
+ * This chatbox is for getting slide effect transition suggestions from gpt and visualizing them
+ */
+ @computed get aiEffects() {
+ return (
+ <div className="presBox-gpt-chat" style={{ display: SnappingManager.PropertiesWidth < 1 || !this._showAIGallery ? 'none' : undefined }}>
+ {/* Custom */}
+ <div className="pres-chat">
+ <div className="pres-chatbox-container-ai">
+ <ReactTextareaAutosize
+ placeholder="Use AI to suggest effects. Leave blank for random results."
+ className="pres-chatbox"
+ ref={r => {
+ setTimeout(() => {
+ if (r && !r.textContent) {
+ r.style.height = '';
+ r.style.height = r.scrollHeight + 'px';
+ }
+ });
+ }}
+ value={this._animationChat}
+ onChange={e => {
+ e.currentTarget.style.height = '';
+ e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px';
+ this.setAnimationChat(e.target.value);
+ }}
+ onKeyDown={e => {
+ this._animationDictation?.stopDictation();
+ e.stopPropagation();
+ }}
+ />
+ </div>
+ <Button
+ style={{ alignSelf: 'flex-end' }}
+ text="Send"
+ type={Type.TERT}
+ icon={this._isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />}
+ iconPlacement="right"
+ color={SnappingManager.userVariantColor}
+ onClick={this.customizeAnimations}
+ />
+ <DictationButton
+ ref={r => {
+ this._animationDictation = r;
+ }}
+ setInput={this.setAnimationChat}
+ />
+ </div>
+ <div style={{ alignItems: 'center' }}>
+ Click a box to use the effect.
+ {/* Preview Animations */}
+ <div className="presBox-effects">
+ {this.generatedAnimations.map((elem, i) => (
+ <div
+ key={i}
+ className="presBox-effect-container"
+ onClick={() => {
+ this.updateEffect(elem.effect, false);
+ this.updateEffectDirection(elem.direction);
+ this.updateEffectTiming(this.activeItem, {
+ type: SpringType.CUSTOM,
+ stiffness: elem.stiffness,
+ damping: elem.damping,
+ mass: elem.mass,
+ });
+ }}>
+ <SlideEffect dir={elem.direction} presEffect={elem.effect} springSettings={elem} infinite>
+ <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[i] }} />
+ </SlideEffect>
+ </div>
+ ))}
+ </div>
+ </div>
+ </div>
+ );
+ }
+
@computed get transitionDropdown() {
const { activeItem } = this;
// Retrieving spring timing properties
- const timing = StrCast(activeItem.presentation_effectTiming);
- let timingConfig: SpringSettings | undefined;
- if (timing) {
- timingConfig = JSON.parse(timing);
- }
-
- if (!timingConfig) {
- timingConfig = {
- type: SpringType.GENTLE,
- stiffness: 100,
- damping: 15,
- mass: 1,
- };
- }
+ const timing = StrCast(activeItem?.presentation_effectTiming);
+ const timingConfig: SpringSettings = timing
+ ? JSON.parse(timing)
+ : {
+ type: SpringType.GENTLE,
+ stiffness: 100,
+ damping: 15,
+ mass: 1,
+ };
if (activeItem && this.targetDoc) {
const transitionSpeed = activeItem.presentation_transition ? NumCast(activeItem.presentation_transition) / 1000 : 0.5;
@@ -1830,180 +1837,136 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return (
<>
{/* This chatbox is for customizing the properties of trails, like transition time, movement type (zoom, pan) using GPT */}
- <div className="presBox-gpt-chat">
- <span style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
+ <div className="presBox-gpt-chat" style={{ display: SnappingManager.PropertiesWidth < 1 ? 'none' : undefined }}>
+ <span className="presBox-gpt-chat-span">
Customize Slide Properties{' '}
<div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/trails/#slide-customization')}>
<IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SnappingManager.userColor} />
</div>
</span>
<div className="pres-chat">
- <div className="pres-chatbox-container">
+ <div className="pres-chatbox-container-ai">
<ReactTextareaAutosize
- placeholder="Describe how you would like to modify the slide properties."
+ placeholder="Describe how to modify the slide properties."
className="pres-chatbox"
- value={this.chatInput}
+ ref={r => {
+ setTimeout(() => {
+ if (r && !r.textContent) {
+ r.style.height = '';
+ r.style.height = r.scrollHeight + 'px';
+ }
+ });
+ }}
+ value={this._chatInput}
onChange={e => {
+ e.currentTarget.style.height = '';
+ e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px';
this.setChatInput(e.target.value);
}}
onKeyDown={e => {
- this.stopDictation();
+ this._slideDictation?.stopDictation();
e.stopPropagation();
}}
/>
- <IconButton
- type={Type.TERT}
- color={this.isRecording ? '#2bcaff' : SnappingManager.userVariantColor}
- tooltip="Record"
- icon={<BiMicrophone size="16px" />}
- onClick={() => {
- if (!this.isRecording) {
- this.recordDictation();
- } else {
- this.stopDictation();
- }
+ <DictationButton
+ ref={r => {
+ this._slideDictation = r;
}}
+ setInput={this.setChatInput}
/>
</div>
<Button
style={{ alignSelf: 'flex-end' }}
text="Send"
type={Type.TERT}
- icon={this.isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />}
+ icon={this._isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />}
iconPlacement="right"
color={SnappingManager.userVariantColor}
onClick={() => {
- this.stopDictation();
- this.customizeWithGPT(this.chatInput);
+ this._slideDictation?.stopDictation();
+ this.customizeWithGPT(this._chatInput);
}}
/>
</div>
</div>
+
{/* Movement */}
<div
className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`}
- onPointerDown={StopEvent}
- onPointerUp={StopEvent}
+ onPointerDown={e => e.stopPropagation()}
+ onPointerUp={e => e.stopPropagation()}
onClick={action(e => {
e.stopPropagation();
this._openMovementDropdown = false;
this._openEffectDropdown = false;
this._openBulletEffectDropdown = false;
})}>
- <div
- className="presBox-option-block"
- // style={{ padding: '16px' }}
- >
- Movement
+ <div className="presBox-option-block">
+ <div className="ribbon-doubleButton">
+ <Tooltip title={<div>How long the transition (view navigation and slide animation effect) lasts</div>}>
+ <div className="presBox-subheading">SPEED</div>
+ </Tooltip>
+ <div className="presBox-subheading-slider">
+ {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.updateTransitionTime)}
+ <div className="slider-headers">
+ <div className="slider-text">Fast</div>
+ <div className="slider-text">Slow</div>
+ </div>
+ </div>
+ <div className="ribbon-property" style={{ border: `solid 1px ${SnappingManager.userColor}`, display: 'flex', maxWidth: 60, width: '100%' }}>
+ <input className="presBox-inputNumber" type="number" value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} />
+ <span>s</span>
+ </div>
+ </div>
<Dropdown
color={SnappingManager.userColor}
- formLabel="Movement"
+ formLabel="View"
+ formLabelPlacement="left"
closeOnSelect
items={movementItems}
selectedVal={this.movementName(activeItem)}
- setSelectedVal={val => {
- this.updateMovement(val as PresMovement);
- }}
+ setSelectedVal={val => this.updateMovement(val as PresMovement)}
dropdownType={DropdownType.SELECT}
type={Type.TERT}
/>
- <div className="ribbon-doubleButton" style={{ display: activeItem.presentation_movement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
- <div className="presBox-subheading">Zoom (% screen filled)</div>
- <div className="ribbon-property" style={{ border: `solid 1px ${SnappingManager.userColor}` }}>
- <input className="presBox-input" readOnly type="number" value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
+ <div className="ribbon-doubleButton" style={{ display: activeItem.presentation_movement === PresMovement.Zoom ? undefined : 'none' }}>
+ <Tooltip title={<div>How much (%) of screen target should occupy</div>}>
+ <div className="presBox-subheading">ZOOM %</div>
+ </Tooltip>
+ <div className="presBox-subheading-slider">{PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)}</div>
+ <div className="ribbon-property" style={{ border: `solid 1px ${SnappingManager.userColor}`, display: 'flex', maxWidth: 60, width: '100%' }}>
+ <input className="presBox-inputNumber" type="number" value={zoom} onChange={e => this.updateZoom(e.target.value)} />
+ <span>%</span>
</div>
</div>
- {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)}
- <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Transition Time</div>
- <div className="ribbon-property" style={{ border: `solid 1px ${SnappingManager.userColor}` }}>
- <input className="presBox-input" type="number" readOnly value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s
- </div>
- </div>
- {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.updateTransitionTime)}
- <div className="slider-headers">
- <div className="slider-text">Fast</div>
- <div className="slider-text">Medium</div>
- <div className="slider-text">Slow</div>
- </div>
{/* Easing function */}
- <Dropdown
- color={SnappingManager.userColor}
- formLabel="Easing Function"
- closeOnSelect
- items={easeItems}
- selectedVal={this.activeItem.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'}
- setSelectedVal={val => {
- if (typeof val === 'string') {
- if (val !== 'custom') {
- this.setEaseFunc(this.activeItem, val);
- } else {
- this.setBezierEditorVisibility(true);
- this.setEaseFunc(this.activeItem, TIMING_DEFAULT_MAPPINGS.ease);
- }
- }
- }}
- dropdownType={DropdownType.SELECT}
- type={Type.TERT}
- />
- {/* Custom */}
- <div
- className="presBox-show-hide-dropdown"
- style={{ alignSelf: 'flex-start' }}
- onClick={e => {
- e.stopPropagation();
- this.setBezierEditorVisibility(!this.showBezierEditor);
- }}>
- {`${this.showBezierEditor ? 'Hide' : 'Show'} Timing Editor`}
- <FontAwesomeIcon icon={this.showBezierEditor ? 'chevron-up' : 'chevron-down'} />
- </div>
+ {!this.showEaseFunctions ? null : (
+ <Dropdown
+ color={SnappingManager.userColor}
+ formLabel="Timing"
+ formLabelPlacement="left"
+ closeOnSelect
+ items={easeItems}
+ selectedVal={this.activeItem.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'}
+ setSelectedVal={val => typeof val === 'string' && this.setEaseFunc(this.activeItem, val !== 'custom' ? val : TIMING_DEFAULT_MAPPINGS.ease)}
+ dropdownType={DropdownType.SELECT}
+ type={Type.TERT}
+ />
+ )}
</div>
</div>
{/* Cubic bezier editor */}
- {this.showBezierEditor && (
- <div className="presBox-option-block" style={{ paddingTop: 0 }}>
- <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}>
- Custom Timing Function
- </p>
+ {!this.showEaseFunctions || !StrCast(activeItem.presentation_easeFunc).includes('cubic-bezier') ? null : (
+ <div className="presBox-option-block" style={{ paddingTop: 0, alignItems: 'center' }}>
<CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} />
</div>
)}
- {/* This chatbox is for getting slide effect transition suggestions from gpt and visualizing them */}
- <div className="presBox-gpt-chat">
- Effects
- <div className="pres-chat">
- <div className="pres-chatbox-container">
- <ReactTextareaAutosize
- placeholder="Customize prompt for effect suggestions. Leave blank for random results."
- className="pres-chatbox"
- value={this.animationChat}
- onChange={e => {
- this.setAnimationChat(e.target.value);
- }}
- onKeyDown={e => {
- this.stopDictation();
- e.stopPropagation();
- }}
- />
- </div>
- <Button
- style={{ alignSelf: 'flex-end' }}
- text="Send"
- type={Type.TERT}
- icon={this.isLoading ? <ReactLoading type="spin" color="#ffffff" width={20} height={20} /> : <AiOutlineSend />}
- iconPlacement="right"
- color={SnappingManager.userVariantColor}
- onClick={this.customizeAnimations}
- />
- </div>
- </div>
-
<div
className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`}
- onPointerDown={StopEvent}
- onPointerUp={StopEvent}
+ onPointerDown={e => e.stopPropagation()}
+ onPointerUp={e => e.stopPropagation()}
onClick={action(e => {
e.stopPropagation();
this._openMovementDropdown = false;
@@ -2011,214 +1974,169 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._openBulletEffectDropdown = false;
})}>
<div className="presBox-option-block">
- Click on a box to apply the effect.
- <div className="presBox-option-block presBox-option-center">
- {/* Preview Animations */}
- <div className="presBox-effects">
- {this.generatedAnimations.map((elem, i) => (
- <div
- // eslint-disable-next-line react/no-array-index-key
- key={i}
- className="presBox-effect-container"
- onClick={() => {
- this.updateEffect(elem.effect, false);
- this.updateEffectDirection(elem.direction);
- this.updateEffectTiming(this.activeItem, {
- type: SpringType.CUSTOM,
- stiffness: elem.stiffness,
- damping: elem.damping,
- mass: elem.mass,
- });
- }}>
- <SlideEffect dir={elem.direction} presEffect={elem.effect} springSettings={elem} infinite>
- <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[i] }} />
- </SlideEffect>
- </div>
- ))}
+ {/* Effect dropdown */}
+ <div style={{ display: 'flex' }}>
+ <Dropdown
+ color={SnappingManager.userColor}
+ formLabel="Effect"
+ toolTip="Animation effect to apply when transitiong to slide"
+ formLabelPlacement="left"
+ closeOnSelect
+ items={effectItems}
+ selectedVal={effect?.toString()}
+ setSelectedVal={val => {
+ this.updateEffect(val as PresEffect, false);
+ // set default spring options for that effect
+ this.updateEffectTiming(activeItem, presEffectDefaultTimings[val as keyof typeof presEffectDefaultTimings]);
+ }}
+ dropdownType={DropdownType.SELECT}
+ type={Type.TERT}
+ />
+
+ <div
+ className={`ribbon-toggle ${this._showAIGallery ? 'active' : ''}`}
+ style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
+ color: SnappingManager.userColor,
+ background: this._showAIGallery ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
+ }}
+ onClick={() => this.setShowAIGalleryVisibilty(!this._showAIGallery)}>
+ MORE
</div>
</div>
- {/* Effect dropdown */}
- <Dropdown
- color={SnappingManager.userColor}
- formLabel="Slide Effect"
- closeOnSelect
- items={effectItems}
- selectedVal={effect?.toString()}
- setSelectedVal={val => {
- this.updateEffect(val as PresEffect, false);
- // set default spring options for that effect
- this.updateEffectTiming(activeItem, presEffectDefaultTimings[val as keyof typeof presEffectDefaultTimings]);
- }}
- dropdownType={DropdownType.SELECT}
- type={Type.TERT}
- />
- {/* Effect direction */}
- {/* Only applies to certain effects */}
- {(effect === PresEffect.Flip || effect === PresEffect.Bounce || effect === PresEffect.Roll) && (
- <>
- <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Effect direction</div>
- <div className="ribbon-property" style={{ border: `solid 1px ${SnappingManager.userColor}` }}>
- {StrCast(this.activeItem.presentation_effectDirection)}
- </div>
- </div>
- <div className="presBox-icon-list">
- <IconButton
- type={Type.TERT}
- color={activeItem.presentation_effectDirection === PresEffectDirection.Left ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
- tooltip="Left"
- icon={<FaArrowRight size="16px" />}
- onClick={() => this.updateEffectDirection(PresEffectDirection.Left)}
- />
- <IconButton
- type={Type.TERT}
- color={activeItem.presentation_effectDirection === PresEffectDirection.Right ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
- tooltip="Right"
- icon={<FaArrowLeft size="16px" />}
- onClick={() => this.updateEffectDirection(PresEffectDirection.Right)}
- />
- {effect !== PresEffect.Roll && (
- <>
+
+ {this.aiEffects}
+ <div className="presBox-gpt-chat">
+ {/* Effect direction */}
+ {/* Only applies to certain effects */}
+ {(effect === PresEffect.Flip || effect === PresEffect.Bounce || effect === PresEffect.Roll) && (
+ <div className="ribbon-doubleButton">
+ <div className="presBox-subheading">DIRECTION</div>
+ <div style={{ width: '100%' }}>
+ <div className="presBox-icon-list" style={{ width: 'fit-content', margin: 'auto' }}>
<IconButton
type={Type.TERT}
- color={activeItem.presentation_effectDirection === PresEffectDirection.Top ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
- tooltip="Top"
- icon={<FaArrowDown size="16px" />}
- onClick={() => this.updateEffectDirection(PresEffectDirection.Top)}
+ color={activeItem.presentation_effectDirection === PresEffectDirection.Left ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
+ tooltip="Left"
+ icon={<FaArrowRight size="16px" />}
+ onClick={() => this.updateEffectDirection(PresEffectDirection.Left)}
/>
<IconButton
type={Type.TERT}
- color={activeItem.presentation_effectDirection === PresEffectDirection.Bottom ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
- tooltip="Bottom"
- icon={<FaArrowUp size="16px" />}
- onClick={() => this.updateEffectDirection(PresEffectDirection.Bottom)}
- />
- </>
- )}
- </div>
- </>
- )}
- {/* Spring settings */}
- {/* No spring settings for jackinthebox (lightspeed) */}
- {effect !== PresEffect.Lightspeed && (
- <>
- <Dropdown
- color={SnappingManager.userColor}
- formLabel="Effect Timing"
- closeOnSelect
- items={effectTimings}
- selectedVal={timingConfig.type}
- setSelectedVal={val => {
- this.updateEffectTiming(activeItem, {
- type: val as SpringType,
- ...springMappings[val],
- });
- }}
- dropdownType={DropdownType.SELECT}
- type={Type.TERT}
- />
- <div
- className="presBox-show-hide-dropdown"
- onClick={e => {
- e.stopPropagation();
- this.setSpringEditorVisibility(!this.showSpringEditor);
- }}>
- {`${this.showSpringEditor ? 'Hide' : 'Show'} Spring Settings`}
- <FontAwesomeIcon icon={this.showSpringEditor ? 'chevron-up' : 'chevron-down'} />
- </div>
- {this.showSpringEditor && (
- <>
- <div>Tension</div>
- <div
- onPointerDown={e => {
- e.stopPropagation();
- }}>
- <Slider
- min={1}
- max={1000}
- step={5}
- size="small"
- value={timingConfig.stiffness}
- onChange={(e, val) => {
- if (!timingConfig) return;
- this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, stiffness: val as number });
- }}
- valueLabelDisplay="auto"
+ color={activeItem.presentation_effectDirection === PresEffectDirection.Right ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
+ tooltip="Right"
+ icon={<FaArrowLeft size="16px" />}
+ onClick={() => this.updateEffectDirection(PresEffectDirection.Right)}
/>
+ {effect !== PresEffect.Roll && (
+ <>
+ <IconButton
+ type={Type.TERT}
+ color={activeItem.presentation_effectDirection === PresEffectDirection.Top ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
+ tooltip="Top"
+ icon={<FaArrowDown size="16px" />}
+ onClick={() => this.updateEffectDirection(PresEffectDirection.Top)}
+ />
+ <IconButton
+ type={Type.TERT}
+ color={activeItem.presentation_effectDirection === PresEffectDirection.Bottom ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor}
+ tooltip="Bottom"
+ icon={<FaArrowUp size="16px" />}
+ onClick={() => this.updateEffectDirection(PresEffectDirection.Bottom)}
+ />
+ </>
+ )}
</div>
- <div>Damping</div>
- <div
- onPointerDown={e => {
- e.stopPropagation();
- }}>
- <Slider
- min={1}
- max={100}
- step={1}
- size="small"
- value={timingConfig.damping}
- onChange={(e, val) => {
- if (!timingConfig) return;
- this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, damping: val as number });
- }}
- valueLabelDisplay="auto"
- />
- </div>
- <div>Mass</div>
- <div
- onPointerDown={e => {
- e.stopPropagation();
- }}>
- <Slider
- min={1}
- max={10}
- step={1}
- size="small"
- value={timingConfig.mass}
- onChange={(e, val) => {
- if (!timingConfig) return;
- this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, mass: val as number });
- }}
- valueLabelDisplay="auto"
- />
- </div>
- Preview Effect
- <div className="presBox-option-block presBox-option-center">
- <div className="presBox-effect-container">
- <SlideEffect dir={direction} presEffect={effect} springSettings={timingConfig} infinite>
- <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[0] }} />
- </SlideEffect>
- </div>
- </div>
- </>
- )}
- </>
- )}
+ </div>
+ </div>
+ )}
+ {![PresEffect.Lightspeed, PresEffect.Fade, PresEffect.None, ''].includes(effect) && (
+ <>
+ <Dropdown
+ color={SnappingManager.userColor}
+ formLabel="Springiness"
+ formLabelPlacement="left"
+ closeOnSelect
+ items={effectTimings}
+ selectedVal={timingConfig.type}
+ setSelectedVal={val => this.updateEffectTiming(activeItem, { type: val as SpringType, ...springMappings[val] })}
+ dropdownType={DropdownType.SELECT}
+ type={Type.TERT}
+ />
+
+ <div style={{ display: SnappingManager.PropertiesWidth < 1 ? 'none' : undefined }}>
+ {/* No spring settings for jackinthebox (lightspeed) */}
+ {StrCast(activeItem.presentation_effectTiming).includes('custom') && effect !== PresEffect.None && (
+ <>
+ <div className="presBox-springSlider">
+ <span>Tension</span>
+ <div onPointerDown={e => e.stopPropagation()}>
+ {/* prettier-ignore */}
+ <Slider min={1} max={1000} step={5} size="small"
+ value={timingConfig.stiffness}
+ onChange={(e, val) => timingConfig && this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, stiffness: val as number })}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ </div>
+ <div className="presBox-springSlider">
+ <span>Damping</span>
+ <div onPointerDown={e => e.stopPropagation()}>
+ {/* prettier-ignore */}
+ <Slider min={1} max={100} step={1} size="small"
+ value={timingConfig.damping}
+ onChange={(e, val) => timingConfig && this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, damping: val as number })}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ </div>
+ <div className="presBox-springSlider">
+ <span>Mass</span>
+ <div onPointerDown={e => e.stopPropagation()}>
+ {/* prettier-ignore */}
+ <Slider min={1} max={10} step={1} size="small"
+ value={timingConfig.mass}
+ onChange={(e, val) => timingConfig && this.updateEffectTiming(activeItem, { ...timingConfig, type: SpringType.CUSTOM, mass: val as number })}
+ valueLabelDisplay="auto"
+ />
+ </div>
+ </div>
+ </>
+ )}
+ </div>
+ </>
+ )}
+ </div>
</div>
+ </div>
- {/* Toggles */}
- <div className="presBox-option-block">
- <Toggle
- formLabel="Play Audio Annotation"
- toggleType={ToggleType.SWITCH}
- toggleStatus={BoolCast(activeItem.presentation_playAudio)}
- onClick={() => {
- activeItem.presentation_playAudio = !BoolCast(activeItem.presentation_playAudio);
- }}
- color={SnappingManager.userColor}
- />
- <Toggle
- formLabel="Zoom Text Selections"
- toggleType={ToggleType.SWITCH}
- toggleStatus={BoolCast(activeItem.presentation_zoomText)}
- onClick={() => {
- activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText);
- }}
+ {[PresEffect.None, PresEffect.Fade, ''].includes(effect) ? null : (
+ <div className="presBox-previewContainer">
+ <Button
+ type={Type.TERT}
+ tooltip="show preview of slide animation effect"
+ size={Size.SMALL}
color={SnappingManager.userColor}
+ background="transparent"
+ onClick={action(() => {
+ this._showPreview = false;
+ setTimeout(action(() => { this._showPreview = true; }) ); // prettier-ignore
+ })}
+ text="Preview Effect"
/>
- <Button text="Apply to all" type={Type.TERT} color={SnappingManager.userVariantColor} onClick={() => this.applyTo(this.childDocs)} />
+ <div className="presBox-option-block presBox-option-center">
+ <div className="presBox-effect-container">
+ {!this._showPreview ? null : (
+ <SlideEffect dir={direction} presEffect={effect} springSettings={timingConfig}>
+ <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[0] }} />
+ </SlideEffect>
+ )}
+ </div>
+ </div>
</div>
- </div>
+ )}
+
+ <Button text="Apply to all slides" type={Type.TERT} color={SnappingManager.userVariantColor} onClick={() => this.applyTo(this.childDocs)} />
</>
);
}
@@ -2244,7 +2162,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div id="startTime" className="slider-number" style={{ color: SnappingManager.userColor, backgroundColor: SnappingManager.userBackgroundColor }}>
<input
className="presBox-input"
- style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
type="number"
readOnly
value={NumCast(activeItem.config_clipStart).toFixed(2)}
@@ -2271,7 +2188,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<input
className="presBox-input"
onKeyDown={e => e.stopPropagation()}
- style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }}
type="number"
readOnly
value={configClipEnd.toFixed(2)}
@@ -2612,13 +2528,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
createTemplate = (layout: string, input?: string) => {
const x = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.x) : 0;
const y = this.activeItem && this.targetDoc ? NumCast(this.targetDoc.y) + NumCast(this.targetDoc._height) + 20 : 0;
- const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _text_fontSize: '24pt' });
- const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _text_fontSize: '16pt' });
- const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _text_fontSize: '20pt' });
- const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _text_fontSize: '24pt' });
- const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _text_fontSize: '14pt' });
- const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _text_fontSize: '14pt' });
- const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _text_fontSize: '14pt' });
+ const title = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, text_fontSize: '24pt' });
+ const subtitle = () => Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, text_fontSize: '16pt' });
+ const header = () => Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, text_fontSize: '20pt' });
+ const contentTitle = () => Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, text_fontSize: '24pt' });
+ const content = () => Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, text_fontSize: '14pt' });
+ const content1 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, text_fontSize: '14pt' });
+ const content2 = () => Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, text_fontSize: '14pt' });
// prettier-ignore
switch (layout) {
case 'blank': return Docs.Create.FreeformDocument([], { title: input || 'Blank slide', _width: 400, _height: 225, x, y });
@@ -3045,7 +2961,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div className="Slide">
{mode !== CollectionViewType.Invalid ? (
<CollectionView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
PanelWidth={this._props.PanelWidth}
PanelHeight={this.panelHeight}
@@ -3054,7 +2969,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
ignoreUnrendered
childDragAction={dropActionType.move}
setContentViewBox={emptyFunction}
- // childLayoutFitWidth={returnTrue}
childOpacity={returnOne}
childClickScript={PresBox.navigateToDocScript}
childLayoutTemplate={this.childLayoutTemplate}
@@ -3080,7 +2994,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
} */}
</div>
{/* presbox chatbox */}
- {this.chatActive && <div className="presBox-chatbox" />}
+ {this._chatActive && <div className="presBox-chatbox" />}
</div>
);
}