Files
Siteforge/src/components/NoteCard.tsx
T
2026-06-18 13:46:41 -04:00

97 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { useEffect } from 'react'
interface NoteCardProps {
anchorLabel: string
html: string
autoSaveMsg?: string
onChange: (html: string) => void
onClose: () => void
}
export function NoteCard({ anchorLabel, html, autoSaveMsg, onChange, onClose }: NoteCardProps) {
const editor = useEditor({
extensions: [StarterKit],
content: html || '<p></p>',
onUpdate({ editor }) {
const next = editor.getHTML()
onChange(next === '<p></p>' ? '' : next)
},
})
useEffect(() => {
if (!editor || editor.isDestroyed) return
if (editor.getHTML() !== html) {
editor.commands.setContent(html || '<p></p>', { emitUpdate: false })
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [html])
useEffect(() => () => { editor?.destroy() }, [editor])
if (!editor) return null
return (
<div className="note-card">
<div className="note-card-header">
<span className="note-card-label"> {anchorLabel}</span>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
{autoSaveMsg && <span className="note-card-autosave">{autoSaveMsg}</span>}
<button type="button" className="note-card-close" onClick={onClose} aria-label="Close note">×</button>
</div>
</div>
<div className="note-card-toolbar" role="toolbar" aria-label="Text formatting">
<button
type="button"
className={`note-tool${editor.isActive('bold') ? ' note-tool--active' : ''}`}
onMouseDown={e => { e.preventDefault(); editor.chain().focus().toggleBold().run() }}
aria-label="Bold"
title="Bold (Ctrl+B)"
><strong>B</strong></button>
<button
type="button"
className={`note-tool${editor.isActive('italic') ? ' note-tool--active' : ''}`}
onMouseDown={e => { e.preventDefault(); editor.chain().focus().toggleItalic().run() }}
aria-label="Italic"
title="Italic (Ctrl+I)"
><em>I</em></button>
<button
type="button"
className={`note-tool${editor.isActive('bulletList') ? ' note-tool--active' : ''}`}
onMouseDown={e => { e.preventDefault(); editor.chain().focus().toggleBulletList().run() }}
aria-label="Bullet list"
title="Bullet list"
></button>
<button
type="button"
className={`note-tool${editor.isActive('orderedList') ? ' note-tool--active' : ''}`}
onMouseDown={e => { e.preventDefault(); editor.chain().focus().toggleOrderedList().run() }}
aria-label="Numbered list"
title="Numbered list"
>1.</button>
<div className="note-tool-divider" />
<button
type="button"
className="note-tool"
onMouseDown={e => { e.preventDefault(); editor.chain().focus().undo().run() }}
disabled={!editor.can().undo()}
aria-label="Undo"
title="Undo (Ctrl+Z)"
></button>
<button
type="button"
className="note-tool"
onMouseDown={e => { e.preventDefault(); editor.chain().focus().redo().run() }}
disabled={!editor.can().redo()}
aria-label="Redo"
title="Redo (Ctrl+Y)"
></button>
</div>
<EditorContent editor={editor} className="note-card-editor" />
</div>
)
}