feat: editor improvements and architectural grid prototype
Multiple feature additions and improvements across the application: - NextGen Editor: drag handles, smart paste, block actions - Structured views: Kanban and table layouts for notes - Architectural Grid: new brainstorming/agent interface prototype - Flashcards: SM-2 revision algorithm with AI generation - MCP server: robustness improvements - Graph/PDF chat: fix click propagation and copy behavior - Various UI/UX enhancements and bug fixes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
78
memento-note/components/editor-block-drag-handle.tsx
Normal file
78
memento-note/components/editor-block-drag-handle.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* Poignée drag globale (Novel / tiptap-extension-global-drag-handle).
|
||||
* L’extension TipTap positionne cet élément ; le clic ouvre le menu bloc.
|
||||
*/
|
||||
import { memo, useEffect, useRef } from 'react'
|
||||
import type { Editor } from '@tiptap/core'
|
||||
import { BLOCK_DRAG_HANDLE_ID, resolveBlockAtDragHandle } from '@/lib/editor/block-at-drag-handle'
|
||||
|
||||
type EditorBlockDragHandleProps = {
|
||||
editor: Editor | null
|
||||
onOpenMenu: (anchorRect: DOMRect) => void
|
||||
}
|
||||
|
||||
export const EditorBlockDragHandle = memo(function EditorBlockDragHandle({
|
||||
editor,
|
||||
onOpenMenu,
|
||||
}: EditorBlockDragHandleProps) {
|
||||
const dragStartedRef = useRef(false)
|
||||
const onOpenMenuRef = useRef(onOpenMenu)
|
||||
onOpenMenuRef.current = onOpenMenu
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor || editor.isDestroyed) return
|
||||
const el = document.getElementById(BLOCK_DRAG_HANDLE_ID)
|
||||
if (!el) return
|
||||
|
||||
const onPointerDown = () => {
|
||||
dragStartedRef.current = false
|
||||
}
|
||||
|
||||
const onDragStart = () => {
|
||||
dragStartedRef.current = true
|
||||
}
|
||||
|
||||
const onClick = (e: MouseEvent) => {
|
||||
if (dragStartedRef.current) {
|
||||
dragStartedRef.current = false
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const block = resolveBlockAtDragHandle(editor)
|
||||
if (!block) return
|
||||
onOpenMenuRef.current(el.getBoundingClientRect())
|
||||
}
|
||||
|
||||
el.addEventListener('pointerdown', onPointerDown)
|
||||
el.addEventListener('dragstart', onDragStart)
|
||||
el.addEventListener('click', onClick)
|
||||
|
||||
return () => {
|
||||
el.removeEventListener('pointerdown', onPointerDown)
|
||||
el.removeEventListener('dragstart', onDragStart)
|
||||
el.removeEventListener('click', onClick)
|
||||
}
|
||||
}, [editor])
|
||||
|
||||
return (
|
||||
<div
|
||||
id={BLOCK_DRAG_HANDLE_ID}
|
||||
className="drag-handle hide"
|
||||
role="button"
|
||||
tabIndex={-1}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" aria-hidden="true">
|
||||
<circle cx="4" cy="3" r="1.2" />
|
||||
<circle cx="10" cy="3" r="1.2" />
|
||||
<circle cx="4" cy="7" r="1.2" />
|
||||
<circle cx="10" cy="7" r="1.2" />
|
||||
<circle cx="4" cy="11" r="1.2" />
|
||||
<circle cx="10" cy="11" r="1.2" />
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user