mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 17:29:29 +05:30
feat(flowchart): replace window.prompt with inline modal (C1)
The standalone flowchart generator still used window.prompt() for
editing edge kind/label and changing node kind — defeating the v4.9.9
modal work that's already in flowchart-bundle.js.
This commit switches to the existing window.FlowchartModals helpers:
- onEdgeClick now does two sequential promptInline calls (kind,
then label) instead of two blocking window.prompt calls.
- onShapeMenu uses promptInline for node-kind change.
- onEmptyClick (new) replaces the canvas's old 'auto-add a process
node labelled Node on any empty click' behaviour. The canvas
now hands off x/y to the controller, which prompts for kind +
label before adding.
Canvas change is one line: drop the auto-add and call opts.onEmptyClick.
followups in this batch:
- select/dropdown fields in the modal (currently text-only inputs)
- edge connection geometry (centre-to-centre → boundary)
- connect-mode preview line
- canvas-owned selection state (DOM .selected is fragile)
- undo/redo toolbar + counter
- edge label auto-size + theme-safe background
- zoom/pan, snap-to-grid, copy/paste, mermaid re-import, file IO
Amit Haridas
This commit is contained in:
@@ -223,12 +223,16 @@ function createCanvas(container, store, opts = {}) {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Click on empty canvas: add a process node at the click point.
|
// Click on empty canvas: hand off to the controller via onEmptyClick so it
|
||||||
|
// can prompt the user for the shape kind + label. v4.13.0 — the canvas
|
||||||
|
// no longer auto-creates a process node with placeholder label "Node".
|
||||||
if (ev.target === svg || ev.target === nodesLayer || ev.target === edgesLayer) {
|
if (ev.target === svg || ev.target === nodesLayer || ev.target === edgesLayer) {
|
||||||
const p = getSvgPoint(ev.clientX, ev.clientY);
|
const p = getSvgPoint(ev.clientX, ev.clientY);
|
||||||
const x = Math.max(0, p.x - DEFAULT_WIDTH / 2);
|
const x = Math.max(0, p.x - DEFAULT_WIDTH / 2);
|
||||||
const y = Math.max(0, p.y - DEFAULT_HEIGHT / 2);
|
const y = Math.max(0, p.y - DEFAULT_HEIGHT / 2);
|
||||||
store.addNode({ kind: 'process', x, y, label: 'Node' });
|
if (typeof opts.onEmptyClick === 'function') {
|
||||||
|
opts.onEmptyClick(x, y, ev);
|
||||||
|
}
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@
|
|||||||
const { create: createStore } = window.FlowchartStore;
|
const { create: createStore } = window.FlowchartStore;
|
||||||
const { createCanvas } = window.FlowchartCanvas;
|
const { createCanvas } = window.FlowchartCanvas;
|
||||||
const { toMermaid } = window.FlowchartMermaid;
|
const { toMermaid } = window.FlowchartMermaid;
|
||||||
|
const { promptInline, confirmInline: _confirmInline } = window.FlowchartModals || {};
|
||||||
|
void _confirmInline; // exposed by the bundle for future use (e.g. reset confirmation)
|
||||||
// FlowchartShapes is intentionally unused here but its presence is
|
// FlowchartShapes is intentionally unused here but its presence is
|
||||||
// required for createCanvas() to function; the guard above guarantees it.
|
// required for createCanvas() to function; the guard above guarantees it.
|
||||||
|
|
||||||
@@ -66,6 +68,9 @@
|
|||||||
const PERSIST_DEBOUNCE_MS = 500;
|
const PERSIST_DEBOUNCE_MS = 500;
|
||||||
const PERSISTENCE_FILENAME = 'flowchart-session.json';
|
const PERSISTENCE_FILENAME = 'flowchart-session.json';
|
||||||
|
|
||||||
|
const NODE_KIND_OPTIONS_TEXT = 'process, decision, terminator, subroutine, document';
|
||||||
|
const EDGE_KIND_OPTIONS_TEXT = 'solid, dotted, thick';
|
||||||
|
|
||||||
let _userDataPath = null;
|
let _userDataPath = null;
|
||||||
let _persistenceFile = null;
|
let _persistenceFile = null;
|
||||||
let _previewTimer = null;
|
let _previewTimer = null;
|
||||||
@@ -132,14 +137,25 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
_canvas = createCanvas(els.canvasHost, _store, {
|
_canvas = createCanvas(els.canvasHost, _store, {
|
||||||
onEdgeClick: (edgeId) => {
|
onEdgeClick: async (edgeId) => {
|
||||||
const edge = _store.getGraph().edges.find((e) => e.id === edgeId);
|
const edge = _store.getGraph().edges.find((e) => e.id === edgeId);
|
||||||
if (!edge) return;
|
if (!edge) return;
|
||||||
const nextKind = window.prompt('Edge kind (solid, dotted, thick):', edge.kind);
|
// v4.13.0 — replace window.prompt() with the inline modal helper.
|
||||||
|
// Two sequential prompts: kind first (with the current value as
|
||||||
|
// default), then label.
|
||||||
|
const nextKind = await promptInline({
|
||||||
|
title: 'Edge Style',
|
||||||
|
message: `Style (${EDGE_KIND_OPTIONS_TEXT}):`,
|
||||||
|
defaultValue: edge.kind,
|
||||||
|
});
|
||||||
if (nextKind && ['solid', 'dotted', 'thick'].includes(nextKind)) {
|
if (nextKind && ['solid', 'dotted', 'thick'].includes(nextKind)) {
|
||||||
_store.setEdgeKind(edgeId, nextKind);
|
_store.setEdgeKind(edgeId, nextKind);
|
||||||
}
|
}
|
||||||
const nextLabel = window.prompt('Edge label (empty to clear):', edge.label || '');
|
const nextLabel = await promptInline({
|
||||||
|
title: 'Edge Label',
|
||||||
|
message: 'Empty to clear:',
|
||||||
|
defaultValue: edge.label || '',
|
||||||
|
});
|
||||||
if (nextLabel !== null) {
|
if (nextLabel !== null) {
|
||||||
_store.setEdgeLabel(edgeId, nextLabel);
|
_store.setEdgeLabel(edgeId, nextLabel);
|
||||||
}
|
}
|
||||||
@@ -148,12 +164,33 @@
|
|||||||
// Canvas already paints the .selected highlight; nothing else
|
// Canvas already paints the .selected highlight; nothing else
|
||||||
// needed here for selection state.
|
// needed here for selection state.
|
||||||
},
|
},
|
||||||
onShapeMenu: (nodeId) => {
|
onShapeMenu: async (nodeId) => {
|
||||||
const next = window.prompt(
|
const node = _store.getGraph().nodes.find((n) => n.id === nodeId);
|
||||||
'New shape (process, decision, terminator, subroutine, document):'
|
if (!node) return;
|
||||||
);
|
const next = await promptInline({
|
||||||
|
title: 'Change Shape',
|
||||||
|
message: `New shape (${NODE_KIND_OPTIONS_TEXT}):`,
|
||||||
|
defaultValue: node.kind,
|
||||||
|
});
|
||||||
if (next) _store.setNodeKind(nodeId, next);
|
if (next) _store.setNodeKind(nodeId, next);
|
||||||
},
|
},
|
||||||
|
onEmptyClick: async (x, y) => {
|
||||||
|
// v4.13.0 — canvas click no longer auto-creates a process node
|
||||||
|
// with literal label "Node". User picks the shape kind first.
|
||||||
|
const kind = await promptInline({
|
||||||
|
title: 'Add Node',
|
||||||
|
message: `Shape (${NODE_KIND_OPTIONS_TEXT}):`,
|
||||||
|
defaultValue: 'process',
|
||||||
|
});
|
||||||
|
if (!kind) return;
|
||||||
|
const label = await promptInline({
|
||||||
|
title: 'Node Label',
|
||||||
|
message: 'Label (empty for "Node"):',
|
||||||
|
defaultValue: '',
|
||||||
|
});
|
||||||
|
const finalLabel = label && label.length > 0 ? label : 'Node';
|
||||||
|
_store.addNode({ kind, x, y, label: finalLabel });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
_store.subscribe(() => {
|
_store.subscribe(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user