diff options
-rw-r--r-- | src/client/views/PropertiesView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/trails/CubicBezierEditor.tsx | 95 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.scss | 10 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 466 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresElementBox.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/trails/SlideEffect.scss | 5 | ||||
-rw-r--r-- | src/client/views/nodes/trails/SlideEffect.tsx | 5 | ||||
-rw-r--r-- | src/client/views/nodes/trails/SlideEffectPreview.tsx | 94 |
8 files changed, 273 insertions, 408 deletions
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 15974b9a7..a95477749 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1675,6 +1675,9 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps <div className="propertiesView" style={{ width: this._props.width }}> <div className="propertiesView-sectionTitle" style={{ width: this._props.width }}> Presentation + <div className="propertiesView-info" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/trails/')}> + <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SettingsManager.userColor} /> + </div> </div> <div className="propertiesView-name" style={{ borderBottom: 0 }}> {this.editableTitle} diff --git a/src/client/views/nodes/trails/CubicBezierEditor.tsx b/src/client/views/nodes/trails/CubicBezierEditor.tsx index e12fca20c..aba777e55 100644 --- a/src/client/views/nodes/trails/CubicBezierEditor.tsx +++ b/src/client/views/nodes/trails/CubicBezierEditor.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState } from 'react'; -import { StrCast } from '../../../../fields/Types'; type Props = { setFunc: (newPoints: { p1: number[]; p2: number[] }) => void; @@ -23,15 +22,14 @@ export const TIMING_DEFAULT_MAPPINGS = { const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => { const [animating, setAnimating] = useState(false); - // Should let parent control state - const [cPoints, setCPoints] = useState( - currPoints - ? currPoints - : { - p1: [0.25, 0.1], - p2: [0.25, 1.0], - } - ); + // const [cPoints, setCPoints] = useState( + // currPoints + // ? currPoints + // : { + // p1: [0.25, 0.1], + // p2: [0.25, 1.0], + // } + // ); const [c1Down, setC1Down] = useState(false); const [c2Down, setC2Down] = useState(false); @@ -73,11 +71,11 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => { }; }; - useEffect(() => { - if (!easeFunc.startsWith('cubic')) { - setCPoints(convertToPoints(easeFunc)); - } - }, [easeFunc]); + // useEffect(() => { + // if (!easeFunc.startsWith('cubic')) { + // setFunc(convertToPoints(easeFunc)); + // } + // }, [easeFunc]); useEffect(() => { if (animating) { @@ -93,25 +91,25 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => { setC1Down(false); }); const handlePointerMove = (e: PointerEvent) => { - const newX = cPoints.p1[0] + e.movementX / EDITOR_WIDTH; + const newX = currPoints.p1[0] + e.movementX / EDITOR_WIDTH; if (newX < 0 || newX > 1) { return; } - setCPoints(prev => ({ - ...prev, - p1: [roundToHundredth(prev.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p1[1] - e.movementY / EDITOR_WIDTH)], - })); + // setCPoints(prev => ({ + // ...prev, + // p1: [roundToHundredth(prev.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p1[1] - e.movementY / EDITOR_WIDTH)], + // })); setFunc({ - ...cPoints, - p1: [roundToHundredth(cPoints.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(cPoints.p1[1] - e.movementY / EDITOR_WIDTH)], + ...currPoints, + p1: [roundToHundredth(currPoints.p1[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(currPoints.p1[1] - e.movementY / EDITOR_WIDTH)], }); }; window.addEventListener('pointermove', handlePointerMove); return () => window.removeEventListener('pointermove', handlePointerMove); - }, [c1Down, cPoints]); + }, [c1Down, currPoints]); useEffect(() => { if (!c2Down) return; @@ -119,38 +117,31 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => { setC2Down(false); }); const handlePointerMove = (e: PointerEvent) => { - const newX = cPoints.p2[0] + e.movementX / EDITOR_WIDTH; + const newX = currPoints.p2[0] + e.movementX / EDITOR_WIDTH; if (newX < 0 || newX > 1) { return; } - setCPoints(prev => ({ - ...prev, - p2: [roundToHundredth(prev.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p2[1] - e.movementY / EDITOR_WIDTH)], - })); + // setCPoints(prev => ({ + // ...prev, + // p2: [roundToHundredth(prev.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(prev.p2[1] - e.movementY / EDITOR_WIDTH)], + // })); setFunc({ - ...cPoints, - p2: [roundToHundredth(cPoints.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(cPoints.p2[1] - e.movementY / EDITOR_WIDTH)], + ...currPoints, + p2: [roundToHundredth(currPoints.p2[0] + e.movementX / EDITOR_WIDTH), roundToHundredth(currPoints.p2[1] - e.movementY / EDITOR_WIDTH)], }); }; window.addEventListener('pointermove', handlePointerMove); return () => window.removeEventListener('pointermove', handlePointerMove); - }, [c2Down, cPoints]); + }, [c2Down, currPoints]); return ( <div - // onPointerDown={e => { - // e.stopPropagation; - // }} onPointerMove={e => { e.stopPropagation; - }} - // onPointerUp={e => { - // e.stopPropagation; - // }} - > + }}> <svg className="presBox-bezier-editor" width={`${CONTAINER_WIDTH}`} height={`${CONTAINER_WIDTH}`} xmlns="http://www.w3.org/2000/svg"> {/* Outlines */} <line x1={`${0 + OFFSET}`} y1={`${EDITOR_WIDTH + OFFSET}`} x2={`${EDITOR_WIDTH + OFFSET}`} y2={`${0 + OFFSET}`} stroke="#c1c1c1" strokeWidth="1" /> @@ -158,9 +149,9 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => { <rect x={`${0 + OFFSET}`} y={`${0 + OFFSET}`} width={EDITOR_WIDTH} height={EDITOR_WIDTH} stroke="#c5c5c5" fill="transparent" strokeWidth="1" /> {/* Editor */} <path - d={`M ${0 + OFFSET} ${EDITOR_WIDTH + OFFSET} C ${cPoints.p1[0] * EDITOR_WIDTH + OFFSET} ${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}, ${ - cPoints.p2[0] * EDITOR_WIDTH + OFFSET - } ${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}, ${EDITOR_WIDTH + OFFSET} ${0 + OFFSET}`} + d={`M ${0 + OFFSET} ${EDITOR_WIDTH + OFFSET} C ${currPoints.p1[0] * EDITOR_WIDTH + OFFSET} ${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}, ${ + currPoints.p2[0] * EDITOR_WIDTH + OFFSET + } ${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}, ${EDITOR_WIDTH + OFFSET} ${0 + OFFSET}`} stroke="#ffffff" fill="transparent" /> @@ -174,15 +165,15 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => { }} x1={`${0 + OFFSET}`} y1={`${EDITOR_WIDTH + OFFSET}`} - x2={`${cPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} - y2={`${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} + x2={`${currPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} + y2={`${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} stroke="#00000000" strokeWidth="5" /> - <line x1={`${0 + OFFSET}`} y1={`${EDITOR_WIDTH + OFFSET}`} x2={`${cPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" /> + <line x1={`${0 + OFFSET}`} y1={`${EDITOR_WIDTH + OFFSET}`} x2={`${currPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" /> <circle - cx={`${cPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} - cy={`${EDITOR_WIDTH - cPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} + cx={`${currPoints.p1[0] * EDITOR_WIDTH + OFFSET}`} + cy={`${EDITOR_WIDTH - currPoints.p1[1] * EDITOR_WIDTH + OFFSET}`} r="5" fill={`${c1Down ? '#3fa9ff' : '#ffffff'}`} onPointerDown={e => { @@ -204,15 +195,15 @@ const CubicBezierEditor = ({ setFunc, currPoints, easeFunc }: Props) => { }} x1={`${EDITOR_WIDTH + OFFSET}`} y1={`${0 + OFFSET}`} - x2={`${cPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} - y2={`${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} + x2={`${currPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} + y2={`${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} stroke="#00000000" strokeWidth="5" /> - <line x1={`${EDITOR_WIDTH + OFFSET}`} y1={`${0 + OFFSET}`} x2={`${cPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" /> + <line x1={`${EDITOR_WIDTH + OFFSET}`} y1={`${0 + OFFSET}`} x2={`${currPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} y2={`${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} stroke="#ffffff" strokeWidth="1" /> <circle - cx={`${cPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} - cy={`${EDITOR_WIDTH - cPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} + cx={`${currPoints.p2[0] * EDITOR_WIDTH + OFFSET}`} + cy={`${EDITOR_WIDTH - currPoints.p2[1] * EDITOR_WIDTH + OFFSET}`} r="5" fill={`${c2Down ? '#3fa9ff' : '#ffffff'}`} onPointerDown={e => { diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 58c9b0b42..60d4e580d 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -1,7 +1,7 @@ @import '../../global/globalCssVariables.module.scss'; .presBox-gpt-chat { - padding: 8px; + padding: 16px; display: flex; flex-direction: column; gap: 1rem; @@ -53,13 +53,11 @@ .presBox-effect-container { cursor: pointer; overflow: hidden; - // position: relative; width: 80px; height: 80px; display: flex; justify-content: center; align-items: center; - /* background-color: #1f2028; */ border: 1px solid rgb(118, 118, 118); border-radius: 8px; } @@ -67,13 +65,9 @@ .presBox-effect-demo-box { width: 40px; height: 40px; - // position: absolute; - // top: 20px; - // left: 20px; border-radius: 4px; // default bg background-color: rgb(37, 161, 255); - // transform-origin: center; } // Bezier editor @@ -95,7 +89,7 @@ display: flex; flex-direction: column; gap: 1rem; - // padding: 8px; + padding: 16px; } .presBox-option-center { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 4890c115b..02233d241 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -47,9 +47,9 @@ import { DictationManager } from '../../../util/DictationManager'; import CubicBezierEditor, { TIMING_DEFAULT_MAPPINGS } from './CubicBezierEditor'; import Slider from '@mui/material/Slider'; import { FaArrowDown, FaArrowLeft, FaArrowRight, FaArrowUp, FaCompressArrowsAlt } from 'react-icons/fa'; -import SpringAnimationPreview from './SlideEffectPreview'; import { effectTimings, SpringType, springMappings, effectItems, easeItems, movementItems, SpringSettings, presEffectDefaultTimings, AnimationSettings, springPreviewColors } from './SpringUtils'; import SlideEffect from './SlideEffect'; +import { IoMdInformationCircleOutline } from 'react-icons/io'; export interface pinDataTypes { scrollable?: boolean; @@ -387,24 +387,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { setDictationContent = (value: string) => { console.log('Dictation value', value); this.setChatInput(value); - // // Get the current cursor position - // if (!this._inputref) return; - // const cursorPosition = this._inputref.selectionStart; - // const currentValue = this.chatInput; - - // // split before and after - // const textBeforeCursor = currentValue.slice(0, cursorPosition); - // const textAfterCursor = currentValue.slice(cursorPosition); - - // // insertion - // const updatedText = textBeforeCursor + value + textAfterCursor; - - // // Update the textarea value - // this.setChatInput(updatedText); - - // // set new cursor pos - // const newCursorPosition = cursorPosition + value.length; - // this._inputref.setSelectionRange(newCursorPosition, newCursorPosition); }; @action @@ -415,7 +397,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { try { const res = await getSlideTransitionSuggestions(this.animationChat); - console.log('GPT Result:', res); if (typeof res === 'string') { const resObj = JSON.parse(res); console.log('Parsed GPT Result ', resObj); @@ -448,11 +429,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } } } - console.log('current slide props', currSlideProperties); + console.log('current slide props ', currSlideProperties); try { const res = await gptTrailSlideCustomization(input, currSlideProperties); - console.log('GPT Result:', res); if (typeof res === 'string') { const resObj = JSON.parse(res); console.log('Parsed GPT Result ', resObj); @@ -1798,46 +1778,51 @@ 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="presBox-toggles"> - <Tooltip title={<div className="dash-tooltip">Hide before presented</div>}> - <div - className={`ribbon-toggle ${activeItem.presentation_hideBefore ? 'active' : ''}`} - style={{ border: `solid 1px ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideBefore ? SettingsManager.userVariantColor : SettingsManager.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 ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hide ? SettingsManager.userVariantColor : SettingsManager.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 ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideAfter ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }} - onClick={() => this.updateHideAfter(activeItem)}> - Hide after - </div> - </Tooltip> + <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 ${SettingsManager.userColor}`, + color: SettingsManager.userColor, + background: activeItem.presentation_hideBefore ? SettingsManager.userVariantColor : SettingsManager.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 ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hide ? SettingsManager.userVariantColor : SettingsManager.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 ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presentation_hideAfter ? SettingsManager.userVariantColor : SettingsManager.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 ${SettingsManager.userColor}`, - color: SettingsManager.userColor, - background: activeItem.presentation_openInLightbox ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor, - }} - onClick={() => this.updateOpenDoc(activeItem)}> - Lightbox - </div> - </Tooltip> - {/* <Tooltip title={<div className="dash-tooltip">Transition movement style</div>}> + <Tooltip title={<div className="dash-tooltip">{'Open in lightbox view'}</div>}> + <div + className="ribbon-toggle" + style={{ + border: `solid 1px ${SettingsManager.userColor}`, + color: SettingsManager.userColor, + background: activeItem.presentation_openInLightbox ? SettingsManager.userVariantColor : SettingsManager.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 ${SettingsManager.userColor}`, color: SettingsManager.userColor, background: activeItem.presEaseFunc === 'ease' ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor }} @@ -1845,15 +1830,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { {`${StrCast(activeItem.presEaseFunc, 'ease')}`} </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 ${SettingsManager.userColor}` }}> - <input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s - </div> - {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}> + </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 ${SettingsManager.userColor}` }}> + <input className="presBox-input" type="number" readOnly={true} value={duration} onKeyDown={e => e.stopPropagation()} onChange={e => this.updateDurationTime(e.target.value)} /> s + </div> + {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}> <div className="ribbon-propertyUpDownItem" onClick={() => this.updateDurationTime(String(duration), 1000)}> <FontAwesomeIcon icon={'caret-up'} /> </div> @@ -1861,15 +1846,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <FontAwesomeIcon icon={'caret-down'} /> </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> - </> - )} + </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> ); } @@ -1887,74 +1873,76 @@ 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 ${SettingsManager.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 = 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 ? true : false} - /> - </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 ${SettingsManager.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 ${SettingsManager.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 ${SettingsManager.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 = 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 ? true : false} + /> + </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 ${SettingsManager.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 ${SettingsManager.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: SettingsManager.userColor, - background: SettingsManager.userVariantColor, - borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5, - border: this._openBulletEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.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: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }} - onPointerDown={e => e.stopPropagation()}> - {Object.values(PresEffect) - .filter(v => isNaN(Number(v))) - .map(effect => bulletEffect(effect))} + className="presBox-dropdown" + onClick={action(e => { + e.stopPropagation(); + this._openBulletEffectDropdown = !this._openBulletEffectDropdown; + })} + style={{ + color: SettingsManager.userColor, + background: SettingsManager.userVariantColor, + borderBottomLeftRadius: this._openBulletEffectDropdown ? 0 : 5, + border: this._openBulletEffectDropdown ? `solid 2px ${SettingsManager.userVariantColor}` : `solid 1px ${SettingsManager.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: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }} + onPointerDown={e => e.stopPropagation()}> + {Object.values(PresEffect) + .filter(v => isNaN(Number(v))) + .map(effect => bulletEffect(effect))} + </div> </div> </div> </div> @@ -2016,22 +2004,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { 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 : PresEffect.None; + const effect = StrCast(activeItem.presentation_effect) ? StrCast(activeItem.presentation_effect) : PresEffect.None; + const direction = StrCast(activeItem.presentation_effectDirection); return ( <> - {/* GPT Component */} - {/* This chatbox is for customizing the properties of trails, like transition time, movement type (zoom, pan) */} + {/* This chatbox is for customizing the properties of trails, like transition time, movement type (zoom, pan) using GPT*/} <div className="presBox-gpt-chat"> - <p>Customize with GPT</p> + <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/')}> + <IconButton icon={<FontAwesomeIcon icon="info-circle" />} color={SettingsManager.userColor} /> + </div> + </span> <div className="pres-chat"> <div className="pres-chatbox-container"> <ReactTextareaAutosize - ref={r => (this._inputref = r)} - minRows={1} - placeholder="Customize..." + // ref={r => (this._inputref = r)} + // minRows={1} + placeholder="Describe how you would like to modify the slide properties." className="pres-chatbox" - autoFocus={true} + // autoFocus={true} value={this.chatInput} onChange={e => { this.setChatInput(e.target.value); @@ -2079,7 +2072,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this._openEffectDropdown = false; this._openBulletEffectDropdown = false; })}> - <div className="presBox-option-block" style={{ padding: '16px' }}> + <div + className="presBox-option-block" + // style={{ padding: '16px' }} + > Movement <Dropdown color={StrCast(Doc.UserDoc().userColor)} @@ -2098,14 +2094,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}> <input className="presBox-input" readOnly={true} type="number" value={zoom} onChange={e => this.updateZoom(e.target.value)} />% </div> - {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.userColor }}> - <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), 0.1)}> - <FontAwesomeIcon icon={'caret-up'} /> - </div> - <div className="ribbon-propertyUpDownItem" onClick={() => this.updateZoom(String(zoom), -0.1)}> - <FontAwesomeIcon icon={'caret-down'} /> - </div> - </div> */} </div> {PresBox.inputter('0', '1', '100', zoom, activeItem.presentation_movement === PresMovement.Zoom, this.updateZoom)} <div className="ribbon-doubleButton" style={{ display: 'inline-flex' }}> @@ -2113,14 +2101,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="ribbon-property" style={{ border: `solid 1px ${SettingsManager.userColor}` }}> <input className="presBox-input" type="number" readOnly={true} value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> s </div> - {/* <div className="ribbon-propertyUpDown" style={{ color: SettingsManager.userBackgroundColor, background: SettingsManager.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> - </div> */} </div> {PresBox.inputter('0.1', '0.1', '10', transitionSpeed, true, this.updateTransitionTime)} <div className={'slider-headers'}> @@ -2129,61 +2109,59 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="slider-text">Slow</div> </div> {/* Easing function */} - <div className="presBox-option-block presBox-option-center"> - <Dropdown - color={StrCast(Doc.UserDoc().userColor)} - formLabel={'Easing Function'} - closeOnSelect={true} - items={easeItems} - selectedVal={this.activeItem.presEaseFunc ? (StrCast(this.activeItem.presEaseFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presEaseFunc)) : 'ease'} - setSelectedVal={val => { - if (typeof val === 'string') { - if (val !== 'custom') { - this.setBezierEditorVisibility(true); - this.setEaseFunc(this.activeItem, val); - } else { - this.setEaseFunc(this.activeItem, TIMING_DEFAULT_MAPPINGS.ease); - } + <Dropdown + color={StrCast(Doc.UserDoc().userColor)} + formLabel={'Easing Function'} + closeOnSelect={true} + items={easeItems} + selectedVal={this.activeItem.presEaseFunc ? (StrCast(this.activeItem.presEaseFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presEaseFunc)) : 'ease'} + setSelectedVal={val => { + if (typeof val === 'string') { + if (val !== 'custom') { + this.setBezierEditorVisibility(true); + this.setEaseFunc(this.activeItem, val); + } else { + 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> + } + }} + 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> </div> - <div className="presBox-option-block"> - {this.showBezierEditor && ( - <> - <p className="presBox-submenu-label" style={{ alignSelf: 'flex-start' }}> - Custom Timing Function - </p> - <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} easeFunc={StrCast(this.activeItem.presEaseFunc)} /> - </> - )} - </div> + + {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} easeFunc={StrCast(this.activeItem.presEaseFunc)} /> + </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 - ref={r => (this._inputref2 = r)} - minRows={1} - placeholder="Get effect suggestions..." + // ref={r => (this._inputref2 = r)} + // minRows={1} + placeholder="Customize prompt for effect suggestions. Leave blank for random results." className="pres-chatbox" - autoFocus={true} + // autoFocus={true} value={this.animationChat} onChange={e => { this.setAnimationChat(e.target.value); @@ -2217,10 +2195,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this._openEffectDropdown = false; this._openBulletEffectDropdown = false; })}> - <div className="presBox-option-block" style={{ padding: '16px' }}> + <div className="presBox-option-block"> + Click on a box to apply the effect. <div className="presBox-option-block presBox-option-center"> - {/* Chat for idea generation */} - {/* Preview Animations */} <div className="presBox-effects"> {this.generatedAnimations.map((elem, i) => ( @@ -2269,13 +2246,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> <div className="presBox-icon-list"> - {/* <IconButton - type={Type.TERT} - color={activeItem.presentation_effectDirection === PresEffectDirection.Center ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor} - tooltip="Center" - icon={<FaCompressArrowsAlt size={'16px'} />} - onClick={() => this.updateEffectDirection(PresEffectDirection.Center)} - /> */} <IconButton type={Type.TERT} color={activeItem.presentation_effectDirection === PresEffectDirection.Left ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor} @@ -2395,9 +2365,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { valueLabelDisplay="auto" /> </div> - <SpringAnimationPreview tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass}> - <div style={{ width: '40px', height: '40px', backgroundColor: '#2e96ff', borderRadius: '4px' }}></div> - </SpringAnimationPreview> + Preview Effect + <div className="presBox-option-block presBox-option-center"> + <div className="presBox-effect-container"> + <SlideEffect dir={direction as PresEffectDirection} presEffect={effect as PresEffect} tension={timingConfig.stiffness} friction={timingConfig.damping} mass={timingConfig.mass} infinite> + <div className="presBox-effect-demo-box" style={{ backgroundColor: springPreviewColors[0] }}></div> + </SlideEffect> + </div> + </div> </> )} </> @@ -2405,26 +2380,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> {/* Toggles */} - <Toggle - formLabel={'Play Audio Annotation'} - toggleType={ToggleType.SWITCH} - toggleStatus={BoolCast(activeItem.presPlayAudio)} - onClick={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} - color={SettingsManager.userColor} - /> - <Toggle - formLabel={'Zoom Text Selections'} - toggleType={ToggleType.SWITCH} - toggleStatus={BoolCast(activeItem.presentation_zoomText)} - onClick={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))} - color={SettingsManager.userColor} - /> - <Button text="Apply to all" type={Type.TERT} color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.applyTo(this.childDocs)} /> - {/* <div className="ribbon-final-box"> - <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}> - Apply to all - </div> - </div> */} + <div className="presBox-option-block"> + <Toggle + formLabel={'Play Audio Annotation'} + toggleType={ToggleType.SWITCH} + toggleStatus={BoolCast(activeItem.presPlayAudio)} + onClick={() => (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} + color={SettingsManager.userColor} + /> + <Toggle + formLabel={'Zoom Text Selections'} + toggleType={ToggleType.SWITCH} + toggleStatus={BoolCast(activeItem.presentation_zoomText)} + onClick={() => (activeItem.presentation_zoomText = !BoolCast(activeItem.presentation_zoomText))} + color={SettingsManager.userColor} + /> + <Button text="Apply to all" type={Type.TERT} color={StrCast(Doc.UserDoc().userVariantColor)} onClick={() => this.applyTo(this.childDocs)} /> + </div> </div> </> ); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 268b1c803..1c6b38aeb 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -513,11 +513,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { </Tooltip> ); items.push( - <Tooltip key="edit" title={<div className="dash-tooltip">Edit</div>}> + <Tooltip key="customize-slide" title={<div className="dash-tooltip">Customize Slide</div>}> <div className={'slideButton'} onClick={() => { this.presBoxView?.regularSelect(this.slideDoc, this._itemRef.current!, this._dragRef.current!, true, false); + PresBox.Instance.navigateToActiveItem(); PresBox.Instance.openProperties(); PresBox.Instance.slideToModify = this.Document; }}> diff --git a/src/client/views/nodes/trails/SlideEffect.scss b/src/client/views/nodes/trails/SlideEffect.scss index cadda0ccc..cc851354e 100644 --- a/src/client/views/nodes/trails/SlideEffect.scss +++ b/src/client/views/nodes/trails/SlideEffect.scss @@ -7,10 +7,6 @@ .flip-side { position: absolute; - // max-width: 500px; - // max-height: 500px; - // width: 350px; - // height: 200px; will-change: transform, opacity; backface-visibility: hidden; } @@ -19,6 +15,5 @@ } .flip-back { - // Get the background color of node instead // background-color: rgb(223, 223, 223); } diff --git a/src/client/views/nodes/trails/SlideEffect.tsx b/src/client/views/nodes/trails/SlideEffect.tsx index e04f53f06..db2ac1ea0 100644 --- a/src/client/views/nodes/trails/SlideEffect.tsx +++ b/src/client/views/nodes/trails/SlideEffect.tsx @@ -27,7 +27,10 @@ const infiniteOptions = { delay: 500, }; -// TODO: add visibility detector when the slide comes into view +/** + * This component wraps around the doc to create an effect animation, and also wraps the preview animations + * for the effects as well. + */ export default function SpringAnimation({ doc, dir, friction, tension, mass, presEffect, children, infinite }: Props) { const [springs, api] = useSpring( () => ({ diff --git a/src/client/views/nodes/trails/SlideEffectPreview.tsx b/src/client/views/nodes/trails/SlideEffectPreview.tsx deleted file mode 100644 index d4c16586a..000000000 --- a/src/client/views/nodes/trails/SlideEffectPreview.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { useSpring, animated, easings } from '@react-spring/web'; -import React, { useEffect, useState } from 'react'; - -interface Props { - friction: number; - tension: number; - mass: number; - children: React.ReactNode; -} - -export default function SpringAnimationPreview({ friction, tension, mass, children }: Props) { - const [springs, api] = useSpring( - () => ({ - from: { - x: 0, - y: 0, - opacity: 1, - scale: 1, - }, - config: { - tension: tension, - friction: friction, - mass: mass, - }, - onStart: () => { - // console.log('started'); - }, - onRest: () => { - // console.log('resting'); - }, - }), - [tension, friction, mass] - ); - - // Whether the animation is currently playing - const [animating, setAnimating] = useState(false); - - const bounceConfig = { - from: { - x: -50, - y: 0, - }, - to: [ - { - x: 50, - y: 0, - config: { - tension: tension, - friction: friction, - mass: mass, - }, - }, - { - x: -50, - y: 0, - config: { - duration: 500, - easing: easings.easeInOutCubic, - }, - }, - ], - }; - - const animate = () => { - api.start(bounceConfig); - }; - - useEffect(() => { - animate(); - }, []); - - return ( - <div - style={{ - width: '200px', - height: '150px', - borderRadius: '4px', - border: '1px solid #696969', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - }} - onPointerEnter={() => { - animate(); - }}> - <animated.div - style={{ - ...springs, - }}> - {children} - </animated.div> - </div> - ); -} |