redesign PlayButtonGroup to match card styles

This commit is contained in:
jeffvli
2025-11-29 04:02:47 -08:00
parent a99a02c94c
commit 93ba99e36f
4 changed files with 61 additions and 112 deletions
@@ -12,7 +12,7 @@ import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { useGenreList } from '/@/renderer/features/genres/api/genres-api';
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { PlayButton } from '/@/renderer/features/shared/components/play-button';
import { PlayButtonGroup } from '/@/renderer/features/shared/components/play-button-group';
import { useCurrentServer } from '/@/renderer/store';
import { Checkbox } from '/@/shared/components/checkbox/checkbox';
import { Divider } from '/@/shared/components/divider/divider';
@@ -101,16 +101,6 @@ export const ShuffleAllContextModal = () => {
closeAllModals();
};
const isLoadingNext =
isFetching &&
(fetchTypeRef.current === Play.NEXT || fetchTypeRef.current === Play.NEXT_SHUFFLE);
const isLoadingLast =
isFetching &&
(fetchTypeRef.current === Play.LAST || fetchTypeRef.current === Play.LAST_SHUFFLE);
const isLoadingNow = isFetching && fetchTypeRef.current === Play.NOW;
return (
<Stack gap="md">
<NumberInput
@@ -166,60 +156,10 @@ export const ShuffleAllContextModal = () => {
/>
)}
<Divider />
<Group align="center" gap="md" justify="center" w="100%">
<PlayButton
icon="mediaPlayNext"
isSecondary
loading={isLoadingNext}
onClick={() => handlePlay(Play.NEXT)}
onLongPress={() => handlePlay(Play.NEXT_SHUFFLE)}
/>
<PlayButton
fill
loading={isLoadingNow}
onClick={() => handlePlay(Play.NOW)}
onLongPress={() => handlePlay(Play.SHUFFLE)}
/>
<PlayButton
icon="mediaPlayLast"
isSecondary
loading={isLoadingLast}
onClick={() => handlePlay(Play.LAST)}
onLongPress={() => handlePlay(Play.LAST_SHUFFLE)}
/>
</Group>
{/* <Group grow>
<Button
disabled={!limit || isFetching}
leftSection={<Icon icon="mediaPlayNext" />}
loading={fetchTypeRef.current === Play.NEXT && isFetching}
onClick={() => handlePlay(Play.NEXT)}
type="submit"
variant="default"
>
{t('player.addNext', { postProcess: 'sentenceCase' })}
</Button>
<Button
disabled={!limit || isFetching}
leftSection={<Icon icon="mediaPlayLast" />}
loading={fetchTypeRef.current === Play.LAST && isFetching}
onClick={() => handlePlay(Play.LAST)}
type="submit"
variant="default"
>
{t('player.addLast', { postProcess: 'sentenceCase' })}
</Button>
</Group>
<Button
disabled={!limit || isFetching}
leftSection={<Icon icon="mediaPlay" />}
loading={fetchTypeRef.current === Play.NOW && isFetching}
onClick={() => handlePlay(Play.NOW)}
type="submit"
variant="filled"
>
{t('player.play', { postProcess: 'sentenceCase' })}
</Button> */}
<PlayButtonGroup
loading={(isFetching && fetchTypeRef.current) || false}
onPlay={handlePlay}
/>
</Stack>
);
};
@@ -1,6 +1,5 @@
import { closeAllModals, openModal } from '@mantine/modals';
import { CSSProperties, memo, ReactNode, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import styles from './library-header-bar.module.css';
@@ -53,7 +52,6 @@ const HeaderPlayButton = ({
...props
}: HeaderPlayButtonProps) => {
const serverId = useCurrentServerId();
const { t } = useTranslation();
const player = usePlayer();
const handlePlay = useCallback(
@@ -77,9 +75,16 @@ const HeaderPlayButton = ({
openModal({
children: <PlayButtonGroup onPlay={handlePlay} />,
size: 'xs',
title: t('player.play', { postProcess: 'titleCase' }),
styles: {
body: {
padding: 'var(--theme-spacing-md)',
},
header: {
display: 'none',
},
},
});
}, [serverId, handlePlay, t]);
}, [serverId, handlePlay]);
return (
<div className={styles.playButtonContainer}>
@@ -1,58 +1,56 @@
import i18n from '/@/i18n/i18n';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { PlayButton } from '/@/renderer/features/shared/components/play-button';
import { Group } from '/@/shared/components/group/group';
import { AppIconSelection } from '/@/shared/components/icon/icon';
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
import { Play } from '/@/shared/types/types';
const playButtons: { icon: AppIconSelection; label: string; type: Play }[] = [
{
icon: 'mediaPlay',
label: i18n.t('player.play', { postProcess: 'sentenceCase' }),
type: Play.NOW,
},
const playButtons: { icon: AppIconSelection; label: string; secondary: boolean; type: Play }[] = [
{
icon: 'mediaPlayNext',
label: i18n.t('player.addNext', { postProcess: 'sentenceCase' }),
secondary: true,
type: Play.NEXT,
},
{
icon: 'mediaPlay',
label: i18n.t('player.play', { postProcess: 'sentenceCase' }),
secondary: false,
type: Play.NOW,
},
{
icon: 'mediaPlayLast',
label: i18n.t('player.addLast', { postProcess: 'sentenceCase' }),
secondary: true,
type: Play.LAST,
},
{
icon: 'mediaShuffle',
label: i18n.t('player.shuffle', { postProcess: 'sentenceCase' }),
type: Play.SHUFFLE,
},
];
const LONG_PRESS_PLAY_BEHAVIOR = {
[Play.LAST]: Play.LAST_SHUFFLE,
[Play.NEXT]: Play.NEXT_SHUFFLE,
[Play.NOW]: Play.SHUFFLE,
};
interface PlayButtonGroupProps {
loading?: boolean | Play;
onPlay: (type: Play) => void;
}
export const PlayButtonGroup = ({ onPlay }: PlayButtonGroupProps) => {
export const PlayButtonGroup = ({ loading, onPlay }: PlayButtonGroupProps) => {
return (
<Group grow>
<Group align="center" gap="md" justify="center">
{playButtons.map((button) => (
<ActionIcon
icon={button.icon}
iconProps={{
size: 'xl',
}}
key={button.type}
onClick={() => onPlay(button.type)}
styles={{
root: {
padding: 'var(--mantine-spacing-lg)',
},
}}
tooltip={{
label: button.label,
openDelay: 0,
}}
variant="default"
/>
<Tooltip key={button.type} label={button.label} openDelay={2000}>
<PlayButton
fill={button.type === Play.NOW}
icon={button.icon}
isSecondary={button.secondary}
loading={loading === button.type}
onClick={() => onPlay(button.type)}
onLongPress={() => onPlay(LONG_PRESS_PLAY_BEHAVIOR[button.type])}
/>
</Tooltip>
))}
</Group>
);
@@ -1,6 +1,6 @@
import clsx from 'clsx';
import { t } from 'i18next';
import { memo } from 'react';
import { forwardRef, memo } from 'react';
import styles from './play-button.module.css';
@@ -85,16 +85,19 @@ interface PlayButtonProps {
onLongPress?: (e: React.MouseEvent<HTMLButtonElement>) => void;
}
export const PlayButton = memo(
({
classNames,
fill,
icon = 'mediaPlay',
isSecondary,
loading,
onClick,
onLongPress,
}: PlayButtonProps) => {
const PlayButtonBase = forwardRef<HTMLButtonElement, PlayButtonProps>(
(
{
classNames,
fill,
icon = 'mediaPlay',
isSecondary,
loading,
onClick,
onLongPress,
}: PlayButtonProps,
ref,
) => {
const clickHandlers = usePlayButtonClick({
loading,
onClick,
@@ -107,6 +110,7 @@ export const PlayButton = memo(
[styles.fill]: fill,
[styles.secondary]: isSecondary,
})}
ref={ref}
{...clickHandlers.handlers}
{...clickHandlers.props}
>
@@ -116,4 +120,6 @@ export const PlayButton = memo(
},
);
export const PlayButton = memo(PlayButtonBase);
PlayButton.displayName = 'PlayButton';