Files
readest/apps/readest-app/src/app/reader/components/sidebar/BookCard.tsx
T
Huang Xin 956c71cd7b chore: migrate from ESLint to Biome for linting (#3694)
Replace ESLint with Biome for ~14x faster linting (~360ms vs ~5s).

- Add biome.json with rules matching ESLint parity (Next.js, a11y,
  TypeScript, unused vars/imports)
- Remove eslint, @typescript-eslint/*, eslint-config-next,
  eslint-plugin-jsx-a11y, @eslint/* from deps
- Remove eslint.config.mjs
- Update lint script to: tsgo --noEmit && biome check .
- Fix 11 real code issues caught by Biome (banned types, explicit any,
  unsafe finally, unreachable code, shadowed names)
- Disable Biome formatter (Prettier stays for Tailwind class sorting)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 16:25:04 +02:00

58 lines
1.9 KiB
TypeScript

import clsx from 'clsx';
import { useRef } from 'react';
import { MdInfoOutline } from 'react-icons/md';
import { Book } from '@/types/book';
import { useThemeStore } from '@/store/themeStore';
import { useTranslation } from '@/hooks/useTranslation';
import { eventDispatcher } from '@/utils/event';
import { useResponsiveSize } from '@/hooks/useResponsiveSize';
import { formatAuthors, formatTitle } from '@/utils/book';
import BookCover from '@/components/BookCover';
const BookCard = ({ book }: { book: Book }) => {
const { title, author } = book;
const _ = useTranslation();
const { isDarkMode } = useThemeStore();
const iconSize18 = useResponsiveSize(18);
const bookCoverRef = useRef<HTMLDivElement | null>(null);
const showBookDetails = () => {
eventDispatcher.dispatchSync('show-book-details', book);
};
return (
<div className='flex h-20 w-full items-center'>
<div
ref={bookCoverRef}
className={clsx(
'me-4 aspect-[28/41] max-h-16 w-[15%] max-w-12 overflow-hidden rounded-sm shadow-md',
isDarkMode ? 'mix-blend-screen' : 'mix-blend-multiply',
)}
>
<BookCover
book={book}
mode='list'
coverFit='crop'
imageClassName='rounded-sm'
onImageError={() => (bookCoverRef.current!.style.display = 'none')}
/>
</div>
<div className='min-w-0 flex-1'>
<h4 className='line-clamp-2 w-[90%] text-sm font-semibold'>
{formatTitle(title).replace(/\u00A0/g, ' ')}
</h4>
<p className='truncate text-xs opacity-75'>{formatAuthors(author)}</p>
</div>
<button
className='btn btn-ghost hover:bg-base-300 h-6 min-h-6 w-6 rounded-full p-0 transition-colors'
aria-label={_('More Info')}
onClick={showBookDetails}
>
<MdInfoOutline size={iconSize18} className='fill-base-content' />
</button>
</div>
);
};
export default BookCard;