Files
shelfless/src/components/detail/ChapterList.tsx
Scarriffle 83d8b7b99d Initial commit: Shelfless – alternative Audiobookshelf frontend
React + Vite + TypeScript SPA covering the full ABS feature set (library
browsing, item detail, metadata/cover editing, podcasts, player with session
sync, admin: users/libraries/scanner/server settings). Dev uses a dynamic
CORS proxy; production is served by server/index.mjs (static + reverse proxy
to ABS_URL). Includes systemd unit and installer under deploy/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 20:23:04 +02:00

40 lines
1.4 KiB
TypeScript

import { Play } from 'lucide-react'
import { formatTime } from '@/lib/format'
import { cn } from '@/lib/cn'
import type { Chapter } from '@/types/abs'
interface Props {
chapters: Chapter[]
activeStart?: number
onJump: (start: number) => void
}
export function ChapterList({ chapters, activeStart, onJump }: Props) {
if (!chapters.length) return null
return (
<div className="overflow-hidden rounded-lg border border-border">
{chapters.map((c) => {
const active = activeStart != null && activeStart === c.start
return (
<button
key={c.id}
onClick={() => onJump(c.start)}
className={cn(
'group flex w-full items-center gap-3 border-b border-border px-3 py-2.5 text-left text-sm last:border-b-0 transition-colors hover:bg-surface-2',
active && 'bg-accent-soft',
)}
>
<span className="grid h-7 w-7 shrink-0 place-items-center rounded-full bg-surface-2 text-text-muted group-hover:bg-accent group-hover:text-on-accent">
<Play size={13} />
</span>
<span className={cn('flex-1 truncate', active ? 'text-text' : 'text-text')}>
{c.title}
</span>
<span className="tnum shrink-0 text-xs text-text-muted">{formatTime(c.start)}</span>
</button>
)
})}
</div>
)
}