diff --git a/src/components/captions-control/captions-control.tsx b/src/components/captions-control/captions-control.tsx new file mode 100644 index 000000000..9c86aa66b --- /dev/null +++ b/src/components/captions-control/captions-control.tsx @@ -0,0 +1,120 @@ +import {h} from 'preact'; +import {connect, useSelector} from 'react-redux'; +import {ClosedCaptions} from '../closed-captions'; +import {CaptionsMenu} from '../captions-menu'; +import {ButtonControl} from '../button-control'; +import {Tooltip} from '../tooltip'; +import {Button} from '../button'; +import style from '../../styles/style.scss'; +import {Icon, IconType} from '../icon'; +import {SmartContainer} from '../smart-container'; +import {Text, withText} from 'preact-i18n'; +import {useRef, useState, useEffect, useCallback} from 'preact/hooks'; +import {focusElement} from '../../utils'; +import {createPortal} from 'preact/compat'; +import {CVAAOverlay} from '../cvaa-overlay'; + +/** + * mapping state to props + * @param {*} state - redux store state + * @returns {Object} - mapped state to this component + */ +const mapStateToProps = state => ({ + textTracks: state.engine.textTracks, + showCCButton: state.config.showCCButton, + openMenuFromCCCButton: state.config.openMenuFromCCButton, + isMobile: state.shell.isMobile, + isSmallSize: state.shell.isSmallSize, + isCVAAOverlayOpen: state.shell.isCVAAOverlayOpen +}); + +const COMPONENT_NAME = 'CaptionsControl'; + +/** + * CaptionsControl component + * + * @class CaptionsControl + * @example + * @extends {Component} + */ +const CaptionsControl = connect(mapStateToProps)( + withText({ + captionsLabelText: 'captions.captions', + advancedCaptionsSettingsText: 'captions.advanced_captions_settings' + })((props, context) => { + const [smartContainerOpen, setSmartContainerOpen] = useState(false); + const [cvaaOverlay, setCVAAOverlay] = useState(false); + const [ccOn, setCCOn] = useState(false); + const buttonRef = useRef(null); + const controlCaptionsElement = useRef(null); + + const {player} = context; + const {isSmallSize, isMobile, textTracks} = props; + const activeTextTrack = textTracks.find(textTrack => textTrack.active); + + const onControlButtonClick = (e?: KeyboardEvent, byKeyboard?: boolean): void => { + setSmartContainerOpen(smartContainerOpen => !smartContainerOpen); + if (byKeyboard && smartContainerOpen) { + focusElement(buttonRef.current); + } + }; + + const toggleCVAAOverlay = (): void => { + setCVAAOverlay(cvaaOverlay => !cvaaOverlay); + }; + + const onCVAAOverlayClose = (e?: KeyboardEvent, byKeyboard?: boolean): void => { + toggleCVAAOverlay(); + onControlButtonClick(e, byKeyboard); + }; + + const handleClickOutside = (e: any) => { + if (!isMobile && !isSmallSize && !!controlCaptionsElement.current && !controlCaptionsElement.current.contains(e.target)) { + setSmartContainerOpen(false); + } + }; + + useEffect(() => { + document.addEventListener('click', handleClickOutside); + return () => document.removeEventListener('click', handleClickOutside); + }, [isSmallSize, isMobile]); + + useEffect(() => { + setCCOn(activeTextTrack?.language !== 'off'); + }, [activeTextTrack]); + + const shouldRender = !!textTracks?.length && props.showCCButton; + props.onToggle(COMPONENT_NAME, shouldRender); + if (!shouldRender) return undefined; + + const targetId: HTMLDivElement | Document = (document.getElementById(player.config.targetId) as HTMLDivElement) || document; + const portalSelector = `.overlay-portal`; + + return props.openMenuFromCCCButton ? ( + + + + + {smartContainerOpen && !cvaaOverlay && ( + setSmartContainerOpen(false)} title={}> + + + )} + {cvaaOverlay ? createPortal(, targetId.querySelector(portalSelector)!) :
} + + ) : ( + + ); + }) +); + +CaptionsControl.displayName = COMPONENT_NAME; +export {CaptionsControl}; diff --git a/src/components/captions-control/index.ts b/src/components/captions-control/index.ts new file mode 100644 index 000000000..de9a877af --- /dev/null +++ b/src/components/captions-control/index.ts @@ -0,0 +1 @@ +export {CaptionsControl} from './captions-control'; diff --git a/src/components/captions-menu/captions-menu.tsx b/src/components/captions-menu/captions-menu.tsx index 7bde5c866..abc5c6253 100644 --- a/src/components/captions-menu/captions-menu.tsx +++ b/src/components/captions-menu/captions-menu.tsx @@ -10,6 +10,8 @@ import {withEventManager} from '../../event'; import {withLogger} from '../logger'; import {withEventDispatcher} from '../event-dispatcher'; import {withKeyboardEvent} from '../../components/keyboard'; +import {KeyboardEventHandlers} from '../../types'; +import {Menu} from '../menu'; /** * mapping state to props @@ -86,17 +88,21 @@ class CaptionsMenu extends Component { textOptions.push({label: props.advancedCaptionsSettingsText, value: props.advancedCaptionsSettingsText, active: false}); } - return ( - { - props.pushRef(el); - }} - icon={IconType.Captions} - label={this.props.captionsLabelText} - options={textOptions} - onMenuChosen={textTrack => this.onCaptionsChange(textTrack)} - /> - ); + if (this.props.asDropdown) { + return ( + { + props.pushRef(el); + }} + icon={IconType.Captions} + label={this.props.captionsLabelText} + options={textOptions} + onMenuChosen={textTrack => this.onCaptionsChange(textTrack)} + /> + ); + } else { + return this.onCaptionsChange(textTrack)} onClose={() => {}} />; + } } } diff --git a/src/components/closed-captions/closed-captions.tsx b/src/components/closed-captions/closed-captions.tsx index c5181bc7b..e42b6d41f 100644 --- a/src/components/closed-captions/closed-captions.tsx +++ b/src/components/closed-captions/closed-captions.tsx @@ -16,7 +16,6 @@ import {ButtonControl} from '../button-control'; */ const mapStateToProps = state => ({ textTracks: state.engine.textTracks, - showCCButton: state.config.showCCButton }); const COMPONENT_NAME = 'ClosedCaptions'; @@ -44,9 +43,6 @@ const ClosedCaptions = connect(mapStateToProps)( setCCOn(activeTextTrack?.language !== 'off'); }, [activeTextTrack]); - const shouldRender = !!(props.textTracks?.length && props.showCCButton); - props.onToggle(COMPONENT_NAME, shouldRender); - if (!shouldRender) return undefined; return ( {ccOn ? ( diff --git a/src/components/index.ts b/src/components/index.ts index 9baf64ad2..a7fbd2068 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -84,3 +84,4 @@ export {Settings, Settings as SettingsControl}; export {Volume, Volume as VolumeControl}; export {VrStereo, VrStereo as VrStereoControl}; export {ClosedCaptions, ClosedCaptions as ClosedCaptionsControl}; +export {CaptionsControl} from './captions-control'; diff --git a/src/components/settings/settings.tsx b/src/components/settings/settings.tsx index 42f247267..4cce35700 100644 --- a/src/components/settings/settings.tsx +++ b/src/components/settings/settings.tsx @@ -248,7 +248,7 @@ class Settings extends Component { } onClose={this.onControlButtonClick}> {showAdvancedAudioDescToggle && } {showAudioMenu && } - {showCaptionsMenu && } + {showCaptionsMenu && } {showQualityMenu && } {showSpeedMenu && } diff --git a/src/components/smart-container/smart-container-item-type.ts b/src/components/smart-container/smart-container-item-type.ts new file mode 100644 index 000000000..e120ec3e6 --- /dev/null +++ b/src/components/smart-container/smart-container-item-type.ts @@ -0,0 +1,5 @@ +export enum SmartContainerItemType { + DropDown, + Menu, + ToggleSwitch +} diff --git a/src/reducers/config.ts b/src/reducers/config.ts index b857438ff..6adfcc3c9 100644 --- a/src/reducers/config.ts +++ b/src/reducers/config.ts @@ -13,6 +13,7 @@ export const initialState = { targetId: undefined as unknown as string, forceTouchUI: false, showCCButton: true, + openMenuFromCCButton: false, settings: { showAudioMenu: true, showCaptionsMenu: true, diff --git a/src/types/ui-options.ts b/src/types/ui-options.ts index cfe636363..9f1d8c4c9 100644 --- a/src/types/ui-options.ts +++ b/src/types/ui-options.ts @@ -8,6 +8,7 @@ export interface UIOptionsObject { debugActions?: boolean; forceTouchUI?: boolean; showCCButton?: boolean; + openMenuFromCCButton?: boolean; settings?: { showAudioMenu?: boolean; showCaptionsMenu?: boolean; diff --git a/src/ui-presets/playback.tsx b/src/ui-presets/playback.tsx index 79aaa555e..8646e6db2 100644 --- a/src/ui-presets/playback.tsx +++ b/src/ui-presets/playback.tsx @@ -6,6 +6,7 @@ import {PrePlaybackPlayOverlay} from '../components'; import {Loading} from '../components'; import {Rewind} from '../components'; import {Forward} from '../components'; +import {CaptionsControl} from '../components'; import {SeekBarPlaybackContainer} from '../components'; import {Volume} from '../components'; import {Settings} from '../components';