feat(markdown): integrate undo/redo for edit mode and checkbox toggles

This commit is contained in:
Hiren Kangad
2026-03-13 11:21:28 +05:30
parent 6118193f19
commit b1163c3232
2 changed files with 50 additions and 14 deletions
+1
View File
@@ -161,6 +161,7 @@ export function useCanvasSetup(deps: CanvasSetupDeps) {
// Double-click markdown → enter edit mode // Double-click markdown → enter edit mode
selection.onDoubleClickMarkdown = (item) => { selection.onDoubleClickMarkdown = (item) => {
if (mdOverlayRef.current) { if (mdOverlayRef.current) {
undoRef.current?.saveState();
mdOverlayRef.current.setEditing(item.id); mdOverlayRef.current.setEditing(item.id);
selection.setEnabled(false); selection.setEnabled(false);
} }
+49 -14
View File
@@ -43,6 +43,7 @@ import { StickySprite } from '../canvas/sprites/StickySprite';
import * as ops from '../canvas/operations'; import * as ops from '../canvas/operations';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import MarkdownReadView from '../components/MarkdownReadView'; import MarkdownReadView from '../components/MarkdownReadView';
const LazyMarkdownEditView = React.lazy(() => import('../components/MarkdownEditView'));
// Hooks // Hooks
import { useBoardLoader } from '../hooks/useBoardLoader'; import { useBoardLoader } from '../hooks/useBoardLoader';
@@ -207,6 +208,7 @@ export default function Editor({ isPublicView }: EditorProps) {
// ── Markdown overlay — sync visible card IDs for React portal rendering ── // ── Markdown overlay — sync visible card IDs for React portal rendering ──
const [mdCardIds, setMdCardIds] = useState<string[]>([]); const [mdCardIds, setMdCardIds] = useState<string[]>([]);
const [editingMdId, setEditingMdId] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (!mdOverlay) return; if (!mdOverlay) return;
@@ -227,8 +229,12 @@ export default function Editor({ isPublicView }: EditorProps) {
}; };
mdOverlay.onChange = syncIds; mdOverlay.onChange = syncIds;
mdOverlay.onRequestEdit = (id) => setEditingMdId(id);
syncIds(); syncIds();
return () => { mdOverlay.onChange = null; }; return () => {
mdOverlay.onChange = null;
mdOverlay.onRequestEdit = null;
};
}, [mdOverlay]); }, [mdOverlay]);
// Build canvas objects map for FeedbackPanel (memoized on object count changes) // Build canvas objects map for FeedbackPanel (memoized on object count changes)
@@ -466,6 +472,11 @@ export default function Editor({ isPublicView }: EditorProps) {
broadcastElements: (ids) => syncRef.current?.broadcastElements(ids), broadcastElements: (ids) => syncRef.current?.broadcastElements(ids),
textEditor: textEditor ?? undefined, textEditor: textEditor ?? undefined,
switchToSelect: () => setActiveTool(ToolType.SELECT), switchToSelect: () => setActiveTool(ToolType.SELECT),
onMarkdownCreated: (id) => {
setEditingMdId(id);
mdOverlay?.setEditing(id);
selectionRef.current?.setEnabled(false);
},
}; };
// reviewMode is read at click time via getter so it stays current // reviewMode is read at click time via getter so it stays current
Object.defineProperty(ctx, 'reviewMode', { get: () => reviewModeRef.current }); Object.defineProperty(ctx, 'reviewMode', { get: () => reviewModeRef.current });
@@ -1213,20 +1224,44 @@ export default function Editor({ isPublicView }: EditorProps) {
const item = canvasRef.current?.getScene()?.getById(id); const item = canvasRef.current?.getScene()?.getById(id);
if (!mountPoint || !item || item.type !== 'markdown') return null; if (!mountPoint || !item || item.type !== 'markdown') return null;
const data = item.data as MarkdownObject; const data = item.data as MarkdownObject;
const isEditing = editingMdId === id;
return ReactDOM.createPortal( return ReactDOM.createPortal(
<MarkdownReadView isEditing ? (
key={id} <React.Suspense fallback={<div style={{ padding: 16, color: '#999' }}>Loading editor...</div>}>
content={data.content} <LazyMarkdownEditView
textColor={data.textColor} key={`${id}-edit`}
accentColor={data.accentColor} initialContent={data.content}
bgColor={data.bgColor} accentColor={data.accentColor}
padding={data.padding} onSave={(newContent) => {
onCheckboxToggle={(newContent) => { data.content = newContent;
data.content = newContent; setEditingMdId(null);
onCanvasChange([id]); mdOverlay?.setEditing(null);
mdOverlay?.measureHeight(id); selectionRef.current?.setEnabled(true);
}} onCanvasChange([id]);
/>, mdOverlay?.measureHeight(id);
}}
onCancel={() => {
setEditingMdId(null);
mdOverlay?.setEditing(null);
selectionRef.current?.setEnabled(true);
}}
/>
</React.Suspense>
) : (
<MarkdownReadView
key={id}
content={data.content}
textColor={data.textColor}
accentColor={data.accentColor}
bgColor={data.bgColor}
padding={data.padding}
onCheckboxToggle={(newContent) => {
data.content = newContent;
onCanvasChange([id]);
mdOverlay?.measureHeight(id);
}}
/>
),
mountPoint, mountPoint,
); );
})} })}