Add expandable full-screen editor modal to ResearchInspector note body
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Pin, Archive, RefreshCw, Sparkles } from 'lucide-react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Pin, Archive, RefreshCw, Sparkles, Maximize2, Minimize2 } from 'lucide-react';
|
||||
import type {
|
||||
GhostNote,
|
||||
NoteAuditTrail,
|
||||
@@ -57,6 +58,8 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
|
||||
const [draftTitle, setDraftTitle] = useState('');
|
||||
const [draftBody, setDraftBody] = useState('');
|
||||
const [draftType, setDraftType] = useState<NoteType>('claim');
|
||||
const [isEditorExpanded, setIsEditorExpanded] = useState(false);
|
||||
const expandedEditorRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!note) {
|
||||
@@ -68,6 +71,28 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
|
||||
setDraftType(note.noteType);
|
||||
}, [note]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEditorExpanded && expandedEditorRef.current) {
|
||||
expandedEditorRef.current.focus();
|
||||
expandedEditorRef.current.setSelectionRange(
|
||||
expandedEditorRef.current.value.length,
|
||||
expandedEditorRef.current.value.length,
|
||||
);
|
||||
}
|
||||
}, [isEditorExpanded]);
|
||||
|
||||
const handleExpandKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
setIsEditorExpanded(false);
|
||||
} else if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
setIsEditorExpanded(false);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
if (!note && !ghost) {
|
||||
return (
|
||||
<div className="flex h-full flex-col justify-between p-4 sm:p-5">
|
||||
@@ -154,7 +179,17 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-2">
|
||||
<span className="text-[10px] uppercase tracking-[0.18em] text-term-text-tertiary">Body</span>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[10px] uppercase tracking-[0.18em] text-term-text-tertiary">Body</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsEditorExpanded(true)}
|
||||
className="inline-flex items-center gap-1 rounded-lg border border-term-border bg-term-surface px-1.5 py-1 text-term-text-tertiary transition-colors hover:border-info/60 hover:text-term-text"
|
||||
aria-label="Expand editor"
|
||||
>
|
||||
<Maximize2 className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
value={draftBody}
|
||||
onChange={(event) => setDraftBody(event.target.value)}
|
||||
@@ -273,6 +308,60 @@ export const ResearchInspector: React.FC<ResearchInspectorProps> = ({
|
||||
<p className="text-sm text-term-text-tertiary">Select a note to inspect its evidence chain.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isEditorExpanded && note && createPortal(
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4"
|
||||
onClick={() => setIsEditorExpanded(false)}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Expanded note editor"
|
||||
>
|
||||
<div
|
||||
className="flex max-h-[90vh] w-full max-w-4xl flex-col overflow-hidden rounded-[24px] border border-term-border bg-term-elevated shadow-2xl"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onKeyDown={handleExpandKeyDown}
|
||||
>
|
||||
<div className="flex items-center justify-between border-b border-term-border bg-term-surface px-6 py-4">
|
||||
<div>
|
||||
<h2 className="text-lg font-mono font-semibold text-term-text">Edit Note</h2>
|
||||
<p className="mt-1 text-xs text-term-text-muted">{draftTitle || 'Untitled'}</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsEditorExpanded(false)}
|
||||
className="inline-flex items-center gap-1.5 rounded-xl border border-term-border bg-term-bg px-2.5 py-2 text-term-text-tertiary transition-colors hover:border-info/60 hover:text-term-text"
|
||||
aria-label="Collapse editor"
|
||||
>
|
||||
<Minimize2 className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-6">
|
||||
<textarea
|
||||
ref={expandedEditorRef}
|
||||
value={draftBody}
|
||||
onChange={(e) => setDraftBody(e.target.value)}
|
||||
className="min-h-[50vh] w-full resize-y rounded-2xl border border-term-border bg-term-bg px-4 py-3 text-sm leading-7 text-term-text outline-none transition-colors focus:border-info"
|
||||
placeholder="Write your note..."
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-t border-term-border bg-term-surface px-6 py-4">
|
||||
<p className="text-[10px] font-mono text-term-text-tertiary">
|
||||
Press <kbd className="rounded border border-term-border bg-term-bg px-1 py-0.5">Esc</kbd> or{' '}
|
||||
<kbd className="rounded border border-term-border bg-term-bg px-1 py-0.5">⌘ Enter</kbd> to collapse
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsEditorExpanded(false)}
|
||||
className="rounded border border-info bg-info px-4 py-2 text-xs font-mono font-semibold text-term-bg transition-colors hover:brightness-110"
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user