aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/trails/PresBox.tsx
diff options
context:
space:
mode:
authorNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2024-06-03 13:33:37 -0400
committerNathan-SR <144961007+Nathan-SR@users.noreply.github.com>2024-06-03 13:33:37 -0400
commit9e77f980e7704999ef0a1c1845d660bccb13ff8a (patch)
tree14ca0da5915e4382a7bcb15f7d0b241941c8291f /src/client/views/nodes/trails/PresBox.tsx
parent1be63695875c9242fba43d580465e8765cf3991d (diff)
parent202e994515392892676f8f080852db1e32b8dbd3 (diff)
Merge branch 'master' into nathan-starter
Diffstat (limited to 'src/client/views/nodes/trails/PresBox.tsx')
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx1067
1 files changed, 744 insertions, 323 deletions
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 75492d2f9..0c73400a9 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -2,9 +2,16 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
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 { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, 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';
import { lightOrDark, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../ClientUtils';
import { Doc, DocListCast, Field, FieldResult, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { Animation, DocData, TransitionTimer } from '../../../../fields/DocSymbols';
@@ -16,9 +23,11 @@ import { listSpec } from '../../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, StrCast, toList } from '../../../../fields/Types';
import { emptyFunction, emptyPath, stringHash } from '../../../../Utils';
+import { getSlideTransitionSuggestions, gptSlideProperties, gptTrailSlideCustomization } from '../../../apis/gpt/PresCustomization';
import { DocServer } from '../../../DocServer';
import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { DictationManager } from '../../../util/DictationManager';
import { dropActionType } from '../../../util/DropActionTypes';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SerializationHelper } from '../../../util/SerializationHelper';
@@ -36,8 +45,11 @@ import { FieldView, FieldViewProps } from '../FieldView';
import { FocusViewOptions } from '../FocusViewOptions';
import { OpenWhere, OpenWhereMod } from '../OpenWhere';
import { ScriptingBox } from '../ScriptingBox';
+import CubicBezierEditor, { EaseFuncToPoints, TIMING_DEFAULT_MAPPINGS } from './CubicBezierEditor';
import './PresBox.scss';
import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
+import SlideEffect from './SlideEffect';
+import { AnimationSettings, easeItems, effectItems, effectTimings, movementItems, presEffectDefaultTimings, springMappings, springPreviewColors, SpringSettings, SpringType } from './SpringUtils';
@observer
export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -85,7 +97,100 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@observable _treeViewMap: Map<Doc, number> = new Map();
@observable _presKeyEvents: boolean = false;
@observable _forceKeyEvents: boolean = false;
- @computed get isTreeOrStack() {
+
+ // 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 generatedAnimations: AnimationSettings[] = [
+ // default presets
+ {
+ effect: PresEffect.Bounce,
+ direction: PresEffectDirection.Left,
+ stiffness: 400,
+ damping: 15,
+ mass: 1,
+ },
+ {
+ effect: PresEffect.Fade,
+ direction: PresEffectDirection.Left,
+ stiffness: 100,
+ damping: 15,
+ mass: 1,
+ },
+ {
+ effect: PresEffect.Flip,
+ direction: PresEffectDirection.Left,
+ stiffness: 100,
+ damping: 15,
+ mass: 1,
+ },
+ {
+ effect: PresEffect.Rotate,
+ direction: PresEffectDirection.Left,
+ stiffness: 100,
+ damping: 15,
+ mass: 1,
+ },
+ ];
+
+ @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[] }) => {
+ this.setEaseFunc(this.activeItem, `cubic-bezier(${newPoints.p1[0]}, ${newPoints.p1[1]}, ${newPoints.p2[0]}, ${newPoints.p2[1]})`);
+ };
+
+ @computed
+ get currCPoints() {
+ const strPoints = this.activeItem.presentation_easeFunc ? StrCast(this.activeItem.presentation_easeFunc) : 'ease';
+ return EaseFuncToPoints(strPoints);
+ }
+
+ @computed
+ get isTreeOrStack() {
return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._type_collection) as any);
}
@computed get isTree() {
@@ -213,6 +318,85 @@ 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);
+ };
+
+ @action
+ customizeAnimations = async () => {
+ 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);
+ };
+
+ @action
+ customizeWithGPT = async (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]: any } = {};
+ 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);
+ };
+
// 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
// No more frames in current doc and next slide is defined, therefore move to next slide
@@ -664,6 +848,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
return;
}
const effect = activeItem.presentation_effect && activeItem.presentation_effect !== PresEffect.None ? activeItem.presentation_effect : undefined;
+ // default with effect: 750ms else 500ms
const presTime = NumCast(activeItem.presentation_transition, effect ? 750 : 500);
const options: FocusViewOptions = {
willPan: activeItem.presentation_movement !== PresMovement.None,
@@ -673,9 +858,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
effect: activeItem,
noSelect: true,
openLocation: targetDoc.type === DocumentType.PRES ? ((OpenWhere.replace + ':' + PresBox.PanelName) as OpenWhere) : OpenWhere.addLeft,
- easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any,
+ easeFunc: StrCast(activeItem.presentation_easeFunc, 'ease') as any,
zoomTextSelections: BoolCast(activeItem.presentation_zoomText),
- playAudio: BoolCast(activeItem.presPlayAudio),
+ playAudio: BoolCast(activeItem.presentation_playAudio),
playMedia: activeItem.presentation_mediaStart === 'auto',
};
if (activeItem.presentation_openInLightbox) {
@@ -1109,6 +1294,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@action
keyEvents = (e: KeyboardEvent) => {
if (e.target instanceof HTMLInputElement) return;
+ if (e.target instanceof HTMLTextAreaElement) return;
let handled = false;
const anchorNode = document.activeElement as HTMLDivElement;
if (anchorNode && anchorNode.className?.includes('lm_title')) return;
@@ -1389,9 +1575,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@undoBatch
updateEaseFunc = (activeItem: Doc) => {
- activeItem.presEaseFunc = activeItem.presEaseFunc === 'linear' ? 'ease' : 'linear';
+ activeItem.presentation_easeFunc = activeItem.presentation_easeFunc === 'linear' ? 'ease' : 'linear';
+ this.selectedArray.forEach(doc => {
+ doc.presentation_easeFunc = activeItem.presentation_easeFunc;
+ });
+ };
+
+ setEaseFunc = (activeItem: Doc, easeFunc: string) => {
+ activeItem.presentation_easeFunc = easeFunc;
this.selectedArray.forEach(doc => {
- doc.presEaseFunc = activeItem.presEaseFunc;
+ doc.presentation_easeFunc = activeItem.presentation_easeFunc;
});
};
@@ -1407,6 +1600,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
bullet ? (doc.presBulletEffect = effect) : (doc.presentation_effect = effect);
});
+ @undoBatch
+ updateEffectTiming = (activeItem: Doc, timing: SpringSettings) => {
+ activeItem.presentation_effectTiming = JSON.stringify(timing);
+ this.selectedArray.forEach(doc => {
+ doc.presentation_effectTiming = activeItem.presentation_effectTiming;
+ });
+ };
+
static _sliderBatch: any;
static endBatch = () => {
PresBox._sliderBatch.end();
@@ -1434,6 +1635,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/>
);
+ // Applies the slide transiiton settings to all docs in the array
@undoBatch
applyTo = (array: Doc[]) => {
this.updateMovement(this.activeItem.presentation_movement as PresMovement, true);
@@ -1457,79 +1659,68 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
let duration = activeItem.presentation_duration ? NumCast(activeItem.presentation_duration) / 1000 : 0;
if (activeItem.type === DocumentType.AUDIO) duration = NumCast(activeItem.duration);
return (
- <div className="presBox-ribbon">
- <div className="ribbon-doubleButton">
- <Tooltip title={<div className="dash-tooltip">Hide before presented</div>}>
- <div
- className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`}
- style={{ border: `solid 1px ${SnappingManager.userColor}`, color: SnappingManager.userColor, background: activeItem.presentation_hideBefore ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor }}
- onClick={() => this.updateHideBefore(activeItem)}>
- Hide before
- </div>
- </Tooltip>
- <Tooltip title={<div className="dash-tooltip">Hide while presented</div>}>
- <div
- className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`}
- style={{ border: `solid 1px ${SnappingManager.userColor}`, color: SnappingManager.userColor, background: activeItem.presentation_hide ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor }}
- onClick={() => this.updateHide(activeItem)}>
- Hide
- </div>
- </Tooltip>
-
- <Tooltip title={<div className="dash-tooltip">Hide after presented</div>}>
- <div
- className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`}
- style={{ border: `solid 1px ${SnappingManager.userColor}`, color: SnappingManager.userColor, background: activeItem.presentation_hideAfter ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor }}
- onClick={() => this.updateHideAfter(activeItem)}>
- Hide after
- </div>
- </Tooltip>
-
- <Tooltip title={<div className="dash-tooltip">Open in lightbox view</div>}>
- <div
- className="ribbon-toggle"
- style={{
- border: `solid 1px ${SnappingManager.userColor}`,
- color: SnappingManager.userColor,
- background: activeItem.presentation_openInLightbox ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
- }}
- onClick={() => this.updateOpenDoc(activeItem)}>
- Lightbox
- </div>
- </Tooltip>
- <Tooltip title={<div className="dash-tooltip">Transition movement style</div>}>
- <div
- className="ribbon-toggle"
- style={{ border: `solid 1px ${SnappingManager.userColor}`, color: SnappingManager.userColor, background: activeItem.presEaseFunc === 'ease' ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor }}
- onClick={() => this.updateEaseFunc(activeItem)}>
- {`${StrCast(activeItem.presEaseFunc, 'ease')}`}
- </div>
- </Tooltip>
- </div>
- {[DocumentType.AUDIO, DocumentType.VID].find(dt => dt === targetType) ? 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="presBox-option-block">
+ <div className="presBox-ribbon">
+ <div className="presBox-toggles">
+ <Tooltip title={<div className="dash-tooltip">Hide before presented</div>}>
+ <div
+ className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`}
+ style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
+ color: SnappingManager.userColor,
+ background: activeItem.presentation_hideBefore ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
+ }}
+ onClick={() => this.updateHideBefore(activeItem)}>
+ Hide before
</div>
- <div className="ribbon-propertyUpDown" style={{ color: SnappingManager.userBackgroundColor, background: SnappingManager.userColor }}>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), 1000)}>
- <FontAwesomeIcon icon="caret-up" />
- </div>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), -1000)}>
- <FontAwesomeIcon icon="caret-down" />
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">Hide while presented</div>}>
+ <div
+ className={`ribbon-toggle ${activeItem.presentation_hide ? 'active' : ''}`}
+ style={{ border: `solid 1px ${SnappingManager.userColor}`, color: SnappingManager.userColor, background: activeItem.presentation_hide ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor }}
+ onClick={() => this.updateHide(activeItem)}>
+ Hide
+ </div>
+ </Tooltip>
+ <Tooltip title={<div className="dash-tooltip">Hide after presented</div>}>
+ <div
+ className={`ribbon-toggle ${activeItem.presentation_hideAfter ? 'active' : ''}`}
+ style={{ border: `solid 1px ${SnappingManager.userColor}`, color: SnappingManager.userColor, background: activeItem.presentation_hideAfter ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor }}
+ onClick={() => this.updateHideAfter(activeItem)}>
+ Hide after
+ </div>
+ </Tooltip>
+
+ <Tooltip title={<div className="dash-tooltip">Open in lightbox view</div>}>
+ <div
+ className="ribbon-toggle"
+ style={{
+ border: `solid 1px ${SnappingManager.userColor}`,
+ color: SnappingManager.userColor,
+ background: activeItem.presentation_openInLightbox ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor,
+ }}
+ onClick={() => this.updateOpenDoc(activeItem)}>
+ Lightbox
+ </div>
+ </Tooltip>
+ </div>
+ {[DocumentType.AUDIO, DocumentType.VID].includes(targetType as any 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>
</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>
- </>
- )}
+ {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>
+ </>
+ )}
+ </div>
</div>
);
}
@@ -1548,78 +1739,80 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</div>
);
return (
- <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={() => {
- activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
- activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
- const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
- const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
- activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0;
- // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
- // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
- let dataField = Doc.LayoutFieldKey(tagDoc);
- if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField += '_annotations';
-
- if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
- else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
- }}
- 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={() => {
- 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="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={() => {
+ activeItem.presentation_indexed = activeItem.presentation_indexed === undefined ? 0 : undefined;
+ activeItem.presentation_hideBefore = activeItem.presentation_indexed !== undefined;
+ const tagDoc = PresBox.targetRenderedDoc(this.activeItem);
+ const type = DocCast(tagDoc?.annotationOn)?.type ?? tagDoc.type;
+ activeItem.presentation_indexedStart = type === DocumentType.COL ? 1 : 0;
+ // a progressivized slide doesn't have sub-slides, but rather iterates over the data list of the target being progressivized.
+ // to avoid creating a new slide to correspond to each of the target's data list, we create a computedField to refernce the target's data list.
+ let dataField = Doc.LayoutFieldKey(tagDoc);
+ if (Cast(tagDoc[dataField], listSpec(Doc), null)?.filter(d => d instanceof Doc) === undefined) dataField += '_annotations';
+
+ if (DocCast(activeItem.presentation_targetDoc).annotationOn) activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc.annotationOn?.["${dataField}"]`);
+ else activeItem.data = ComputedField.MakeFunction(`this.presentation_targetDoc?.["${dataField}"]`);
+ }}
+ 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={() => {
+ 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
- <div
- className="presBox-dropdown"
- onClick={action(e => {
- e.stopPropagation();
- this._openBulletEffectDropdown = !this._openBulletEffectDropdown;
- })}
- style={{
- color: SnappingManager.userColor,
- background: SnappingManager.userVariantColor,
- borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5,
- border: this._openBulletEffectDropdown ? `solid 2px ${SnappingManager.userVariantColor}` : `solid 1px ${SnappingManager.userColor}`,
- }}>
- {effect?.toString()}
- <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openBulletEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon="angle-down" />
+ <div className="ribbon-box">
+ Bullet Effect
<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))}
+ className="presBox-dropdown"
+ onClick={action(e => {
+ e.stopPropagation();
+ this._openBulletEffectDropdown = !this._openBulletEffectDropdown;
+ })}
+ style={{
+ color: SnappingManager.userColor,
+ background: SnappingManager.userVariantColor,
+ borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5,
+ border: this._openBulletEffectDropdown ? `solid 2px ${SnappingManager.userVariantColor}` : `solid 1px ${SnappingManager.userColor}`,
+ }}>
+ {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>
</div>
</div>
</div>
@@ -1628,190 +1821,428 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
return null;
}
+
+ @computed get gptDropdown() {
+ return <div />;
+ }
+
@computed get transitionDropdown() {
const { activeItem } = this;
- const preseEffect = (effect: PresEffect) => (
- <div
- className={`presBox-dropdownOption ${activeItem.presentation_effect === effect || (effect === PresEffect.None && !activeItem.presentation_effect) ? 'active' : ''}`}
- onPointerDown={StopEvent}
- onClick={() => this.updateEffect(effect, false)}>
- {effect}
- </div>
- );
- const presMovement = (movement: PresMovement) => (
- <div className={`presBox-dropdownOption ${activeItem.presentation_movement === movement ? 'active' : ''}`} onPointerDown={StopEvent} onClick={() => this.updateMovement(movement)}>
- {movement}
- </div>
- );
- const presDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => {
- const color = activeItem.presentation_effectDirection === direction || (direction === PresEffectDirection.Center && !activeItem.presentation_effectDirection) ? SnappingManager.userVariantColor : SnappingManager.userColor;
- return (
- <Tooltip title={<div className="dash-tooltip">{direction}</div>}>
- <div
- style={{ ...opts, border: direction === PresEffectDirection.Center ? `solid 2px ${color}` : undefined, borderRadius: '100%', cursor: 'pointer', gridColumn, gridRow, justifySelf: 'center', color }}
- onClick={() => this.updateEffectDirection(direction)}>
- {icon ? <FontAwesomeIcon icon={icon as any} /> : null}
- </div>
- </Tooltip>
- );
- };
+ // 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,
+ };
+ }
+
if (activeItem && this.targetDoc) {
const transitionSpeed = activeItem.presentation_transition ? NumCast(activeItem.presentation_transition) / 1000 : 0.5;
const zoom = NumCast(activeItem.config_zoom, 1) * 100;
- const effect = activeItem.presentation_effect ? activeItem.presentation_effect : PresMovement.None;
+ const effect = StrCast(activeItem.presentation_effect) ? (StrCast(activeItem.presentation_effect) as any as PresEffect) : PresEffect.None;
+ const direction = StrCast(activeItem.presentation_effectDirection) as PresEffectDirection;
+
return (
- <div
- className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`}
- onPointerDown={StopEvent}
- onPointerUp={StopEvent}
- onClick={action(e => {
- e.stopPropagation();
- this._openMovementDropdown = false;
- this._openEffectDropdown = false;
- this._openBulletEffectDropdown = false;
- })}>
- <div className="ribbon-box">
- Movement
- <div
- className="presBox-dropdown"
- onClick={action(e => {
- e.stopPropagation();
- this._openMovementDropdown = !this._openMovementDropdown;
- })}
- style={{
- color: SnappingManager.userColor,
- background: SnappingManager.userVariantColor,
- borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5,
- border: this._openMovementDropdown ? `solid 2px ${SnappingManager.userVariantColor}` : `solid 1px ${SnappingManager.userColor}`,
- }}>
- {this.movementName(activeItem)}
- <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon="angle-down" />
- <div
- className="presBox-dropdownOptions"
- id="presBoxMovementDropdown"
- onPointerDown={StopEvent}
- style={{
- color: SnappingManager.userColor,
- background: SnappingManager.userBackgroundColor,
- display: this._openMovementDropdown ? 'grid' : 'none',
- }}>
- {presMovement(PresMovement.None)}
- {presMovement(PresMovement.Center)}
- {presMovement(PresMovement.Zoom)}
- {presMovement(PresMovement.Pan)}
- {presMovement(PresMovement.Jump)}
+ <>
+ {/* 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' }}>
+ 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>
- </div>
- <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" type="number" readOnly value={zoom} onChange={e => this.updateZoom(e.target.value)} />%
+ </span>
+ <div className="pres-chat">
+ <div className="pres-chatbox-container">
+ <ReactTextareaAutosize
+ placeholder="Describe how you would like to modify the slide properties."
+ className="pres-chatbox"
+ value={this.chatInput}
+ onChange={e => {
+ this.setChatInput(e.target.value);
+ }}
+ onKeyDown={e => {
+ this.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();
+ }
+ }}
+ />
</div>
- <div className="ribbon-propertyUpDown" style={{ color: SnappingManager.userBackgroundColor, background: SnappingManager.userColor }}>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), 0.1)}>
- <FontAwesomeIcon icon="caret-up" />
+ <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.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}
+ onClick={action(e => {
+ e.stopPropagation();
+ this._openMovementDropdown = false;
+ this._openEffectDropdown = false;
+ this._openBulletEffectDropdown = false;
+ })}>
+ <div
+ className="presBox-option-block"
+ // style={{ padding: '16px' }}
+ >
+ Movement
+ <Dropdown
+ color={SnappingManager.userColor}
+ formLabel="Movement"
+ closeOnSelect
+ items={movementItems}
+ selectedVal={this.movementName(activeItem)}
+ 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>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), -0.1)}>
- <FontAwesomeIcon icon="caret-down" />
+ </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>
- </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
+ {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>
- <div className="ribbon-propertyUpDown" style={{ color: SnappingManager.userBackgroundColor, background: SnappingManager.userColor }}>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.updateTransitionTime(String(transitionSpeed), 1000)}>
- <FontAwesomeIcon icon="caret-up" />
- </div>
- <div className="ribbon-propertyUpDownItem" onClick={() => this.updateTransitionTime(String(transitionSpeed), -1000)}>
- <FontAwesomeIcon icon="caret-down" />
- </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>
</div>
- {PresBox.inputter('0.1', '0.1', '100', 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>
</div>
- <div className="ribbon-box">
+
+ {/* 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>
+ <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="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Play Audio Annotation</div>
- <input
- className="presBox-checkbox"
- style={{ margin: 10, border: `solid 1px ${SnappingManager.userColor}` }}
- type="checkbox"
- onChange={() => {
- activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio);
- }}
- checked={BoolCast(activeItem.presPlayAudio)}
+ <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 className="ribbon-doubleButton" style={{ display: 'inline-flex' }}>
- <div className="presBox-subheading">Zoom Text Selections</div>
- <input
- className="presBox-checkbox"
- style={{ margin: 10, border: `solid 1px ${SnappingManager.userColor}` }}
- type="checkbox"
- onChange={() => {
- activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText);
+ </div>
+
+ <div
+ className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`}
+ onPointerDown={StopEvent}
+ onPointerUp={StopEvent}
+ onClick={action(e => {
+ e.stopPropagation();
+ this._openMovementDropdown = false;
+ this._openEffectDropdown = false;
+ 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>
+ ))}
+ </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]);
}}
- checked={BoolCast(activeItem.presentation_zoomText)}
+ 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 && (
+ <>
+ <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>
+ </>
+ )}
+ {/* 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"
+ />
+ </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
- className="presBox-dropdown"
- onClick={action(e => {
- e.stopPropagation();
- this._openEffectDropdown = !this._openEffectDropdown;
- })}
- style={{
- color: SnappingManager.userColor,
- background: SnappingManager.userVariantColor,
- borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5,
- border: this._openEffectDropdown ? `solid 2px ${SnappingManager.userVariantColor}` : `solid 1px ${SnappingManager.userColor}`,
- }}>
- {effect?.toString()}
- <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this._openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon="angle-down" />
- <div
- className="presBox-dropdownOptions"
- id="presBoxMovementDropdown"
- style={{
- color: SnappingManager.userColor,
- background: SnappingManager.userBackgroundColor,
- display: this._openEffectDropdown ? 'grid' : 'none',
+
+ {/* 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);
}}
- onPointerDown={e => e.stopPropagation()}>
- {Object.values(PresEffect)
- .filter(v => isNaN(Number(v)))
- .map(presEffect => preseEffect(presEffect))}
- </div>
- </div>
- <div className="ribbon-doubleButton" style={{ display: effect === PresEffectDirection.None ? 'none' : '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="effectDirection" style={{ display: effect === PresEffectDirection.None ? 'none' : 'grid', width: 40 }}>
- {presDirection(PresEffectDirection.Left, 'angle-right', 1, 2, {})}
- {presDirection(PresEffectDirection.Right, 'angle-left', 3, 2, {})}
- {presDirection(PresEffectDirection.Top, 'angle-down', 2, 1, {})}
- {presDirection(PresEffectDirection.Bottom, 'angle-up', 2, 3, {})}
- {presDirection(PresEffectDirection.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
- </div>
- </div>
- <div className="ribbon-final-box">
- <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
- Apply to all
+ color={SnappingManager.userColor}
+ />
+ <Toggle
+ formLabel="Zoom Text Selections"
+ toggleType={ToggleType.SWITCH}
+ toggleStatus={BoolCast(activeItem.presentation_zoomText)}
+ onClick={() => {
+ activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText);
+ }}
+ color={SnappingManager.userColor}
+ />
+ <Button text="Apply to all" type={Type.TERT} color={SnappingManager.userVariantColor} onClick={() => this.applyTo(this.childDocs)} />
</div>
</div>
- </div>
+ </>
);
}
return undefined;
@@ -1995,23 +2426,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/>
<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>
</div>
@@ -2284,6 +2698,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
SnappingManager.SetPropertiesWidth(SnappingManager.PropertiesWidth > 0 ? 0 : 250);
};
+ @action
+ openProperties = () => {
+ // need to also focus slide
+ SnappingManager.SetPropertiesWidth(250);
+ };
+
@computed get toolbar() {
const propIcon = SnappingManager.PropertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
const propTitle = SnappingManager.PropertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
@@ -2670,7 +3090,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
/>
) : null}
</div>
-
{/* {
// if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
<Tooltip title={<div className="dash-tooltip">{'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}</div>}>
@@ -2680,6 +3099,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
</Tooltip>
} */}
</div>
+ {/* presbox chatbox */}
+ {this.chatActive && <div className="presBox-chatbox" />}
</div>
);
}