Remove ColorNode
This commit is contained in:
parent
552f351949
commit
1b5d7735ce
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"id": "1",
|
|
||||||
"type": "custom",
|
|
||||||
"data": {
|
|
||||||
"label": "Node 1",
|
|
||||||
"icon": "https://s3.dvirlabs.com/lab-icons/dev-tools/argocd.svg"
|
|
||||||
},
|
|
||||||
"position": {
|
|
||||||
"x": 307.58165778037267,
|
|
||||||
"y": 195.1586839847007
|
|
||||||
},
|
|
||||||
"width": 103,
|
|
||||||
"height": 103
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"edges": []
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { Handle, Position } from 'reactflow';
|
|
||||||
|
|
||||||
function ColorNode({ data }) {
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
background: 'white',
|
|
||||||
border: '2px solid #ff0071',
|
|
||||||
borderRadius: 8,
|
|
||||||
padding: 10,
|
|
||||||
width: 120,
|
|
||||||
textAlign: 'center'
|
|
||||||
}}>
|
|
||||||
<Handle type="target" position={Position.Left} />
|
|
||||||
<div style={{ fontSize: 12, marginBottom: 6 }}>🎨 Shape Color</div>
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
value={data.color || '#ff0071'}
|
|
||||||
onChange={(e) => data.onChange?.(e.target.value)}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
/>
|
|
||||||
<Handle type="source" position={Position.Right} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ColorNode;
|
|
||||||
@ -17,59 +17,45 @@ import CustomNode from './CustomNode';
|
|||||||
import IconSelector from './IconSelector';
|
import IconSelector from './IconSelector';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import RouterNode from './RouterNode';
|
import RouterNode from './RouterNode';
|
||||||
import ColorNode from './ColorNode';
|
|
||||||
|
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
custom: CustomNode,
|
custom: CustomNode,
|
||||||
router: RouterNode,
|
router: RouterNode,
|
||||||
color: ColorNode,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function Diagram() {
|
function Diagram() {
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
const [selectedNode, setSelectedNode] = useState(null);
|
const [selectedNode, setSelectedNode] = useState(null);
|
||||||
|
const [selectedEdge, setSelectedEdge] = useState(null);
|
||||||
const [showForm, setShowForm] = useState(false);
|
const [showForm, setShowForm] = useState(false);
|
||||||
const [newLabel, setNewLabel] = useState('');
|
const [newLabel, setNewLabel] = useState('');
|
||||||
const [newType, setNewType] = useState('custom');
|
const [newType, setNewType] = useState('custom');
|
||||||
const [selectedIcon, setSelectedIcon] = useState('');
|
const [selectedIcon, setSelectedIcon] = useState('');
|
||||||
const [selectedEdge, setSelectedEdge] = useState(null);
|
|
||||||
const [diagramName, setDiagramName] = useState(null);
|
const [diagramName, setDiagramName] = useState(null);
|
||||||
const [diagramList, setDiagramList] = useState([]);
|
const [diagramList, setDiagramList] = useState([]);
|
||||||
const isIframe = window.self !== window.top;
|
const isIframe = window.self !== window.top;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
|
||||||
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
try {
|
try {
|
||||||
const { diagrams } = await listDiagrams();
|
const { diagrams } = await listDiagrams();
|
||||||
if (isMounted) {
|
|
||||||
setDiagramList(diagrams);
|
setDiagramList(diagrams);
|
||||||
|
|
||||||
if (diagrams.length > 0) {
|
if (diagrams.length > 0) {
|
||||||
setDiagramName((prev) => prev || diagrams[0]);
|
setDiagramName((prev) => prev || diagrams[0]);
|
||||||
} else {
|
} else {
|
||||||
setDiagramName(null);
|
setDiagramName(null);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
setDiagramList([]);
|
setDiagramList([]);
|
||||||
setDiagramName(null);
|
setDiagramName(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
load();
|
load();
|
||||||
return () => {
|
|
||||||
isMounted = false;
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
|
||||||
|
|
||||||
const loadDiagram = async () => {
|
const loadDiagram = async () => {
|
||||||
if (!diagramName) {
|
if (!diagramName) {
|
||||||
setNodes([]);
|
setNodes([]);
|
||||||
@ -78,18 +64,13 @@ function Diagram() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const data = await fetchDiagramByName(diagramName);
|
const data = await fetchDiagramByName(diagramName);
|
||||||
if (isMounted) {
|
|
||||||
setNodes(data.nodes || []);
|
setNodes(data.nodes || []);
|
||||||
const sanitizedEdges = (data.edges || []).map(({ id, source, target, animated }) => ({
|
const sanitizedEdges = (data.edges || []).map(({ id, source, target, animated }) => ({
|
||||||
id,
|
id, source, target, animated: !!animated
|
||||||
source,
|
|
||||||
target,
|
|
||||||
animated: !!animated,
|
|
||||||
}));
|
}));
|
||||||
setEdges(sanitizedEdges);
|
setEdges(sanitizedEdges);
|
||||||
setSelectedNode(null);
|
setSelectedNode(null);
|
||||||
setSelectedEdge(null);
|
setSelectedEdge(null);
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
toast.error(`❌ Failed to load diagram "${diagramName}"`);
|
toast.error(`❌ Failed to load diagram "${diagramName}"`);
|
||||||
@ -97,29 +78,18 @@ function Diagram() {
|
|||||||
setEdges([]);
|
setEdges([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadDiagram();
|
loadDiagram();
|
||||||
return () => {
|
}, [diagramName]);
|
||||||
isMounted = false;
|
|
||||||
};
|
|
||||||
}, [diagramName, setNodes, setEdges]);
|
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (!diagramName) return;
|
if (!diagramName) return;
|
||||||
const cleanedEdges = edges.map(({ id, source, target, animated }) => ({
|
const cleanedEdges = edges.map(({ id, source, target, animated }) => ({
|
||||||
id,
|
id, source, target, animated: !!animated
|
||||||
source,
|
|
||||||
target,
|
|
||||||
animated: !!animated,
|
|
||||||
}));
|
}));
|
||||||
await saveDiagramByName(diagramName, { nodes, edges: cleanedEdges });
|
await saveDiagramByName(diagramName, { nodes, edges: cleanedEdges });
|
||||||
toast.success(`✅ Diagram "${diagramName}" saved!`);
|
toast.success(`✅ Diagram "${diagramName}" saved!`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddNode = () => {
|
|
||||||
setShowForm(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmitNode = () => {
|
const handleSubmitNode = () => {
|
||||||
const id = (nodes.length + 1).toString();
|
const id = (nodes.length + 1).toString();
|
||||||
const label = newLabel || `Node ${id}`;
|
const label = newLabel || `Node ${id}`;
|
||||||
@ -128,9 +98,7 @@ function Diagram() {
|
|||||||
const newNode = {
|
const newNode = {
|
||||||
id,
|
id,
|
||||||
type: newType,
|
type: newType,
|
||||||
data: newType === 'custom'
|
data: newType === 'custom' ? { label, icon } : { label },
|
||||||
? { label, icon }
|
|
||||||
: { label }, // router and other types don't have icons
|
|
||||||
position: { x: Math.random() * 400, y: Math.random() * 300 },
|
position: { x: Math.random() * 400, y: Math.random() * 300 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -150,6 +118,11 @@ function Diagram() {
|
|||||||
setSelectedNode(null);
|
setSelectedNode(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onConnect = useCallback(
|
||||||
|
(params) => setEdges((eds) => addEdge({ ...params, animated: true }, eds)),
|
||||||
|
[setEdges]
|
||||||
|
);
|
||||||
|
|
||||||
const onEdgeClick = (_, edge) => {
|
const onEdgeClick = (_, edge) => {
|
||||||
setSelectedEdge(edge);
|
setSelectedEdge(edge);
|
||||||
setSelectedNode(null);
|
setSelectedNode(null);
|
||||||
@ -162,12 +135,9 @@ function Diagram() {
|
|||||||
setSelectedEdge(null);
|
setSelectedEdge(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onConnect = useCallback(
|
const onNodeClick = (_, node) => setSelectedNode(node);
|
||||||
(params) => setEdges((eds) => addEdge({ ...params, animated: true }, eds)),
|
|
||||||
[setEdges]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleNodeDoubleClick = (event, node) => {
|
const handleNodeDoubleClick = (_, node) => {
|
||||||
const newLabel = prompt('Enter new name:', node.data.label);
|
const newLabel = prompt('Enter new name:', node.data.label);
|
||||||
if (newLabel !== null) {
|
if (newLabel !== null) {
|
||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
@ -179,19 +149,12 @@ function Diagram() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNodeClick = (_, node) => {
|
|
||||||
setSelectedNode(node);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteDiagram = async () => {
|
const handleDeleteDiagram = async () => {
|
||||||
if (!diagramName || diagramName === 'default') {
|
if (!diagramName || diagramName === 'default') {
|
||||||
toast.warn("❌ Cannot delete 'default' diagram or nothing selected.");
|
toast.warn("❌ Cannot delete 'default' diagram or nothing selected.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!confirm(`Delete "${diagramName}"?`)) return;
|
||||||
const confirmDelete = confirm(`Are you sure you want to delete diagram "${diagramName}"?`);
|
|
||||||
if (!confirmDelete) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteDiagramByName(diagramName);
|
await deleteDiagramByName(diagramName);
|
||||||
const updatedList = diagramList.filter(name => name !== diagramName);
|
const updatedList = diagramList.filter(name => name !== diagramName);
|
||||||
@ -208,8 +171,8 @@ function Diagram() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100vw', height: '100vh' }}>
|
<div style={{ width: '100vw', height: '100vh' }}>
|
||||||
<div style={{ position: 'absolute', zIndex: 10, right: 10, top: 10, display: 'flex', alignItems: 'center', gap: '10px', height: '48px' }}>
|
<div style={{ position: 'absolute', zIndex: 10, right: 10, top: 10, display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||||
<div style={{ position: 'relative', display: 'flex', alignItems: 'center' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
<select
|
<select
|
||||||
value={diagramName || ''}
|
value={diagramName || ''}
|
||||||
onChange={(e) => setDiagramName(e.target.value)}
|
onChange={(e) => setDiagramName(e.target.value)}
|
||||||
@ -229,22 +192,12 @@ function Diagram() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{diagramList.map((name) => (
|
{diagramList.map((name) => (
|
||||||
<option key={name} value={name} style={{ color: 'white', font: 'bold 16px Arial' }}>
|
<option key={name} value={name}>
|
||||||
📌 {name}
|
📌 {name}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<div
|
<div style={{ position: 'absolute', right: 12, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none', color: 'white' }}>
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 12,
|
|
||||||
top: '50%',
|
|
||||||
transform: 'translateY(-50%)',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
fontSize: 16,
|
|
||||||
color: 'white',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
⏷
|
⏷
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -255,43 +208,36 @@ function Diagram() {
|
|||||||
const newName = prompt("Enter new diagram name:");
|
const newName = prompt("Enter new diagram name:");
|
||||||
if (newName) {
|
if (newName) {
|
||||||
if (!diagramList.includes(newName)) {
|
if (!diagramList.includes(newName)) {
|
||||||
const updatedList = [...diagramList, newName];
|
setDiagramList((prev) => [...prev, newName]);
|
||||||
setDiagramList(updatedList);
|
|
||||||
}
|
}
|
||||||
setDiagramName(newName);
|
setDiagramName(newName);
|
||||||
setNodes([]);
|
setNodes([]);
|
||||||
setEdges([]);
|
setEdges([]);
|
||||||
setSelectedNode(null);
|
|
||||||
setSelectedEdge(null);
|
|
||||||
}
|
}
|
||||||
}}>🆕 New Diagram</button>
|
}}>🆕 New Diagram</button>
|
||||||
|
|
||||||
<button className="btn" onClick={handleSave} style={{ background: 'green' }}>💾 Save</button>
|
<button className="btn" onClick={handleSave} style={{ background: 'green' }}>💾 Save</button>
|
||||||
<button className="btn" onClick={handleAddNode} style={{ background: 'blue' }}>➕ Add Node</button>
|
<button className="btn" onClick={() => setShowForm(true)} style={{ background: 'blue' }}>➕ Add Node</button>
|
||||||
|
<button className="btn" onClick={handleDeleteNode} style={{ background: selectedNode ? '#b81a1a' : '#ccc' }} disabled={!selectedNode}>🗑️ Delete Node</button>
|
||||||
<button className="btn" onClick={handleDeleteNode} style={{ background: selectedNode ? '#b81a1a' : '#ccc', color: selectedNode ? 'white' : '#666', cursor: selectedNode ? 'pointer' : 'not-allowed' }} disabled={!selectedNode}>🗑️ Delete Node</button>
|
<button className="btn" onClick={handleDeleteEdge} style={{ background: selectedEdge ? '#b81a1a' : '#ccc' }} disabled={!selectedEdge}>🗑️ Delete Edge</button>
|
||||||
|
|
||||||
<button className="btn" onClick={handleDeleteEdge} style={{ background: selectedEdge ? '#b81a1a' : '#ccc', color: selectedEdge ? 'white' : '#666', cursor: selectedEdge ? 'pointer' : 'not-allowed' }} disabled={!selectedEdge}>🗑️ Delete Edge</button>
|
|
||||||
|
|
||||||
<button className="btn" onClick={handleDeleteDiagram} style={{ background: 'red' }}>🗑️ Delete Diagram</button>
|
<button className="btn" onClick={handleDeleteDiagram} style={{ background: 'red' }}>🗑️ Delete Diagram</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<div style={{ position: 'absolute', zIndex: 20, right: 10, top: 80, background: '#e6f0fa', padding: '16px', borderRadius: '12px', boxShadow: '0 4px 8px rgba(0,0,0,0.15)', width: 280 }}>
|
<div style={{ position: 'absolute', zIndex: 20, right: 10, top: 80, background: '#13141a', padding: '16px', borderRadius: '12px', boxShadow: '0 4px 8px rgba(0,0,0,0.15)', width: 280 }}>
|
||||||
<h4 style={{ color: '#125ea8', marginBottom: 12, fontSize: 18, display: 'flex', alignItems: 'center', gap: 8 }}>🧩 Add New Node</h4>
|
<h4 style={{ color: 'white', marginBottom: 12 }}>🧩 Add New Node</h4>
|
||||||
|
|
||||||
<label style={{ fontWeight: 'bold', marginBottom: 4, display: 'block', color: '#333' }}>Label:</label>
|
<label style={{ fontWeight: 'bold', color: 'white' }}>Label:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Enter label"
|
|
||||||
value={newLabel}
|
value={newLabel}
|
||||||
onChange={(e) => setNewLabel(e.target.value)}
|
onChange={(e) => setNewLabel(e.target.value)}
|
||||||
style={{ padding: '6px', width: '100%', borderRadius: 6, border: '1px solid #ccc', marginBottom: 12 }}
|
style={{ padding: '6px', width: '100%', borderRadius: 6, border: '1px solid #ccc', marginBottom: 12 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<label style={{ fontWeight: 'bold', marginBottom: 4, display: 'block', color: '#333' }}>Type:</label>
|
<label style={{ fontWeight: 'bold', color: 'white' }}>Type:</label>
|
||||||
<select
|
<select
|
||||||
value={newType}
|
value={newType}
|
||||||
onChange={(e) => setNewType(e.target.value)}
|
onChange={(e) => setNewType(e.target.value)}
|
||||||
@ -299,12 +245,9 @@ function Diagram() {
|
|||||||
>
|
>
|
||||||
<option value="custom">🧱 Custom</option>
|
<option value="custom">🧱 Custom</option>
|
||||||
<option value="router">📡 Router</option>
|
<option value="router">📡 Router</option>
|
||||||
<option value="color">🎨 Color Node</option>
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{newType === 'custom' && (
|
{newType === 'custom' && <IconSelector onSelect={setSelectedIcon} />}
|
||||||
<IconSelector onSelect={setSelectedIcon} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div style={{ marginTop: 12 }}>
|
<div style={{ marginTop: 12 }}>
|
||||||
<button className="btn" onClick={handleSubmitNode}>✅ Add</button>
|
<button className="btn" onClick={handleSubmitNode}>✅ Add</button>
|
||||||
|
|||||||
@ -25,7 +25,7 @@ function IconSelector({ onSelect }) {
|
|||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: '1rem' }}>
|
<div style={{ marginBottom: '1rem' }}>
|
||||||
<div style={{ marginBottom: '0.5rem' }}>
|
<div style={{ marginBottom: '0.5rem' }}>
|
||||||
<label style={{ fontWeight: 'bold', color: '#333' }}>Category: </label>
|
<label style={{ fontWeight: 'bold', color: 'white' }}>Category: </label>
|
||||||
<select
|
<select
|
||||||
value={selectedCategory}
|
value={selectedCategory}
|
||||||
onChange={handleCategoryChange}
|
onChange={handleCategoryChange}
|
||||||
@ -47,7 +47,7 @@ function IconSelector({ onSelect }) {
|
|||||||
|
|
||||||
{selectedCategory && (
|
{selectedCategory && (
|
||||||
<div>
|
<div>
|
||||||
<label style={{ fontWeight: 'bold', color: 'black' }}>Choose an icon:</label>
|
<label style={{ fontWeight: 'bold', color: 'white' }}>Choose an icon:</label>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
@ -81,7 +81,7 @@ function IconSelector({ onSelect }) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{selectedIcon && (
|
{selectedIcon && (
|
||||||
<p style={{ marginTop: 8, fontSize: 13, color: 'black' }}>
|
<p style={{ marginTop: 8, fontSize: 13, color: 'white' }}>
|
||||||
Selected: <code>{selectedIcon.split('/').pop()}</code>
|
Selected: <code>{selectedIcon.split('/').pop()}</code>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user