forked from akai/readest
127 lines
4.0 KiB
TypeScript
127 lines
4.0 KiB
TypeScript
import clsx from 'clsx';
|
|
import React from 'react';
|
|
|
|
import { useEnv } from '@/context/EnvContext';
|
|
import { BookNote } from '@/types/book';
|
|
import { useReaderStore } from '@/store/readerStore';
|
|
import useScrollToItem from '../../hooks/useScrollToItem';
|
|
|
|
interface BooknoteItemProps {
|
|
bookKey: string;
|
|
item: BookNote;
|
|
editable?: boolean;
|
|
}
|
|
|
|
const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, editable = false }) => {
|
|
const { envConfig } = useEnv();
|
|
const { settings, getConfig, saveConfig, getProgress, getView } = useReaderStore();
|
|
const { updateBooknotes, setNotebookEditAnnotation, setNotebookVisible } = useReaderStore();
|
|
|
|
const { text, cfi, note } = item;
|
|
const progress = getProgress(bookKey);
|
|
const { isCurrent, viewRef } = useScrollToItem(cfi, progress);
|
|
|
|
const handleClickItem = (event: React.MouseEvent) => {
|
|
event.preventDefault();
|
|
getView(bookKey)?.goTo(cfi);
|
|
if (note) {
|
|
setNotebookVisible(true);
|
|
}
|
|
};
|
|
|
|
const deleteNote = (note: BookNote) => {
|
|
if (!bookKey) return;
|
|
const config = getConfig(bookKey);
|
|
if (!config) return;
|
|
const { booknotes = [] } = config;
|
|
const updatedNotes = booknotes.filter((item) => item.id !== note.id);
|
|
const updatedConfig = updateBooknotes(bookKey, updatedNotes);
|
|
if (updatedConfig) {
|
|
saveConfig(envConfig, bookKey, updatedConfig, settings);
|
|
}
|
|
};
|
|
|
|
const editNote = (note: BookNote) => {
|
|
setNotebookEditAnnotation(note);
|
|
};
|
|
|
|
return (
|
|
<li
|
|
ref={viewRef}
|
|
className={clsx(
|
|
'border-base-300 my-2 cursor-pointer rounded-lg p-2 text-sm',
|
|
editable && 'collapse-arrow collapse',
|
|
isCurrent ? 'bg-base-300/85 hover:bg-base-300' : 'hover:bg-base-200 bg-base-100',
|
|
)}
|
|
tabIndex={0}
|
|
onClick={handleClickItem}
|
|
>
|
|
<div
|
|
className={clsx('collapse-title min-h-4 p-0', editable && 'pr-4')}
|
|
style={
|
|
{
|
|
'--top-override': '0.7rem',
|
|
'--end-override': '0.3rem',
|
|
} as React.CSSProperties
|
|
}
|
|
>
|
|
{item.note && <span className='line-clamp-3 font-normal'>{item.note}</span>}
|
|
<div className='flex items-start'>
|
|
{item.note && (
|
|
<div className='my-1 mr-2 min-h-full self-stretch border-l-2 border-gray-300'></div>
|
|
)}
|
|
<div className='line-clamp-3'>
|
|
<span
|
|
className={clsx(
|
|
'inline',
|
|
item.note && 'text-xs text-gray-500',
|
|
(item.style === 'underline' || item.style === 'squiggly') &&
|
|
'underline decoration-2',
|
|
item.style === 'highlight' && `bg-${item.color}-100`,
|
|
item.style === 'underline' && `decoration-${item.color}-400`,
|
|
item.style === 'squiggly' && `decoration-wavy decoration-${item.color}-400`,
|
|
)}
|
|
>
|
|
{text || ''}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{editable && (
|
|
<div
|
|
className={clsx('collapse-content invisible !p-0 text-xs')}
|
|
style={
|
|
{
|
|
'--bottom-override': 0,
|
|
} as React.CSSProperties
|
|
}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<div className='flex justify-end space-x-3'>
|
|
<button
|
|
className={clsx(
|
|
'btn btn-ghost settings-content hover:bg-transparent',
|
|
'flex h-4 min-h-4 items-end p-0',
|
|
)}
|
|
onClick={editNote.bind(null, item)}
|
|
>
|
|
<div className='align-bottom text-xs text-blue-400'>Edit</div>
|
|
</button>
|
|
<button
|
|
className={clsx(
|
|
'btn btn-ghost settings-content hover:bg-transparent',
|
|
'flex h-4 min-h-4 items-end p-0',
|
|
)}
|
|
onClick={deleteNote.bind(null, item)}
|
|
>
|
|
<div className='align-bottom text-xs text-red-400'>Delete</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</li>
|
|
);
|
|
};
|
|
|
|
export default BooknoteItem;
|