diff --git a/src/common/Router/Route/RouteFocusableProvider.js b/src/common/Focusable/FocusableProvider.js similarity index 53% rename from src/common/Router/Route/RouteFocusableProvider.js rename to src/common/Focusable/FocusableProvider.js index 9a039d08e..6f3011cc4 100644 --- a/src/common/Router/Route/RouteFocusableProvider.js +++ b/src/common/Focusable/FocusableProvider.js @@ -1,12 +1,13 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { FocusableContext, withModalsContainer } from 'stremio-common'; +import withModalsContainer from '../Modal/withModalsContainer'; +import FocusableContext from './FocusableContext'; -class RouteFocusableProvider extends Component { +class FocusableProvider extends Component { constructor(props) { super(props); - this.routeContentRef = React.createRef(); + this.contentRef = React.createRef(); this.modalsContainerDomTreeObserver = new MutationObserver(this.onModalsContainerDomTreeChange); this.state = { focusable: false @@ -32,32 +33,45 @@ class RouteFocusableProvider extends Component { componentDidUpdate(prevProps, prevState) { if (prevState.focusable && !this.state.focusable) { - const focusedElement = this.routeContentRef.current.querySelector(':focus'); + const focusedElement = this.contentRef.current.querySelector(':focus'); if (focusedElement !== null) { focusedElement.blur(); } } + + if (prevProps.modalsContainer !== this.props.modalsContainer) { + this.onModalsContainerDomTreeChange(); + this.modalsContainerDomTreeObserver.disconnect(); + this.modalsContainerDomTreeObserver.observe(this.props.modalsContainer, { + childList: true + }); + } } onModalsContainerDomTreeChange = () => { - this.setState({ focusable: this.props.modalsContainer.childElementCount === 0 }); + const focusable = this.props.onModalsContainerDomTreeChange({ + modalsContainerElement: this.props.modalsContainer, + contentElement: this.contentRef.current + }); + this.setState({ focusable }); } render() { return ( - {React.cloneElement(React.Children.only(this.props.children), { ref: this.routeContentRef })} + {React.cloneElement(React.Children.only(this.props.children), { ref: this.contentRef })} ); } } -RouteFocusableProvider.propTypes = { - modalsContainer: PropTypes.instanceOf(HTMLElement).isRequired +FocusableProvider.propTypes = { + modalsContainer: PropTypes.instanceOf(HTMLElement).isRequired, + onModalsContainerDomTreeChange: PropTypes.func.isRequired }; -const RouteFocusableProviderWithModalsContainer = withModalsContainer(RouteFocusableProvider); +const FocusableProviderWithModalsContainer = withModalsContainer(FocusableProvider); -RouteFocusableProviderWithModalsContainer.displayName = 'RouteFocusableProviderWithModalsContainer'; +FocusableProviderWithModalsContainer.displayName = 'FocusableProviderWithModalsContainer'; -export default RouteFocusableProviderWithModalsContainer; +export default FocusableProviderWithModalsContainer; diff --git a/src/common/Focusable/index.js b/src/common/Focusable/index.js index b6def29fb..b0fbc3db4 100644 --- a/src/common/Focusable/index.js +++ b/src/common/Focusable/index.js @@ -1,7 +1,7 @@ -import FocusableContext from './FocusableContext'; +import FocusableProvider from './FocusableProvider'; import withFocusable from './withFocusable'; export { - FocusableContext, + FocusableProvider, withFocusable }; diff --git a/src/common/Modal/Modal.js b/src/common/Modal/Modal.js index 233a86544..7dabafc80 100644 --- a/src/common/Modal/Modal.js +++ b/src/common/Modal/Modal.js @@ -1,28 +1,28 @@ -import React, { useRef, useState, useEffect } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import ReactDOM from 'react-dom'; -import { FocusableContext } from 'stremio-common'; +import { FocusableProvider } from 'stremio-common'; import withModalsContainer from './withModalsContainer'; -const Modal = ({ modalsContainer, children }) => { - const modalContainerRef = useRef(null); - const [focusable, setFocusable] = useState(false); - useEffect(() => { - const nextFocusable = modalsContainer.lastElementChild === modalContainerRef.current; - if (nextFocusable !== focusable) { - setFocusable(nextFocusable); - } - }); +const onModalsContainerDomTreeChange = ({ modalsContainerElement, contentElement }) => { + return modalsContainerElement.lastElementChild === contentElement; +}; +const Modal = ({ modalsContainer, children }) => { return ReactDOM.createPortal( - -
+ +
{children}
- , +
, modalsContainer ); }; +Modal.propTypes = { + modalsContainer: PropTypes.instanceOf(HTMLElement).isRequired +}; + const ModalWithModalsContainer = withModalsContainer(Modal); ModalWithModalsContainer.displayName = 'ModalWithModalsContainer'; diff --git a/src/common/Router/Route/Route.js b/src/common/Router/Route/Route.js index 8b5c233a5..cb502d424 100644 --- a/src/common/Router/Route/Route.js +++ b/src/common/Router/Route/Route.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; +import { FocusableProvider } from 'stremio-common'; import ModalsContainerProvider from './ModalsContainerProvider'; -import RouteFocusableProvider from './RouteFocusableProvider'; import styles from './styles'; class Route extends Component { @@ -8,15 +8,19 @@ class Route extends Component { return nextProps.children !== this.props.children; } + onModalsContainerDomTreeChange = ({ modalsContainerElement }) => { + return modalsContainerElement.childElementCount === 0; + }; + render() { return (
- +
{this.props.children}
-
+
); diff --git a/src/common/index.js b/src/common/index.js index da43fea34..19f511a77 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -1,5 +1,5 @@ import Slider from './Slider'; -import { FocusableContext, withFocusable } from './Focusable'; +import { FocusableProvider, withFocusable } from './Focusable'; import Button from './Button'; import Checkbox from './Checkbox'; import TextInput from './TextInput'; @@ -28,7 +28,7 @@ export { ShareAddon, UserPanel, Slider, - FocusableContext, + FocusableProvider, withFocusable, Button };