extract play button from item card and add long press animation

This commit is contained in:
jeffvli
2025-11-25 16:20:44 -08:00
parent 8ad5e26c2f
commit 2264fa0d29
10 changed files with 296 additions and 226 deletions
@@ -0,0 +1,121 @@
import { useCallback, useMemo, useState } from 'react';
import { useIsPlayerFetching } from '/@/renderer/features/player/context/player-context';
import { useLongPress } from '/@/shared/hooks/use-long-press';
interface UsePlayButtonClickOptions {
disabled?: boolean;
loading?: boolean;
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onLongPress?: (e: React.MouseEvent<HTMLButtonElement>) => void;
}
interface UsePlayButtonClickReturn {
handlers: {
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
onMouseDown: (e: React.MouseEvent<HTMLButtonElement>) => void;
onMouseLeave: (e: React.MouseEvent) => void;
onMouseUp: (e: React.MouseEvent) => void;
onTouchCancel: (e: React.TouchEvent) => void;
onTouchEnd: (e: React.TouchEvent) => void;
onTouchStart: (e: React.TouchEvent) => void;
};
props: {
'data-pressing'?: string;
disabled: boolean;
style: React.CSSProperties;
};
}
export const usePlayButtonClick = ({
loading,
onClick,
onLongPress,
}: UsePlayButtonClickOptions): UsePlayButtonClickReturn => {
const isPlayerFetching = useIsPlayerFetching();
const isDisabled = Boolean(isPlayerFetching || loading);
const [isPressing, setIsPressing] = useState(false);
const [isLongPressing, setIsLongPressing] = useState(false);
const longPressHandlers = useLongPress<HTMLButtonElement>({
onClick: (e) => {
if (isDisabled || loading) {
return;
}
e.stopPropagation();
e.preventDefault();
onClick?.(e as React.MouseEvent<HTMLButtonElement>);
},
onFinish: () => {
setIsPressing(false);
setIsLongPressing(false);
},
onLongPress: (e) => {
if (isDisabled || loading) {
return;
}
e.stopPropagation();
e.preventDefault();
setIsPressing(false);
setIsLongPressing(true);
onLongPress?.(e as React.MouseEvent<HTMLButtonElement>);
},
onStart: () => {
if (!isDisabled && !loading) {
setIsPressing(true);
setIsLongPressing(false);
}
},
});
const handleMouseDown = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
longPressHandlers.onMouseDown?.(e);
},
[longPressHandlers],
);
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
e.preventDefault();
}, []);
const handlers = useMemo(
() => ({
onClick: handleClick,
onMouseDown: handleMouseDown,
onMouseLeave: longPressHandlers.onMouseLeave,
onMouseUp: longPressHandlers.onMouseUp,
onTouchCancel: longPressHandlers.onTouchCancel,
onTouchEnd: longPressHandlers.onTouchEnd,
onTouchStart: longPressHandlers.onTouchStart,
}),
[
handleClick,
handleMouseDown,
longPressHandlers.onMouseLeave,
longPressHandlers.onMouseUp,
longPressHandlers.onTouchCancel,
longPressHandlers.onTouchEnd,
longPressHandlers.onTouchStart,
],
);
const props = useMemo(() => {
return {
'data-pressing': isPressing ? 'true' : undefined,
disabled: isDisabled,
style: {
'--long-press-duration': '300ms',
'--play-button-scale': isLongPressing ? 1.15 : 1,
opacity: isDisabled ? 0.5 : 1,
transition: 'opacity 0.2s ease-in-out, transform 0.2s ease-in-out',
} as React.CSSProperties,
};
}, [isDisabled, isPressing, isLongPressing]);
return { handlers, props };
};