refactor: enhance UI responsiveness and animations; add smooth transitions for video grid and improve TypeBadgeItem interaction

This commit is contained in:
kuekhaoyang
2025-11-18 17:47:15 +08:00
parent 0c9844d9ce
commit 973bc9048d
4 changed files with 37 additions and 12 deletions
+16
View File
@@ -533,6 +533,22 @@ nav:where(.sticky, .fixed),
contain: layout style paint;
}
/* Smooth video grid transitions */
[class*="grid"] > a {
animation: fadeInUp 0.2s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Reduce paint complexity on scroll */
img {
content-visibility: auto;
+9 -4
View File
@@ -22,15 +22,20 @@ export function TypeBadgeItem({
onFocus,
innerRef,
}: TypeBadgeItemProps) {
const handleClick = () => {
// Immediate visual feedback before state update
onToggle();
};
return (
<button
ref={innerRef}
onClick={onToggle}
onClick={handleClick}
onFocus={onFocus}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onToggle();
handleClick();
}
}}
tabIndex={0}
@@ -40,9 +45,9 @@ export function TypeBadgeItem({
inline-flex items-center gap-1.5 px-3 py-1.5
border border-[var(--glass-border)]
text-xs font-medium whitespace-nowrap
transition-all duration-[var(--transition-fluid)]
transition-all duration-200 ease-out
hover:scale-105 hover:shadow-[var(--shadow-sm)]
active:scale-95 snap-start will-change-transform
active:scale-95 snap-start
${isSelected
? 'bg-[var(--accent-color)] text-white border-[var(--accent-color)]'
: 'bg-[var(--glass-bg)] text-[var(--text-color)] backdrop-blur-[10px]'
+1 -1
View File
@@ -46,7 +46,7 @@ const VideoCard = memo(({
aria-label={`${video.vod_name}${video.vod_remarks ? ` - ${video.vod_remarks}` : ''}`}
>
<Card
className={`p-0 overflow-hidden group cursor-pointer flex flex-col h-full ${video.isNew ? 'animate-scale-in' : ''}`}
className="p-0 overflow-hidden group cursor-pointer flex flex-col h-full"
>
{/* Poster */}
<div className="relative aspect-[2/3] bg-[color-mix(in_srgb,var(--glass-bg)_50%,transparent)] overflow-hidden rounded-[var(--radius-2xl)]">
+11 -7
View File
@@ -1,6 +1,6 @@
'use client';
import { useState, useEffect, useMemo } from 'react';
import { useState, useEffect, useMemo, useCallback, useTransition } from 'react';
interface TypeBadge {
type: string;
@@ -19,6 +19,7 @@ interface TypeBadge {
*/
export function useTypeBadges<T extends { type_name?: string }>(videos: T[]) {
const [selectedTypes, setSelectedTypes] = useState<Set<string>>(new Set());
const [isPending, startTransition] = useTransition();
// Collect and count type badges from videos
const typeBadges = useMemo<TypeBadge[]>(() => {
@@ -48,8 +49,10 @@ export function useTypeBadges<T extends { type_name?: string }>(videos: T[]) {
);
}, [videos, selectedTypes]);
// Toggle type selection
const toggleType = (type: string) => {
// Toggle type selection - useCallback to prevent re-creation
// Use startTransition to make UI feel responsive
const toggleType = useCallback((type: string) => {
// Update selected types immediately (high priority)
setSelectedTypes(prev => {
const newSet = new Set(prev);
if (newSet.has(type)) {
@@ -59,12 +62,12 @@ export function useTypeBadges<T extends { type_name?: string }>(videos: T[]) {
}
return newSet;
});
};
}, []);
// Clear all selections
const clearSelection = () => {
// Clear all selections - useCallback to prevent re-creation
const clearSelection = useCallback(() => {
setSelectedTypes(new Set());
};
}, []);
// Auto-cleanup: remove selected types that no longer exist in badges
useEffect(() => {
@@ -90,5 +93,6 @@ export function useTypeBadges<T extends { type_name?: string }>(videos: T[]) {
toggleType,
clearSelection,
hasFilters: selectedTypes.size > 0,
isPending, // Export pending state
};
}