optimize library headers (#1374)

This commit is contained in:
jeffvli
2025-12-14 02:33:19 -08:00
parent 4cc51c3700
commit b4b106222e
15 changed files with 247 additions and 155 deletions
@@ -26,7 +26,7 @@
grid-template-areas: 'image info';
grid-template-rows: auto;
grid-template-columns: 225px minmax(0, 1fr);
align-items: center;
align-items: flex-end;
justify-items: start;
height: auto;
min-height: 340px;
@@ -98,7 +98,7 @@
.title {
display: flex;
margin: var(--theme-spacing-sm) 0;
font-size: clamp(2rem, 3.5dvw, 3.25rem);
font-size: clamp(1.75rem, 3dvw, 2.75rem);
line-height: 1.2;
}
@@ -7,9 +7,12 @@ import { Link } from 'react-router';
import styles from './library-header.module.css';
import {
PlayLastTextButton,
PlayNextTextButton,
PlayTextButton,
WideShuffleButton,
} from '/@/renderer/features/shared/components/play-button';
import { LONG_PRESS_PLAY_BEHAVIOR } from '/@/renderer/features/shared/components/play-button-group';
import { usePlayButtonClick } from '/@/renderer/features/shared/hooks/use-play-button-click';
import { useIsMutatingCreateFavorite } from '/@/renderer/features/shared/mutations/create-favorite-mutation';
import { useIsMutatingDeleteFavorite } from '/@/renderer/features/shared/mutations/delete-favorite-mutation';
import { useIsMutatingRating } from '/@/renderer/features/shared/mutations/set-rating-mutation';
@@ -20,6 +23,7 @@ import { Image } from '/@/shared/components/image/image';
import { Rating } from '/@/shared/components/rating/rating';
import { Text } from '/@/shared/components/text/text';
import { LibraryItem } from '/@/shared/types/domain-types';
import { Play } from '/@/shared/types/types';
interface LibraryHeaderProps {
children?: ReactNode;
@@ -145,36 +149,36 @@ export const LibraryHeader = forwardRef(
const calculateTitleSize = (title: string) => {
const titleLength = title.length;
let baseSize = '3.5dvw';
let baseSize = '3dvw';
if (titleLength > 20) {
baseSize = '3dvw';
}
if (titleLength > 30) {
baseSize = '2.75dvw';
}
if (titleLength > 40) {
baseSize = '2.5dvw';
}
if (titleLength > 50) {
if (titleLength > 30) {
baseSize = '2.25dvw';
}
if (titleLength > 60) {
if (titleLength > 40) {
baseSize = '2dvw';
}
return `clamp(2rem, ${baseSize}, 3.25rem)`;
if (titleLength > 50) {
baseSize = '1.875dvw';
}
if (titleLength > 60) {
baseSize = '1.75dvw';
}
return `clamp(1.75rem, ${baseSize}, 2.75rem)`;
};
interface LibraryHeaderMenuProps {
favorite?: boolean;
onFavorite?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onMore?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onPlay?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onPlay?: (type: Play) => void;
onRating?: (rating: number) => void;
onShuffle?: (e: React.MouseEvent<HTMLButtonElement>) => void;
rating?: number;
@@ -186,7 +190,6 @@ export const LibraryHeaderMenu = ({
onMore,
onPlay,
onRating,
onShuffle,
rating,
}: LibraryHeaderMenuProps) => {
const isMutatingRating = useIsMutatingRating();
@@ -194,11 +197,43 @@ export const LibraryHeaderMenu = ({
const isMutatingDeleteFavorite = useIsMutatingDeleteFavorite();
const isMutatingFavorite = isMutatingCreateFavorite || isMutatingDeleteFavorite;
const handlePlayNow = usePlayButtonClick({
onClick: () => {
onPlay?.(Play.NOW);
},
onLongPress: () => {
onPlay?.(LONG_PRESS_PLAY_BEHAVIOR[Play.NOW]);
},
});
const handlePlayNext = usePlayButtonClick({
onClick: () => {
onPlay?.(Play.NEXT);
},
onLongPress: () => {
onPlay?.(LONG_PRESS_PLAY_BEHAVIOR[Play.NEXT]);
},
});
const handlePlayLast = usePlayButtonClick({
onClick: () => {
onPlay?.(Play.LAST);
},
onLongPress: () => {
onPlay?.(LONG_PRESS_PLAY_BEHAVIOR[Play.LAST]);
},
});
return (
<div className={styles.libraryHeaderMenu}>
<Group wrap="nowrap">
{onPlay && <PlayTextButton onClick={onPlay} />}
{onShuffle && <WideShuffleButton onClick={onShuffle} />}
{onPlay && <PlayTextButton {...handlePlayNow.handlers} {...handlePlayNow.props} />}
{onPlay && (
<PlayNextTextButton {...handlePlayNext.handlers} {...handlePlayNext.props} />
)}
{onPlay && (
<PlayLastTextButton {...handlePlayLast.handlers} {...handlePlayLast.props} />
)}
</Group>
<Group gap="sm" wrap="nowrap">
{onRating && (
@@ -50,10 +50,26 @@
padding-left: var(--theme-spacing-xl);
background: white;
border-radius: var(--theme-radius-xl);
transition: background-color 0.2s ease-in-out;
transition: background-color 0.2s ease-in-out !important;
&[data-variant='subtle'] {
transition: background-color 0.2s ease-in-out !important;
&:hover,
&:active,
&:focus-visible {
transition: background-color 0.2s ease-in-out !important;
}
}
}
.wide-text-button.unthemed {
transition: background-color 0.2s ease-in-out !important;
&[data-variant='subtle'] {
transition: background-color 0.2s ease-in-out !important;
}
@mixin light {
background: black;
@@ -62,8 +78,11 @@
fill: white;
}
&:hover {
background: lighten(black, 10%);
&[data-variant='subtle']:hover,
&[data-variant='subtle']:active,
&[data-variant='subtle']:focus-visible {
background: lighten(black, 10%) !important;
transition: background-color 0.2s ease-in-out !important;
}
}
@@ -75,8 +94,11 @@
fill: black;
}
&:hover {
background: darken(white, 20%);
&[data-variant='subtle']:hover,
&[data-variant='subtle']:active,
&[data-variant='subtle']:focus-visible {
background: darken(white, 20%) !important;
transition: background-color 0.2s ease-in-out !important;
}
}
}
@@ -90,14 +112,16 @@
color: white;
}
svg {
color: black;
fill: black;
}
}
.no-fill {
fill: none !important;
}
.play-button {
all: unset;
display: flex;
@@ -4,12 +4,14 @@ import { forwardRef, memo } from 'react';
import styles from './play-button.module.css';
import { PlayTooltip } from '/@/renderer/features/shared/components/play-button-group';
import { usePlayButtonClick } from '/@/renderer/features/shared/hooks/use-play-button-click';
import { ActionIcon, ActionIconProps } from '/@/shared/components/action-icon/action-icon';
import { Button, ButtonProps } from '/@/shared/components/button/button';
import { Group } from '/@/shared/components/group/group';
import { AppIcon, Icon } from '/@/shared/components/icon/icon';
import { Spinner } from '/@/shared/components/spinner/spinner';
import { Play } from '/@/shared/types/types';
export interface DefaultPlayButtonProps extends ActionIconProps {
size?: number | string;
@@ -36,14 +38,18 @@ export const DefaultPlayButton = forwardRef<HTMLButtonElement, DefaultPlayButton
DefaultPlayButton.displayName = 'DefaultPlayButton';
interface TextPlayButtonProps extends ButtonProps {}
interface TextPlayButtonProps extends ButtonProps {
onLongPress?: (e: React.MouseEvent<HTMLButtonElement>) => void;
showTooltip?: boolean;
}
export const PlayTextButton = ({
className,
showTooltip = true,
variant = 'default',
...props
}: TextPlayButtonProps) => {
return (
const button = (
<Button
className={clsx(styles.wideTextButton, className, {
[styles.unthemed]: variant !== 'filled',
@@ -57,12 +63,64 @@ export const PlayTextButton = ({
>
{props.children || (
<Group gap="sm" wrap="nowrap">
<Icon fill="default" icon="mediaPlay" size="lg" />
<Icon icon="mediaPlay" size="lg" />
{t('player.play', { postProcess: 'sentenceCase' })}
</Group>
)}
</Button>
);
const hasLongPress = Boolean(
props.onLongPress || (props as any).onMouseDown || (props as any).onTouchStart,
);
if (hasLongPress && showTooltip) {
return <PlayTooltip type={Play.NOW}>{button}</PlayTooltip>;
}
return button;
};
export const PlayNextTextButton = ({ ...props }: TextPlayButtonProps) => {
const button = (
<PlayTextButton {...props} showTooltip={false}>
<Group gap="sm" wrap="nowrap">
<Icon className={styles.noFill} icon="mediaPlayNext" size="lg" />
{t('player.addNext', { postProcess: 'sentenceCase' })}
</Group>
</PlayTextButton>
);
const hasLongPress = Boolean(
props.onLongPress || (props as any).onMouseDown || (props as any).onTouchStart,
);
if (hasLongPress) {
return <PlayTooltip type={Play.NEXT}>{button}</PlayTooltip>;
}
return button;
};
export const PlayLastTextButton = ({ ...props }: TextPlayButtonProps) => {
const button = (
<PlayTextButton {...props} showTooltip={false}>
<Group gap="sm" wrap="nowrap">
<Icon className={styles.noFill} icon="mediaPlayLast" size="lg" />
{t('player.addLast', { postProcess: 'sentenceCase' })}
</Group>
</PlayTextButton>
);
const hasLongPress = Boolean(
props.onLongPress || (props as any).onMouseDown || (props as any).onTouchStart,
);
if (hasLongPress) {
return <PlayTooltip type={Play.LAST}>{button}</PlayTooltip>;
}
return button;
};
export const WideShuffleButton = ({ ...props }: TextPlayButtonProps) => {