Files
Momento/memento-note/components/note-peek/use-note-peek.ts
Antigravity 261eee2953
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m6s
CI / Deploy production (on server) (push) Successful in 26s
refactor: factorisation peek panel — NotePeekPanel réutilisable
Architecture:
- components/note-peek/use-note-peek.ts: hook (fetch + state + events)
- components/note-peek/note-peek-content.tsx: rendu markdown/richtext/KaTeX
- components/note-peek/note-peek-panel.tsx: panel (overlay + inline modes)
- components/note-peek/index.ts: exports
- lib/use-scroll-to-block.ts: utilitaire scroll vers data-id

insights/page.tsx:
- ~95 lignes (state + fetch + KaTeX useEffect + AnimatePresence) → 10 lignes
- peek.open(noteId) remplace handleNoteClick complexe
- <NotePeekPanel mode=overlay /> remplace tout le JSX du panel

NotePeekPanel gère:
- markdown (MarkdownContent) + richtext (DOMPurify + KaTeX lazy)
- RTL (slide gauche pour fa/ar)
- prefers-reduced-motion
- role=dialog + aria-modal (overlay mode)
- loading spinner
- bouton Maximize2 + X
- renderContent prop pour custom (éditeur read-only)
2026-07-04 23:37:37 +00:00

61 lines
1.7 KiB
TypeScript

'use client'
import { useState, useCallback, useEffect } from 'react'
import { getNoteById } from '@/app/actions/notes'
import type { Note } from '@/lib/types'
import {
NOTE_PEEK_OPEN_EVENT,
NOTE_PEEK_CLOSE_EVENT,
type NotePeekOpenDetail,
} from '@/lib/note-peek-sync'
interface UseNotePeekOptions {
selfNoteId?: string
}
export function useNotePeek(opts: UseNotePeekOptions = {}) {
const { selfNoteId } = opts
const [note, setNote] = useState<Note | null>(null)
const [blockId, setBlockId] = useState<string | undefined>(undefined)
const [loading, setLoading] = useState(false)
const [isOpen, setIsOpen] = useState(false)
const open = useCallback(async (targetNoteId: string, targetBlockId?: string) => {
if (selfNoteId && targetNoteId === selfNoteId) return
setLoading(true)
setIsOpen(true)
setBlockId(targetBlockId)
try {
const fetched = await getNoteById(targetNoteId)
if (fetched) setNote(fetched)
} catch {
// silent
} finally {
setLoading(false)
}
}, [selfNoteId])
const close = useCallback(() => {
setIsOpen(false)
setNote(null)
setBlockId(undefined)
}, [])
useEffect(() => {
const onOpen = (e: Event) => {
const detail = (e as CustomEvent<NotePeekOpenDetail>).detail
if (!detail) return
void open(detail.noteId, detail.blockId)
}
const onClose = () => close()
window.addEventListener(NOTE_PEEK_OPEN_EVENT, onOpen)
window.addEventListener(NOTE_PEEK_CLOSE_EVENT, onClose)
return () => {
window.removeEventListener(NOTE_PEEK_OPEN_EVENT, onOpen)
window.removeEventListener(NOTE_PEEK_CLOSE_EVENT, onClose)
}
}, [open, close])
return { note, blockId, loading, isOpen, open, close }
}