Improves sound placement and preview logic

Refines the sound placement logic in the sequencer to ensure sounds
are placed correctly within track boundaries. It restricts sound
placement to the track duration, preventing sounds from being placed
out of bounds.

Enhances the drag preview by visually indicating invalid placement
positions with a red border and "Invalid" label.

Also extracts duration and size formatting into separate utility functions
for better code organization.
This commit is contained in:
JSC
2025-09-03 17:17:19 +02:00
parent dba08e2ec0
commit 1ba6f23999
3 changed files with 104 additions and 98 deletions

View File

@@ -157,26 +157,38 @@ function TrackRow({ track, duration, zoom, isPlaying, currentTime, draggedItem,
</div>
{/* Precise drag preview */}
{draggedItem && dragOverInfo && dragOverInfo.trackId === track.id && (
<div
className="absolute top-2 bottom-2 border-2 border-dashed border-primary/60 bg-primary/10 rounded pointer-events-none z-10 flex items-center px-2"
style={{
left: `${dragOverInfo.x}px`,
width: `${Math.max(60,
draggedItem.type === 'sound'
? (draggedItem.sound.duration / 1000) * zoom
: draggedItem.duration * zoom
)}px`,
}}
>
<div className="text-xs text-primary/80 truncate font-medium">
{draggedItem.type === 'sound'
? (draggedItem.sound.name || draggedItem.sound.filename)
: draggedItem.name
}
{draggedItem && dragOverInfo && dragOverInfo.trackId === track.id && (() => {
const soundDuration = draggedItem.type === 'sound'
? draggedItem.sound.duration / 1000 // Convert ms to seconds
: draggedItem.duration
const startTime = dragOverInfo.x / zoom
const endTime = startTime + soundDuration
const isValidPosition = startTime >= 0 && endTime <= duration
return (
<div
className={`absolute top-2 bottom-2 border-2 border-dashed rounded pointer-events-none z-10 flex items-center px-2 ${
isValidPosition
? 'border-primary/60 bg-primary/10'
: 'border-red-500/60 bg-red-500/10'
}`}
style={{
left: `${Math.max(0, dragOverInfo.x)}px`,
width: `${Math.max(60, soundDuration * zoom)}px`,
}}
>
<div className={`text-xs truncate font-medium ${
isValidPosition ? 'text-primary/80' : 'text-red-500/80'
}`}>
{draggedItem.type === 'sound'
? (draggedItem.sound.name || draggedItem.sound.filename)
: draggedItem.name
}
{!isValidPosition && ' (Invalid)'}
</div>
</div>
</div>
)}
)
})()}
{/* Playhead */}
{isPlaying && (

View File

@@ -21,6 +21,8 @@ import {
} from 'lucide-react'
import { useCallback, useEffect, useState } from 'react'
import { toast } from 'sonner'
import { formatDuration } from '@/utils/format-duration'
import { formatSize } from '@/utils/format-size'
interface DraggableSoundProps {
sound: Sound
@@ -43,18 +45,6 @@ function DraggableSound({ sound }: DraggableSoundProps) {
// Don't apply transform to prevent layout shift - DragOverlay handles the visual feedback
const style = undefined
const formatDuration = (ms: number): string => {
const seconds = Math.floor(ms / 1000)
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return `${mins}:${secs.toString().padStart(2, '0')}`
}
const formatFileSize = (bytes: number): string => {
const mb = bytes / (1024 * 1024)
return `${mb.toFixed(1)}MB`
}
return (
<div
ref={setNodeRef}
@@ -81,7 +71,7 @@ function DraggableSound({ sound }: DraggableSoundProps) {
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-1">
<span>{formatDuration(sound.duration)}</span>
<span></span>
<span>{formatFileSize(sound.size)}</span>
<span>{formatSize(sound.size)}</span>
{sound.play_count > 0 && (
<>
<span></span>