fix mouseover state causing rerender in fullscreen player (#1535)

This commit is contained in:
jeffvli
2026-01-17 03:02:05 -08:00
parent b4b0c6cedd
commit 3f3540dd2b
4 changed files with 36 additions and 31 deletions
@@ -91,7 +91,7 @@ export const FullScreenPlayerQueue = () => {
pos="relative" pos="relative"
size="lg" size="lg"
uppercase uppercase
variant="subtle" variant="transparent"
> >
{item.label} {item.label}
</Button> </Button>
@@ -10,8 +10,31 @@
@media screen and (orientation: portrait) { @media screen and (orientation: portrait) {
padding: 2rem 2rem 1rem; padding: 2rem 2rem 1rem;
} }
&:hover .controls-container {
[data-variant='subtle'] {
color: var(--theme-colors-foreground);
background: var(--theme-colors-surface);
border: 1px solid transparent;
&:hover,
&:active,
&:focus-visible {
@mixin dark {
background: lighten(var(--theme-colors-surface), 5%);
}
@mixin light {
background: lighten(var(--theme-colors-surface), 5%);
}
}
}
}
} }
.responsive-container { .responsive-container {
display: grid; display: grid;
grid-template-rows: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr);
@@ -220,11 +220,7 @@ const BackgroundImageOverlay = memo(
BackgroundImageOverlay.displayName = 'BackgroundImageOverlay'; BackgroundImageOverlay.displayName = 'BackgroundImageOverlay';
interface ControlsProps { const Controls = () => {
isPageHovered: boolean;
}
const Controls = ({ isPageHovered }: ControlsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { const {
dynamicBackground, dynamicBackground,
@@ -271,6 +267,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
return ( return (
<Group <Group
className={styles.controlsContainer}
gap="sm" gap="sm"
p="1rem" p="1rem"
pos="absolute" pos="absolute"
@@ -285,7 +282,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
iconProps={{ size: 'lg' }} iconProps={{ size: 'lg' }}
onClick={handleToggleFullScreenPlayer} onClick={handleToggleFullScreenPlayer}
tooltip={{ label: t('common.minimize', { postProcess: 'titleCase' }) }} tooltip={{ label: t('common.minimize', { postProcess: 'titleCase' }) }}
variant={isPageHovered ? 'default' : 'subtle'} variant="subtle"
/> />
<Popover position="bottom-start"> <Popover position="bottom-start">
<Popover.Target> <Popover.Target>
@@ -293,7 +290,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
icon="settings2" icon="settings2"
iconProps={{ size: 'lg' }} iconProps={{ size: 'lg' }}
tooltip={{ label: t('common.configure', { postProcess: 'titleCase' }) }} tooltip={{ label: t('common.configure', { postProcess: 'titleCase' }) }}
variant={isPageHovered ? 'default' : 'subtle'} variant="subtle"
/> />
</Popover.Target> </Popover.Target>
<Popover.Dropdown> <Popover.Dropdown>
@@ -556,7 +553,7 @@ const Controls = ({ isPageHovered }: ControlsProps) => {
</Popover> </Popover>
<ListConfigMenu <ListConfigMenu
buttonProps={{ buttonProps={{
variant: isPageHovered ? 'default' : 'subtle', variant: 'subtle',
}} }}
displayTypes={[{ hidden: true, value: ListDisplayType.GRID }]} displayTypes={[{ hidden: true, value: ListDisplayType.GRID }]}
listKey={ItemListKey.FULL_SCREEN} listKey={ItemListKey.FULL_SCREEN}
@@ -616,20 +613,11 @@ interface PlayerContainerProps {
children: ReactNode; children: ReactNode;
dynamicBackground: boolean | undefined; dynamicBackground: boolean | undefined;
dynamicIsImage: boolean | undefined; dynamicIsImage: boolean | undefined;
onMouseEnter: () => void;
onMouseLeave: () => void;
windowBarStyle: Platform; windowBarStyle: Platform;
} }
const PlayerContainer = memo( const PlayerContainer = memo(
({ ({ children, dynamicBackground, dynamicIsImage, windowBarStyle }: PlayerContainerProps) => {
children,
dynamicBackground,
dynamicIsImage,
onMouseEnter,
onMouseLeave,
windowBarStyle,
}: PlayerContainerProps) => {
const currentSong = usePlayerSong(); const currentSong = usePlayerSong();
const imageUrl = useItemImageUrl({ const imageUrl = useItemImageUrl({
id: currentSong?.imageId || undefined, id: currentSong?.imageId || undefined,
@@ -650,8 +638,6 @@ const PlayerContainer = memo(
custom={{ background, dynamicBackground, windowBarStyle }} custom={{ background, dynamicBackground, windowBarStyle }}
exit="closed" exit="closed"
initial="closed" initial="closed"
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
transition={{ duration: 2 }} transition={{ duration: 2 }}
variants={containerVariants} variants={containerVariants}
> >
@@ -672,8 +658,6 @@ export const FullScreenPlayer = () => {
const { setStore } = useFullScreenPlayerStoreActions(); const { setStore } = useFullScreenPlayerStoreActions();
const { windowBarStyle } = useWindowSettings(); const { windowBarStyle } = useWindowSettings();
const [isPageHovered, setIsPageHovered] = useState(false);
const location = useLocation(); const location = useLocation();
const isOpenedRef = useRef<boolean | null>(null); const isOpenedRef = useRef<boolean | null>(null);
@@ -689,11 +673,9 @@ export const FullScreenPlayer = () => {
<PlayerContainer <PlayerContainer
dynamicBackground={dynamicBackground} dynamicBackground={dynamicBackground}
dynamicIsImage={dynamicIsImage} dynamicIsImage={dynamicIsImage}
onMouseEnter={() => setIsPageHovered(true)}
onMouseLeave={() => setIsPageHovered(false)}
windowBarStyle={windowBarStyle} windowBarStyle={windowBarStyle}
> >
<Controls isPageHovered={isPageHovered} /> <Controls />
<BackgroundImageOverlay <BackgroundImageOverlay
dynamicBackground={dynamicBackground} dynamicBackground={dynamicBackground}
dynamicImageBlur={dynamicImageBlur} dynamicImageBlur={dynamicImageBlur}
+5 -5
View File
@@ -1,4 +1,4 @@
import { memo, ReactNode, useMemo } from 'react'; import { ReactNode, useMemo } from 'react';
import styles from './option.module.css'; import styles from './option.module.css';
@@ -12,7 +12,7 @@ interface OptionProps extends GroupProps {
const defaultClassNames = { root: styles.root }; const defaultClassNames = { root: styles.root };
export const Option = memo(({ children, classNames, ...props }: OptionProps) => { export const Option = ({ children, classNames, ...props }: OptionProps) => {
const mergedClassNames = useMemo( const mergedClassNames = useMemo(
() => (classNames ? { ...defaultClassNames, ...classNames } : defaultClassNames), () => (classNames ? { ...defaultClassNames, ...classNames } : defaultClassNames),
[classNames], [classNames],
@@ -23,7 +23,7 @@ export const Option = memo(({ children, classNames, ...props }: OptionProps) =>
{children} {children}
</Group> </Group>
); );
}); };
Option.displayName = 'Option'; Option.displayName = 'Option';
@@ -43,5 +43,5 @@ const Control = ({ children }: ControlProps) => {
return <Flex justify="flex-end">{children}</Flex>; return <Flex justify="flex-end">{children}</Flex>;
}; };
(Option as typeof Option & { Label: typeof Label }).Label = Label; Option.Label = Label;
(Option as typeof Option & { Control: typeof Control }).Control = Control; Option.Control = Control;