mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
commit
97803321d5
9 changed files with 244 additions and 8 deletions
|
|
@ -6,7 +6,7 @@ const { useTranslation } = require('react-i18next');
|
|||
const { Router } = require('stremio-router');
|
||||
const { Core, Shell, Chromecast, DragAndDrop, KeyboardShortcuts, ServicesProvider } = require('stremio/services');
|
||||
const { NotFound } = require('stremio/routes');
|
||||
const { ToastProvider, CONSTANTS, withCoreSuspender } = require('stremio/common');
|
||||
const { ToastProvider, TooltipProvider, CONSTANTS, withCoreSuspender } = require('stremio/common');
|
||||
const ServicesToaster = require('./ServicesToaster');
|
||||
const DeepLinkHandler = require('./DeepLinkHandler');
|
||||
const ErrorDialog = require('./ErrorDialog');
|
||||
|
|
@ -159,13 +159,15 @@ const App = () => {
|
|||
<ErrorDialog className={styles['error-container']} />
|
||||
:
|
||||
<ToastProvider className={styles['toasts-container']}>
|
||||
<ServicesToaster />
|
||||
<DeepLinkHandler />
|
||||
<RouterWithProtectedRoutes
|
||||
className={styles['router']}
|
||||
viewsConfig={routerViewsConfig}
|
||||
onPathNotMatch={onPathNotMatch}
|
||||
/>
|
||||
<TooltipProvider className={styles['tooltip-container']}>
|
||||
<ServicesToaster />
|
||||
<DeepLinkHandler />
|
||||
<RouterWithProtectedRoutes
|
||||
className={styles['router']}
|
||||
viewsConfig={routerViewsConfig}
|
||||
onPathNotMatch={onPathNotMatch}
|
||||
/>
|
||||
</TooltipProvider>
|
||||
</ToastProvider>
|
||||
:
|
||||
<div className={styles['loader-container']} />
|
||||
|
|
|
|||
|
|
@ -116,6 +116,19 @@ html {
|
|||
}
|
||||
}
|
||||
|
||||
.tooltip-container {
|
||||
height: 2.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 1.5rem;
|
||||
font-size: 1rem;
|
||||
color: var(--primary-foreground-color);
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--modal-background-color);
|
||||
transition: opacity 0.25s ease-out;
|
||||
}
|
||||
|
||||
.router {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
63
src/common/Tooltip/Tooltip.js
Normal file
63
src/common/Tooltip/Tooltip.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
const useTooltip = require('./useTooltip');
|
||||
const styles = require('./styles');
|
||||
|
||||
const createId = () => (Math.random() + 1).toString(36).substring(7);
|
||||
|
||||
const Tooltip = ({ label, position, margin }) => {
|
||||
const tooltip = useTooltip();
|
||||
|
||||
const id = React.useRef(createId());
|
||||
const element = React.useRef(null);
|
||||
|
||||
const onMouseEnter = () => {
|
||||
tooltip.toggle(id.current, true);
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
tooltip.toggle(id.current, false);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (element.current && element.current.parentElement) {
|
||||
const parentElement = element.current.parentElement;
|
||||
tooltip.add({
|
||||
id: id.current,
|
||||
label,
|
||||
position,
|
||||
margin,
|
||||
parent: parentElement,
|
||||
});
|
||||
|
||||
parentElement.addEventListener('mouseenter', onMouseEnter);
|
||||
parentElement.addEventListener('mouseleave', onMouseLeave);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (element.current && element.current.parentElement) {
|
||||
const parentElement = element.current.parentElement;
|
||||
parentElement.removeEventListener('mouseenter', onMouseEnter);
|
||||
parentElement.removeEventListener('mouseleave', onMouseLeave);
|
||||
|
||||
tooltip.remove(id.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={element} className={styles['tooltip']} />
|
||||
);
|
||||
};
|
||||
|
||||
Tooltip.displayName = 'Tooltip';
|
||||
|
||||
Tooltip.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
position: PropTypes.string.isRequired,
|
||||
margin: PropTypes.number,
|
||||
};
|
||||
|
||||
module.exports = Tooltip;
|
||||
8
src/common/Tooltip/TooltipContext.js
Normal file
8
src/common/Tooltip/TooltipContext.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const { createContext } = require('react');
|
||||
|
||||
const TooltipContext = createContext(null);
|
||||
|
||||
module.exports = TooltipContext;
|
||||
|
||||
106
src/common/Tooltip/TooltipProvider.js
Normal file
106
src/common/Tooltip/TooltipProvider.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// 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 (
|
||||
<TooltipContext.Provider value={{ add, remove, toggle }}>
|
||||
{ children }
|
||||
<div className={'tooltips-container'}>
|
||||
{
|
||||
tooltips.map(({ ref, id, label, position, margin, active, parent }) => (
|
||||
<div
|
||||
key={id}
|
||||
ref={ref}
|
||||
className={classNames(className, styles['tooltip-container'], { 'active': active })}
|
||||
style={style(ref, position, margin, active, parent)}
|
||||
>
|
||||
{ label }
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</TooltipContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
TooltipProvider.propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
module.exports = TooltipProvider;
|
||||
9
src/common/Tooltip/index.js
Normal file
9
src/common/Tooltip/index.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const TooltipProvider = require('./TooltipProvider');
|
||||
const Tooltip = require('./Tooltip');
|
||||
|
||||
module.exports = {
|
||||
TooltipProvider,
|
||||
Tooltip,
|
||||
};
|
||||
22
src/common/Tooltip/styles.less
Normal file
22
src/common/Tooltip/styles.less
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// 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 {
|
||||
position: fixed;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
|
||||
&:global(.active) {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
10
src/common/Tooltip/useTooltip.js
Normal file
10
src/common/Tooltip/useTooltip.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const React = require('react');
|
||||
const TooltipContext = require('./TooltipContext');
|
||||
|
||||
const useTooltip = () => {
|
||||
return React.useContext(TooltipContext);
|
||||
};
|
||||
|
||||
module.exports = useTooltip;
|
||||
|
|
@ -23,6 +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 comparatorWithPriorities = require('./comparatorWithPriorities');
|
||||
const CONSTANTS = require('./CONSTANTS');
|
||||
const { withCoreSuspender, useCoreSuspender } = require('./CoreSuspender');
|
||||
|
|
@ -70,6 +71,8 @@ module.exports = {
|
|||
TextInput,
|
||||
ToastProvider,
|
||||
useToast,
|
||||
TooltipProvider,
|
||||
Tooltip,
|
||||
comparatorWithPriorities,
|
||||
CONSTANTS,
|
||||
withCoreSuspender,
|
||||
|
|
|
|||
Loading…
Reference in a new issue