UNPKG

testeranto

Version:

the AI powered BDD test framework for typescript projects

438 lines (437 loc) 18.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.SVGEditorPage = void 0; /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ const react_1 = __importStar(require("react")); const SVGAttributesEditor_1 = require("./SVGEditor/SVGAttributesEditor"); const SVGElementForm_1 = require("./SVGEditor/SVGElementForm"); const GenericXMLEditorPage_1 = require("./GenericXMLEditorPage"); const SVGPreview_1 = require("./SVGEditor/SVGPreview"); const SVGEditorPage = () => { const initialTree = { id: "root", type: "svg", attributes: { width: "400", height: "400", viewBox: "0 0 400 400", xmlns: "http://www.w3.org/2000/svg", }, children: [ { id: "rect-1", type: "rect", attributes: { x: "50", y: "50", width: "100", height: "100", fill: "blue", stroke: "black", "stroke-width": "2", }, children: [], } ], }; const [svgTree, setSvgTree] = (0, react_1.useState)(initialTree); const [selectedNodeId, setSelectedNodeId] = (0, react_1.useState)(null); const [activeTab, setActiveTab] = (0, react_1.useState)("preview"); const [history, setHistory] = (0, react_1.useState)([initialTree]); const [historyIndex, setHistoryIndex] = (0, react_1.useState)(0); const [editMode, setEditMode] = (0, react_1.useState)("none"); const [showGrid, setShowGrid] = (0, react_1.useState)(false); const [snapToGrid, setSnapToGrid] = (0, react_1.useState)(false); const [hiddenNodes, setHiddenNodes] = (0, react_1.useState)(new Set()); const [dragInfo, setDragInfo] = (0, react_1.useState)({ isDragging: false, nodeId: null, }); // Find a node by ID const findNode = (0, react_1.useCallback)((node, id) => { if (node.id === id) return node; for (const child of node.children) { const found = findNode(child, id); if (found) return found; } return null; }, []); // Helper to update tree and manage history const updateTree = (0, react_1.useCallback)((newTree) => { setSvgTree(newTree); // Add to history, truncating future history if we're not at the end setHistory((prevHistory) => { const newHistory = prevHistory.slice(0, historyIndex + 1); newHistory.push(newTree); return newHistory; }); setHistoryIndex((prevIndex) => prevIndex + 1); }, [historyIndex]); // Update node attributes const updateNodeAttributes = (0, react_1.useCallback)((nodeId, attributes) => { setSvgTree((prevTree) => { const newTree = JSON.parse(JSON.stringify(prevTree)); const node = findNode(newTree, nodeId); if (node) { node.attributes = Object.assign(Object.assign({}, node.attributes), attributes); } updateTree(newTree); return newTree; }); }, [findNode, updateTree]); // Update node text content const updateNodeTextContent = (0, react_1.useCallback)((nodeId, textContent) => { setSvgTree((prevTree) => { const newTree = JSON.parse(JSON.stringify(prevTree)); const node = findNode(newTree, nodeId); if (node) { // For text and tspan nodes, we need to handle text content // This is a simplified approach - in a real implementation, you'd track text content separately // For now, we'll add a special attribute to track the text content node.attributes['data-text-content'] = textContent; } updateTree(newTree); return newTree; }); }, [findNode, updateTree]); // Toggle node visibility const toggleNodeVisibility = (0, react_1.useCallback)((nodeId) => { setHiddenNodes((prev) => { const newHidden = new Set(prev); if (newHidden.has(nodeId)) { newHidden.delete(nodeId); } else { newHidden.add(nodeId); } return newHidden; }); }, []); // Handle drag start const handleDragStart = (0, react_1.useCallback)((nodeId) => { setDragInfo({ isDragging: true, nodeId }); }, []); // Handle drag end const handleDragEnd = (0, react_1.useCallback)(() => { setDragInfo({ isDragging: false, nodeId: null }); }, []); // Move a node const moveNode = (0, react_1.useCallback)((nodeId, newParentId, index) => { setSvgTree((prevTree) => { const newTree = JSON.parse(JSON.stringify(prevTree)); // Find the node to move and its original parent let nodeToMove = null; let oldParent = null; const findNodeAndParent = (current, parent = null) => { if (current.id === nodeId) { nodeToMove = current; oldParent = parent; return true; } for (const child of current.children) { if (findNodeAndParent(child, current)) return true; } return false; }; findNodeAndParent(newTree); if (!nodeToMove || !oldParent) return prevTree; // Remove from old parent oldParent.children = oldParent.children.filter((child) => child.id !== nodeId); // Find new parent let targetParent = null; if (newParentId === null) { targetParent = newTree; } else { const findParent = (current) => { if (current.id === newParentId) return current; for (const child of current.children) { const found = findParent(child); if (found) return found; } return null; }; targetParent = findParent(newTree); } if (!targetParent) return prevTree; // Insert at specified index targetParent.children.splice(index, 0, nodeToMove); updateTree(newTree); return newTree; }); }, [updateTree]); // Add a new child node const addChildNode = (0, react_1.useCallback)((parentId, nodeType) => { setSvgTree((prevTree) => { const newTree = JSON.parse(JSON.stringify(prevTree)); const parent = findNode(newTree, parentId); if (parent) { // Default attributes based on node type to make them visible let defaultAttributes = {}; switch (nodeType) { case "rect": defaultAttributes = { x: "50", y: "50", width: "100", height: "100", fill: "blue", stroke: "black", "stroke-width": "2", }; break; case "circle": defaultAttributes = { cx: "100", cy: "100", r: "50", fill: "red", stroke: "black", "stroke-width": "2", }; break; case "path": defaultAttributes = { d: "M50 50 L150 150", stroke: "green", "stroke-width": "3", fill: "none", }; break; case "text": defaultAttributes = { x: "50", y: "50", fill: "black", "font-size": "16", }; break; case "g": defaultAttributes = { transform: "translate(0,0)", }; break; default: defaultAttributes = {}; } const newNode = { id: `${nodeType}-${Date.now()}`, type: nodeType, attributes: defaultAttributes, children: [], }; parent.children = [...parent.children, newNode]; setSelectedNodeId(newNode.id); updateTree(newTree); } return newTree; }); }, [findNode, updateTree]); // Remove a node const removeNode = (0, react_1.useCallback)((nodeId) => { setSvgTree((prevTree) => { const newTree = JSON.parse(JSON.stringify(prevTree)); // Function to remove node from its parent's children const removeFromParent = (node, targetId) => { for (let i = 0; i < node.children.length; i++) { if (node.children[i].id === targetId) { node.children.splice(i, 1); return true; } if (removeFromParent(node.children[i], targetId)) { return true; } } return false; }; removeFromParent(newTree, nodeId); if (selectedNodeId === nodeId) { setSelectedNodeId(null); } updateTree(newTree); return newTree; }); }, [selectedNodeId, updateTree]); // Undo/Redo handlers const handleUndo = (0, react_1.useCallback)(() => { if (historyIndex > 0) { setHistoryIndex((prevIndex) => prevIndex - 1); setSvgTree(history[historyIndex - 1]); } }, [history, historyIndex]); const handleRedo = (0, react_1.useCallback)(() => { if (historyIndex < history.length - 1) { setHistoryIndex((prevIndex) => prevIndex + 1); setSvgTree(history[historyIndex + 1]); } }, [history, historyIndex]); // Move handler - for now, just log to console const handleMove = (0, react_1.useCallback)(() => { console.log("Move functionality to be implemented"); // This would typically involve setting up a mode where nodes can be dragged }, []); // Helper variables for edit modes const isMoveMode = editMode === "move"; const isScaleMode = editMode === "scale"; const isRotateMode = editMode === "rotate"; const selectedNode = selectedNodeId ? findNode(svgTree, selectedNodeId) : null; // Define node types for SVG const svgNodeTypes = [ { label: 'Rectangle', type: 'rect' }, { label: 'Circle', type: 'circle' }, { label: 'Path', type: 'path' }, { label: 'Text', type: 'text' }, { label: 'Group', type: 'g' } ]; // Function to create new SVG nodes const createSVGNode = (parentId, nodeType) => { // Default attributes based on node type let defaultAttributes = {}; switch (nodeType) { case "rect": defaultAttributes = { x: "50", y: "50", width: "100", height: "100", fill: "blue", stroke: "black", "stroke-width": "2", }; break; case "circle": defaultAttributes = { cx: "100", cy: "100", r: "50", fill: "red", stroke: "black", "stroke-width": "2", }; break; case "path": defaultAttributes = { d: "M50 50 L150 150", stroke: "green", "stroke-width": "3", fill: "none", }; break; case "text": defaultAttributes = { x: "50", y: "50", fill: "black", "font-size": "16", }; break; case "g": defaultAttributes = { transform: "translate(0,0)", }; break; default: defaultAttributes = {}; } return { id: `${nodeType}-${Date.now()}`, type: nodeType, attributes: defaultAttributes, children: [], }; }; // Render preview for SVG const renderSVGPreview = (node, isSelected, eventHandlers) => { const { type, attributes, children, textContent } = node; const style = isSelected ? { outline: '2px dashed blue' } : {}; // Convert children to React elements const childElements = children.map(child => renderSVGPreview(child, selectedNodeId === child.id, {})); // Handle text content for text elements if (type === 'text' || type === 'tspan') { return react_1.default.createElement(type, Object.assign(Object.assign(Object.assign({ key: node.id }, attributes), eventHandlers), { style }), textContent, ...childElements); } // For other SVG elements // Note: React.createElement expects the type to be a string for HTML/SVG elements // Make sure the type is always lowercase const elementType = type.toLowerCase(); return react_1.default.createElement(elementType, Object.assign(Object.assign(Object.assign({ key: node.id }, attributes), eventHandlers), { style }), childElements); }; // Attribute editor for SVG const renderSVGAttributeEditor = (node, onUpdateAttributes, onUpdateTextContent) => { // Use SVGElementForm for supported types, fallback to SVGAttributesEditor if (["circle", "rect", "g"].includes(node.type)) { return (react_1.default.createElement(SVGElementForm_1.SVGElementForm, { elementType: node.type, attributes: node.attributes, onChange: onUpdateAttributes })); } else { return (react_1.default.createElement(SVGAttributesEditor_1.SVGAttributesEditor, { node: node, onUpdateAttributes: onUpdateAttributes, onUpdateTextContent: onUpdateTextContent })); } }; // Custom SVG preview that uses the original SVGPreview component const SvgCustomPreview = () => { // Convert XMLNode to SVGNode const svgTree = { id: xmlTree.id, type: xmlTree.type, attributes: xmlTree.attributes, children: xmlTree.children }; return (react_1.default.createElement(SVGPreview_1.SVGPreview, { svgTree: svgTree, editMode: editMode, showGrid: showGrid, selectedNodeId: selectedNodeId, onNodeInteraction: (nodeId, updates) => { // Update node attributes based on interaction updateNodeAttributes(nodeId, updates); }, hiddenNodes: hiddenNodes })); }; // Modify renderSVGPreview to use our custom preview const renderSVGPreviewWithControls = (node, isSelected, eventHandlers) => { // For the generic preview, we'll still use the basic rendering // The SVG-specific interactions are handled by SVGPreview component return renderSVGPreview(node, isSelected, eventHandlers); }; // For the SVG editor, we'll need to create a custom implementation // that properly integrates the SVGPreview component // Let's use the GenericXMLEditorPage but override the preview when in SVG mode return (react_1.default.createElement(GenericXMLEditorPage_1.GenericXMLEditorPage, { initialTree: initialTree, renderPreview: renderSVGPreviewWithControls, attributeEditor: renderSVGAttributeEditor, nodeTypes: svgNodeTypes, onAddNode: createSVGNode })); }; exports.SVGEditorPage = SVGEditorPage;