feat(markdown): integrate undo/redo for edit mode and checkbox toggles
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +1224,30 @@ 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(
|
||||||
|
isEditing ? (
|
||||||
|
<React.Suspense fallback={<div style={{ padding: 16, color: '#999' }}>Loading editor...</div>}>
|
||||||
|
<LazyMarkdownEditView
|
||||||
|
key={`${id}-edit`}
|
||||||
|
initialContent={data.content}
|
||||||
|
accentColor={data.accentColor}
|
||||||
|
onSave={(newContent) => {
|
||||||
|
data.content = newContent;
|
||||||
|
setEditingMdId(null);
|
||||||
|
mdOverlay?.setEditing(null);
|
||||||
|
selectionRef.current?.setEnabled(true);
|
||||||
|
onCanvasChange([id]);
|
||||||
|
mdOverlay?.measureHeight(id);
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
setEditingMdId(null);
|
||||||
|
mdOverlay?.setEditing(null);
|
||||||
|
selectionRef.current?.setEnabled(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</React.Suspense>
|
||||||
|
) : (
|
||||||
<MarkdownReadView
|
<MarkdownReadView
|
||||||
key={id}
|
key={id}
|
||||||
content={data.content}
|
content={data.content}
|
||||||
@@ -1226,7 +1260,8 @@ export default function Editor({ isPublicView }: EditorProps) {
|
|||||||
onCanvasChange([id]);
|
onCanvasChange([id]);
|
||||||
mdOverlay?.measureHeight(id);
|
mdOverlay?.measureHeight(id);
|
||||||
}}
|
}}
|
||||||
/>,
|
/>
|
||||||
|
),
|
||||||
mountPoint,
|
mountPoint,
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user