diff --git a/src/common/MetaPreview/ActionButton/ActionButton.js b/src/common/MetaPreview/ActionButton/ActionButton.js index f1120e054..8e2fcd11b 100644 --- a/src/common/MetaPreview/ActionButton/ActionButton.js +++ b/src/common/MetaPreview/ActionButton/ActionButton.js @@ -6,7 +6,7 @@ const classnames = require('classnames'); const { default: Icon } = require('@stremio/stremio-icons/react'); const Button = require('stremio/common/Button'); const styles = require('./styles'); -const { Tooltip } = require('stremio/common/Tooltip'); +const { Tooltip } = require('stremio/common/Tooltips'); const ActionButton = ({ className, icon, label, tooltip, ...props }) => { return ( diff --git a/src/common/Tooltip/TooltipProvider.js b/src/common/Tooltip/TooltipProvider.js deleted file mode 100644 index c19780248..000000000 --- a/src/common/Tooltip/TooltipProvider.js +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) 2017-2023 Smart code 203358507 - -const React = require('react'); -const { useState, createRef } = require('react'); -const PropTypes = require('prop-types'); -const classNames = require('classnames'); -const TooltipContext = require('./TooltipContext'); -const styles = require('./styles'); - -const TooltipProvider = ({ children, className }) => { - const [tooltips, setTooltips] = useState([]); - - const add = ({ id, label, position, margin = 15, parent }) => { - const ref = createRef(null); - - const tooltip = { - ref, - id, - label, - position, - margin: margin, - active: false, - parent, - }; - - setTooltips((tooltips) => ([ - ...tooltips, - tooltip, - ])); - }; - - const remove = (id) => { - setTooltips((tooltips) => ( - tooltips.filter((tooltip) => tooltip.id !== id) - )); - }; - - const toggle = (id, state) => { - setTooltips((tooltips) => ( - tooltips.map((tooltip) => { - if (tooltip.id === id) { - tooltip.active = state; - } - return tooltip; - }) - )); - }; - - const style = (ref, position, margin, active, parent) => { - if (!active) return {}; - - const tooltipHeight = ref.current?.offsetHeight ?? 0; - const tooltipWidth = ref.current?.offsetWidth ?? 0; - const parentBounds = parent.getBoundingClientRect(); - - switch (position) { - case 'top': - return { - top: `${parentBounds.top - tooltipHeight - margin}px`, - left: `${(parentBounds.left + (parentBounds.width / 2)) - (tooltipWidth / 2)}px`, - }; - case 'bottom': - return { - top: `${parentBounds.top + parentBounds.height + margin}px`, - left: `${(parentBounds.left + (parentBounds.width / 2)) - (tooltipWidth / 2)}px`, - }; - case 'left': - return { - top: `${parentBounds.top + (parentBounds.height / 2) - (tooltipHeight / 2)}px`, - left: `${(parentBounds.left - tooltipWidth - margin)}px`, - }; - case 'right': - return { - top: `${parentBounds.top + (parentBounds.height / 2) - (tooltipHeight / 2)}px`, - left: `${(parentBounds.left + parentBounds.width + margin)}px`, - }; - } - }; - - return ( - - { children } -
- { - tooltips.map(({ ref, id, label, position, margin, active, parent }) => ( -
- { label } -
- )) - } -
-
- ); -}; - -TooltipProvider.propTypes = { - children: PropTypes.node, - className: PropTypes.string, -}; - -module.exports = TooltipProvider; diff --git a/src/common/Tooltip/Tooltip.js b/src/common/Tooltips/Tooltip/Tooltip.js similarity index 77% rename from src/common/Tooltip/Tooltip.js rename to src/common/Tooltips/Tooltip/Tooltip.js index 40316c484..7f0c5bdad 100644 --- a/src/common/Tooltip/Tooltip.js +++ b/src/common/Tooltips/Tooltip/Tooltip.js @@ -2,25 +2,35 @@ const React = require('react'); const PropTypes = require('prop-types'); -const useTooltip = require('./useTooltip'); +const useTooltip = require('../useTooltip'); const styles = require('./styles'); const createId = () => (Math.random() + 1).toString(36).substring(7); -const Tooltip = ({ label, position, margin }) => { +const Tooltip = ({ label, position, margin = 15 }) => { const tooltip = useTooltip(); const id = React.useRef(createId()); const element = React.useRef(null); const onMouseEnter = () => { - tooltip.toggle(id.current, true); + tooltip.update(id.current, { + active: true, + }); }; const onMouseLeave = () => { - tooltip.toggle(id.current, false); + tooltip.update(id.current, { + active: false, + }); }; + React.useEffect(() => { + tooltip.update(id.current, { + label, + }); + }, [label]); + React.useLayoutEffect(() => { if (element.current && element.current.parentElement) { const parentElement = element.current.parentElement; @@ -45,15 +55,13 @@ const Tooltip = ({ label, position, margin }) => { tooltip.remove(id.current); } }; - }, [label]); + }, []); return ( -
+
); }; -Tooltip.displayName = 'Tooltip'; - Tooltip.propTypes = { label: PropTypes.string.isRequired, position: PropTypes.string.isRequired, diff --git a/src/common/Tooltips/Tooltip/index.js b/src/common/Tooltips/Tooltip/index.js new file mode 100644 index 000000000..c2972e079 --- /dev/null +++ b/src/common/Tooltips/Tooltip/index.js @@ -0,0 +1,6 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const Tooltip = require('./Tooltip'); + +module.exports = Tooltip; + diff --git a/src/common/Tooltips/Tooltip/styles.less b/src/common/Tooltips/Tooltip/styles.less new file mode 100644 index 000000000..bfe64c47c --- /dev/null +++ b/src/common/Tooltips/Tooltip/styles.less @@ -0,0 +1,11 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +.tooltip-placeholder { + z-index: -1; + visibility: hidden; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} \ No newline at end of file diff --git a/src/common/Tooltip/TooltipContext.js b/src/common/Tooltips/TooltipContext.js similarity index 100% rename from src/common/Tooltip/TooltipContext.js rename to src/common/Tooltips/TooltipContext.js diff --git a/src/common/Tooltips/TooltipItem/TooltipItem.js b/src/common/Tooltips/TooltipItem/TooltipItem.js new file mode 100644 index 000000000..38c6e6455 --- /dev/null +++ b/src/common/Tooltips/TooltipItem/TooltipItem.js @@ -0,0 +1,72 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const React = require('react'); +const PropTypes = require('prop-types'); +const classNames = require('classnames'); +const styles = require('./styles'); + +const TooltipItem = React.memo(({ className, active, label, position, margin, parent }) => { + const ref = React.useRef(null); + const timeout = React.useRef(null); + + const [style, setStyle] = React.useState({}); + + React.useEffect(() => { + clearTimeout(timeout.current); + + timeout.current = setTimeout(() => { + if (active && ref.current) { + const tooltipBounds = ref.current.getBoundingClientRect(); + const parentBounds = parent.getBoundingClientRect(); + + switch (position) { + case 'top': + return setStyle({ + top: `${parentBounds.top - tooltipBounds.height - margin}px`, + left: `${(parentBounds.left + (parentBounds.width / 2)) - (tooltipBounds.width / 2)}px`, + }); + case 'bottom': + return setStyle({ + top: `${parentBounds.top + parentBounds.height + margin}px`, + left: `${(parentBounds.left + (parentBounds.width / 2)) - (tooltipBounds.width / 2)}px`, + }); + case 'left': + return setStyle({ + top: `${parentBounds.top + (parentBounds.height / 2) - (tooltipBounds.height / 2)}px`, + left: `${(parentBounds.left - tooltipBounds.width - margin)}px`, + }); + case 'right': + return setStyle({ + top: `${parentBounds.top + (parentBounds.height / 2) - (tooltipBounds.height / 2)}px`, + left: `${(parentBounds.left + parentBounds.width + margin)}px`, + }); + } + } + }); + + return () => clearTimeout(timeout.current); + }, [active, position, margin, parent, label]); + + return ( +
+ { label } +
+ ); +}); + +TooltipItem.displayName = 'TooltipItem'; + +TooltipItem.propTypes = { + className: PropTypes.string, + active: PropTypes.bool, + label: PropTypes.string, + position: PropTypes.string, + margin: PropTypes.number, + parent: PropTypes.instanceOf(HTMLElement), +}; + +module.exports = TooltipItem; diff --git a/src/common/Tooltips/TooltipItem/index.js b/src/common/Tooltips/TooltipItem/index.js new file mode 100644 index 000000000..04eb64174 --- /dev/null +++ b/src/common/Tooltips/TooltipItem/index.js @@ -0,0 +1,6 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const TooltipItem = require('./TooltipItem'); + +module.exports = TooltipItem; + diff --git a/src/common/Tooltip/styles.less b/src/common/Tooltips/TooltipItem/styles.less similarity index 55% rename from src/common/Tooltip/styles.less rename to src/common/Tooltips/TooltipItem/styles.less index d69825b71..4fcbecbb9 100644 --- a/src/common/Tooltip/styles.less +++ b/src/common/Tooltips/TooltipItem/styles.less @@ -1,16 +1,6 @@ // Copyright (C) 2017-2023 Smart code 203358507 -.tooltip { - z-index: -1; - visibility: hidden; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - -.tooltip-container { +.tooltip-item { position: fixed; visibility: hidden; opacity: 0; diff --git a/src/common/Tooltips/TooltipProvider.js b/src/common/Tooltips/TooltipProvider.js new file mode 100644 index 000000000..b52ed0c26 --- /dev/null +++ b/src/common/Tooltips/TooltipProvider.js @@ -0,0 +1,66 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +const React = require('react'); +const PropTypes = require('prop-types'); +const TooltipContext = require('./TooltipContext'); +const TooltipItem = require('./TooltipItem'); + +const TooltipProvider = ({ children, className }) => { + const [tooltips, setTooltips] = React.useState([]); + + const add = (options) => { + const tooltip = { + ...options, + active: false, + }; + + setTooltips((tooltips) => ([ + ...tooltips, + tooltip, + ])); + }; + + const remove = (id) => { + setTooltips((tooltips) => ( + tooltips.filter((tooltip) => tooltip.id !== id) + )); + }; + + const update = (id, state) => { + setTooltips((tooltips) => ( + tooltips.map((tooltip) => { + if (tooltip.id === id) { + tooltip = { + ...tooltip, + ...state, + }; + } + return tooltip; + }) + )); + }; + + return ( + + { children } +
+ { + tooltips.map(({ id, ...tooltip }) => ( + + )) + } +
+
+ ); +}; + +TooltipProvider.propTypes = { + children: PropTypes.node, + className: PropTypes.string, +}; + +module.exports = TooltipProvider; diff --git a/src/common/Tooltip/index.js b/src/common/Tooltips/index.js similarity index 100% rename from src/common/Tooltip/index.js rename to src/common/Tooltips/index.js diff --git a/src/common/Tooltip/useTooltip.js b/src/common/Tooltips/useTooltip.js similarity index 100% rename from src/common/Tooltip/useTooltip.js rename to src/common/Tooltips/useTooltip.js diff --git a/src/common/index.js b/src/common/index.js index 700fd432b..a173a66d4 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -23,7 +23,7 @@ const SharePrompt = require('./SharePrompt'); const Slider = require('./Slider'); const TextInput = require('./TextInput'); const { ToastProvider, useToast } = require('./Toast'); -const { TooltipProvider, Tooltip } = require('./Tooltip'); +const { TooltipProvider, Tooltip } = require('./Tooltips'); const comparatorWithPriorities = require('./comparatorWithPriorities'); const CONSTANTS = require('./CONSTANTS'); const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');