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:
80
memento-note/components/smart-paste-menu.tsx
Normal file
80
memento-note/components/smart-paste-menu.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
import { Link2, Blocks } from 'lucide-react'
|
||||
import type { ParsedBlockReference } from '@/lib/editor/parse-block-reference'
|
||||
|
||||
export type SmartPasteMenuProps = {
|
||||
anchor: { top: number; left: number }
|
||||
reference: ParsedBlockReference
|
||||
sourceNoteTitle?: string
|
||||
onLive: () => void
|
||||
onPlain: () => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function SmartPasteMenu({
|
||||
anchor,
|
||||
reference,
|
||||
sourceNoteTitle,
|
||||
onLive,
|
||||
onPlain,
|
||||
onClose,
|
||||
}: SmartPasteMenuProps) {
|
||||
const { t } = useLanguage()
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape') onClose()
|
||||
}
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
document.addEventListener('keydown', handleKeyDown)
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
}
|
||||
}, [onClose])
|
||||
|
||||
const menuStyle: React.CSSProperties = {
|
||||
position: 'fixed',
|
||||
left: anchor.left,
|
||||
top: anchor.top + 8,
|
||||
zIndex: 9999,
|
||||
maxWidth: 320,
|
||||
}
|
||||
|
||||
if (Number(menuStyle.left) + 320 > window.innerWidth) {
|
||||
menuStyle.left = Math.max(8, window.innerWidth - 328)
|
||||
}
|
||||
if (Number(menuStyle.top) + 140 > window.innerHeight) {
|
||||
menuStyle.top = anchor.top - 132
|
||||
}
|
||||
|
||||
const previewTitle = sourceNoteTitle?.trim() || t('smartPaste.unknownNote')
|
||||
|
||||
return createPortal(
|
||||
<div ref={menuRef} style={menuStyle} className="block-action-menu smart-paste-menu">
|
||||
<p className="smart-paste-menu__hint">{t('smartPaste.prompt')}</p>
|
||||
<p className="smart-paste-menu__source" title={reference.raw}>
|
||||
{previewTitle}
|
||||
</p>
|
||||
<button type="button" className="block-action-item" onClick={onLive}>
|
||||
<Blocks size={16} />
|
||||
<span>{t('smartPaste.liveBlock')}</span>
|
||||
</button>
|
||||
<button type="button" className="block-action-item" onClick={onPlain}>
|
||||
<Link2 size={16} />
|
||||
<span>{t('smartPaste.plainLink')}</span>
|
||||
</button>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user