- Created automated script (scripts/align-8px-grid.py) to align all spacing to 8px grid - Replaced non-8px-aligned spacing: gap-3/p-3/m-3 (12px) → gap-4/p-4/m-4 (16px), gap-5/p-5/m-5 (20px) → gap-6/p-6/m-6 (24px), gap-10/p-10/m-10 (40px) → gap-12/p-12/m-12 (48px), gap-20/p-20/m-20 (80px) → gap-24/p-24/m-24 (96px) - Preserved: 4px values (gap-1, p-1, m-1) as they may be intentional fine-tuning, responsive breakpoints (sm:, md:, lg:), test files, documentation - Modified files across all components to ensure consistent 8px grid alignment - Action 11.2.1.3: Align all elements to 8px grid - COMPLETE
177 lines
5.7 KiB
TypeScript
177 lines
5.7 KiB
TypeScript
/**
|
|
* Composant TrackListSelectionActions
|
|
* Barre d'actions pour les pistes sélectionnées
|
|
*/
|
|
|
|
import { Play, Trash2, Heart, Download, MoreVertical, X } from 'lucide-react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
export interface TrackListSelectionActionsProps {
|
|
selectedCount: number;
|
|
onPlay?: (trackIds: string[]) => void;
|
|
onDelete?: (trackIds: string[]) => void;
|
|
onLike?: (trackIds: string[]) => void;
|
|
onDownload?: (trackIds: string[]) => void;
|
|
onMore?: (trackIds: string[]) => void;
|
|
onClearSelection?: () => void;
|
|
selectedTrackIds: string[];
|
|
className?: string;
|
|
}
|
|
|
|
export function TrackListSelectionActions({
|
|
selectedCount,
|
|
onPlay,
|
|
onDelete,
|
|
onLike,
|
|
onDownload,
|
|
onMore,
|
|
onClearSelection,
|
|
selectedTrackIds,
|
|
className,
|
|
}: TrackListSelectionActionsProps) {
|
|
if (selectedCount === 0) {
|
|
return null;
|
|
}
|
|
|
|
const handlePlay = () => {
|
|
onPlay?.(selectedTrackIds);
|
|
};
|
|
|
|
const handleDelete = () => {
|
|
onDelete?.(selectedTrackIds);
|
|
};
|
|
|
|
const handleLike = () => {
|
|
onLike?.(selectedTrackIds);
|
|
};
|
|
|
|
const handleDownload = () => {
|
|
onDownload?.(selectedTrackIds);
|
|
};
|
|
|
|
const handleMore = () => {
|
|
onMore?.(selectedTrackIds);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'fixed bottom-0 left-0 right-0 z-50',
|
|
'bg-kodo-cyan dark:bg-kodo-cyan text-white',
|
|
'px-4 py-4 shadow-lg',
|
|
'flex items-center justify-between gap-4',
|
|
'transition-transform duration-300 ease-in-out',
|
|
className,
|
|
)}
|
|
role="toolbar"
|
|
aria-label={`Actions pour ${selectedCount} piste${selectedCount > 1 ? 's' : ''} sélectionnée${selectedCount > 1 ? 's' : ''}`}
|
|
>
|
|
<div className="flex items-center gap-4">
|
|
<span className="text-sm font-medium">
|
|
{selectedCount} piste{selectedCount > 1 ? 's' : ''} sélectionnée
|
|
{selectedCount > 1 ? 's' : ''}
|
|
</span>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{onPlay && (
|
|
<button
|
|
type="button"
|
|
onClick={handlePlay}
|
|
className={cn(
|
|
'p-2 rounded-md transition-colors',
|
|
'hover:bg-white/5 dark:hover:bg-white/5',
|
|
'focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-blue-600',
|
|
)}
|
|
aria-label={`Lire ${selectedCount} piste${selectedCount > 1 ? 's' : ''}`}
|
|
>
|
|
<Play className="h-5 w-5" aria-hidden="true" />
|
|
<span className="sr-only">Lire</span>
|
|
</button>
|
|
)}
|
|
|
|
{onLike && (
|
|
<button
|
|
type="button"
|
|
onClick={handleLike}
|
|
className={cn(
|
|
'p-2 rounded-md transition-colors',
|
|
'hover:bg-white/5 dark:hover:bg-white/5',
|
|
'focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-blue-600',
|
|
)}
|
|
aria-label={`Ajouter ${selectedCount} piste${selectedCount > 1 ? 's' : ''} aux favoris`}
|
|
>
|
|
<Heart className="h-5 w-5" aria-hidden="true" />
|
|
<span className="sr-only">Ajouter aux favoris</span>
|
|
</button>
|
|
)}
|
|
|
|
{onDownload && (
|
|
<button
|
|
type="button"
|
|
onClick={handleDownload}
|
|
className={cn(
|
|
'p-2 rounded-md transition-colors',
|
|
'hover:bg-white/5 dark:hover:bg-white/5',
|
|
'focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-blue-600',
|
|
)}
|
|
aria-label={`Télécharger ${selectedCount} piste${selectedCount > 1 ? 's' : ''}`}
|
|
>
|
|
<Download className="h-5 w-5" aria-hidden="true" />
|
|
<span className="sr-only">Télécharger</span>
|
|
</button>
|
|
)}
|
|
|
|
{onDelete && (
|
|
<button
|
|
type="button"
|
|
onClick={handleDelete}
|
|
className={cn(
|
|
'p-2 rounded-md transition-colors',
|
|
'hover:bg-kodo-red dark:hover:bg-kodo-red',
|
|
'focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-blue-600',
|
|
)}
|
|
aria-label={`Supprimer ${selectedCount} piste${selectedCount > 1 ? 's' : ''}`}
|
|
>
|
|
<Trash2 className="h-5 w-5" aria-hidden="true" />
|
|
<span className="sr-only">Supprimer</span>
|
|
</button>
|
|
)}
|
|
|
|
{onMore && (
|
|
<button
|
|
type="button"
|
|
onClick={handleMore}
|
|
className={cn(
|
|
'p-2 rounded-md transition-colors',
|
|
'hover:bg-white/5 dark:hover:bg-white/5',
|
|
'focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-blue-600',
|
|
)}
|
|
aria-label={`Plus d'options pour ${selectedCount} piste${selectedCount > 1 ? 's' : ''}`}
|
|
>
|
|
<MoreVertical className="h-5 w-5" aria-hidden="true" />
|
|
<span className="sr-only">Plus d'options</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{onClearSelection && (
|
|
<button
|
|
type="button"
|
|
onClick={onClearSelection}
|
|
className={cn(
|
|
'p-2 rounded-md transition-colors',
|
|
'hover:bg-white/5 dark:hover:bg-white/5',
|
|
'focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-blue-600',
|
|
)}
|
|
aria-label="Désélectionner toutes les pistes"
|
|
>
|
|
<X className="h-5 w-5" aria-hidden="true" />
|
|
<span className="sr-only">Fermer</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default TrackListSelectionActions;
|