diff options
Diffstat (limited to 'src/client/views/nodes/trails/PresBox.tsx')
| -rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 475 |
1 files changed, 261 insertions, 214 deletions
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3919ac3a8..5dbf502d2 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -100,8 +100,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @observable _presKeyEvents: boolean = false; @observable _forceKeyEvents: boolean = false; + @observable _showAIGallery = false; @observable _showBezierEditor = false; - @observable _showSpringEditor = true; + @observable _showSpringEditor = false; @observable _showPreview = true; @observable _easeDropdownVal = 'ease'; @@ -151,6 +152,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { setIsRecording = action((input: boolean) => { this._isRecording = input; }); // prettier-ignore setBezierEditorVisibility = action((visible: boolean) => { this._showBezierEditor = visible; }); // prettier-ignore setSpringEditorVisibility = action((visible: boolean) => { this._showSpringEditor = visible; }); // 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]})`); }); @@ -1735,19 +1737,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { 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 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; @@ -1758,20 +1755,32 @@ 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" + ref={r => { + setTimeout(() => { + if (r && !r.textContent) { + r.style.height = ''; + r.style.height = r.scrollHeight + 'px'; + } + }); + }} value={this._chatInput} - onChange={e => this.setChatInput(e.target.value)} + onChange={e => { + e.currentTarget.style.height = ''; + e.currentTarget.style.height = e.currentTarget.scrollHeight + 'px'; + this.setChatInput(e.target.value); + }} onKeyDown={e => { this.stopDictation(); e.stopPropagation(); @@ -1805,6 +1814,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { /> </div> </div> + {/* Movement */} <div className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presentation_status === PresStatus.Edit ? 'active' : ''}`} @@ -1816,46 +1826,47 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { this._openEffectDropdown = false; this._openBulletEffectDropdown = false; })}> - <div - className="presBox-option-block" - // style={{ padding: '16px' }} - > - Movement + <div className="presBox-option-block"> <Dropdown color={SnappingManager.userColor} - formLabel="Movement" + formLabel="Navigation" + 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 style={{ maxWidth: 25 }} className="presBox-input" readOnly 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 className="ribbon-doubleButton"> + <div className="presBox-subheading">ZOOM/EFFECT TIME</div> + <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 style={{ maxWidth: 25 }} className="presBox-input" type="number" readOnly value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.updateTransitionTime(e.target.value))} /> + <span>s</span> </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" + formLabel="Timing Function" + formLabelPlacement="left" closeOnSelect items={easeItems} selectedVal={this.activeItem.presentation_easeFunc ? (StrCast(this.activeItem.presentation_easeFunc).startsWith('cubic') ? 'custom' : StrCast(this.activeItem.presentation_easeFunc)) : 'ease'} @@ -1880,32 +1891,75 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { e.stopPropagation(); this.setBezierEditorVisibility(!this._showBezierEditor); }}> - {`${this._showBezierEditor ? 'Hide' : 'Show'} Timing Editor`} - <FontAwesomeIcon icon={this._showBezierEditor ? 'chevron-up' : 'chevron-down'} /> + TIMING EDITOR + <FontAwesomeIcon icon={this._showBezierEditor ? 'chevron-down' : 'chevron-up'} /> </div> </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> + <div className="presBox-option-block" style={{ paddingTop: 0, alignItems: 'center' }}> <CubicBezierEditor setFunc={this.setBezierControlPoints} currPoints={this.currCPoints} /> </div> )} + {effect === PresEffect.None ? 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" + /> + <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> + )} + {/* 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"> + <div className="presBox-gpt-chat" style={{ display: SnappingManager.PropertiesWidth < 1 ? 'none' : undefined }}> + {/* Custom */} + <div + className="presBox-show-hide-dropdown" + style={{ alignSelf: 'flex-start' }} + onClick={e => { + e.stopPropagation(); + this.setShowAIGalleryVisibilty(!this._showAIGallery); + }}> + AI EFFECTS + <FontAwesomeIcon icon={this._showAIGallery ? 'chevron-down' : 'chevron-up'} /> + </div> + <div className="pres-chat" style={{ display: this._showAIGallery ? undefined : 'none' }}> + <div className="pres-chatbox-container-ai"> <ReactTextareaAutosize - placeholder="Customize prompt for effect suggestions. Leave blank for random results." + 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 => { @@ -1924,6 +1978,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { onClick={this.customizeAnimations} /> </div> + <div style={{ display: this._showAIGallery ? 'flex' : 'none', 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> <div @@ -1937,171 +2016,139 @@ 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 - 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]); - }} - 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> + <Dropdown + color={SnappingManager.userColor} + formLabel="Slide Effect" + 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> + <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">EFFECT 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.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> + </div> </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 + </> + )} + {effect !== PresEffect.Lightspeed && ( + <> + <Dropdown + color={SnappingManager.userColor} + formLabel="Effect Timing" + 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} - 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()}> - {/* 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>Damping</div> - <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>Mass</div> - <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 style={{ display: SnappingManager.PropertiesWidth < 1 ? 'none' : undefined }}> + {/* Custom */} + <div + className="presBox-show-hide-dropdown" + style={{ display: effect !== PresEffect.None ? undefined : 'none' }} + onClick={e => { + e.stopPropagation(); + this.setSpringEditorVisibility(!this._showSpringEditor); + }}> + SPRING SETTINGS + <FontAwesomeIcon icon={this._showSpringEditor ? 'chevron-down' : 'chevron-up'} /> </div> - <div style={{ display: 'flex', alignItems: 'center'}}> - <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 of: ' + effect + ', ' + direction} - /> - <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> - )} + {/* Spring settings */} + {/* No spring settings for jackinthebox (lightspeed) */} + {this._showSpringEditor && 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> - </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> {/* Toggles */} |
