System updated with many bug revised
This commit is contained in:
+118
-19
@@ -144,9 +144,16 @@
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.tree-folder summary {
|
||||
summary.tree-folder {
|
||||
font-weight: 500;
|
||||
color: var(--accent);
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
summary.tree-folder::marker {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.74rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.component-leaf {
|
||||
@@ -484,10 +491,11 @@
|
||||
}
|
||||
|
||||
.tree-summary-row {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
width: calc(100% - 18px);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.folder-icon {
|
||||
@@ -563,6 +571,33 @@
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.tree-delete-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid rgba(239, 68, 68, 0.45);
|
||||
border-radius: 6px;
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
color: #fca5a5;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
font-size: 0.78rem;
|
||||
line-height: 1;
|
||||
padding: 0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.tree-delete-btn:hover {
|
||||
background: rgba(239, 68, 68, 0.18);
|
||||
border-color: var(--danger);
|
||||
color: #fecaca;
|
||||
}
|
||||
|
||||
body.light-mode .tree-delete-btn {
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
details[open] > summary .tree-expander {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
@@ -1076,7 +1111,7 @@
|
||||
);
|
||||
};
|
||||
|
||||
const ProjectTreeNode = ({ name, children, onOpenComposite, onOpenProject, onSelectInstance, onRenameCanvas }) => {
|
||||
const ProjectTreeNode = ({ name, children, onOpenComposite, onOpenProject, onSelectInstance, onRenameCanvas, onDeleteCanvas }) => {
|
||||
if (children && children.__type__ === 'project') {
|
||||
const projectName = children.__name__ || name;
|
||||
const composites = children.composites || [];
|
||||
@@ -1088,12 +1123,12 @@
|
||||
<summary className="tree-folder" onDoubleClick={handleDoubleClick} style={{ cursor: 'pointer' }}>
|
||||
<span className="tree-summary-row">
|
||||
<span className="folder-icon project-folder"></span>
|
||||
<span className="tree-summary-name">Project - {name}</span>
|
||||
<span className="tree-summary-name">{name}</span>
|
||||
<span className="tree-expander">></span>
|
||||
</span>
|
||||
</summary>
|
||||
{composites.map(comp => (
|
||||
<ProjectTreeNode key={comp.pageId || comp.__name__} name={comp.__name__} children={comp} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} />
|
||||
<ProjectTreeNode key={comp.pageId || comp.__name__} name={comp.__name__} children={comp} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} onDeleteCanvas={onDeleteCanvas} />
|
||||
))}
|
||||
</details>
|
||||
);
|
||||
@@ -1125,8 +1160,14 @@
|
||||
};
|
||||
const handleOpen = (event) => {
|
||||
if (event.target.closest('.tree-expander')) return;
|
||||
if (event.target.closest('.tree-delete-btn')) return;
|
||||
if (onOpenComposite) onOpenComposite(cellName);
|
||||
};
|
||||
const handleDeleteCanvas = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (onDeleteCanvas) onDeleteCanvas(cellName);
|
||||
};
|
||||
return (
|
||||
<details>
|
||||
<summary className="tree-folder" draggable onDragStart={handleDragStart} onClick={handleOpen}>
|
||||
@@ -1139,6 +1180,7 @@
|
||||
onRename={onRenameCanvas}
|
||||
onOpen={() => onOpenComposite && onOpenComposite(cellName)}
|
||||
/>
|
||||
<button className="tree-delete-btn" type="button" title={`Delete ${cellName}`} onClick={handleDeleteCanvas}>x</button>
|
||||
<span className="tree-expander">></span>
|
||||
</span>
|
||||
</summary>
|
||||
@@ -1186,7 +1228,7 @@
|
||||
</span>
|
||||
</summary>
|
||||
{Object.entries(children).map(([childName, childData]) => (
|
||||
<ProjectTreeNode key={childName} name={childName} children={childData} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} />
|
||||
<ProjectTreeNode key={childName} name={childName} children={childData} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} onDeleteCanvas={onDeleteCanvas} />
|
||||
))}
|
||||
</details>
|
||||
);
|
||||
@@ -1229,7 +1271,7 @@
|
||||
return null;
|
||||
};
|
||||
|
||||
const LeftPanel = ({ projectTreeItems, library, treeKey, expanded, onToggle, treeRef, width, onOpenComposite, onOpenProject, onSelectInstance, onRenameCanvas, projectExpanded, onProjectToggle, projectTreeRef, projectTreeKey }) => {
|
||||
const LeftPanel = ({ projectTreeItems, library, treeKey, expanded, onToggle, treeRef, width, onOpenComposite, onOpenProject, onSelectInstance, onRenameCanvas, onDeleteCanvas, projectExpanded, onProjectToggle, projectTreeRef, projectTreeKey }) => {
|
||||
const [projectPanelHeight, setProjectPanelHeight] = useState(270);
|
||||
const [resizingProjectPanel, setResizingProjectPanel] = useState(false);
|
||||
const leftPanelRef = useRef(null);
|
||||
@@ -1278,11 +1320,11 @@
|
||||
projectTreeItems.map(item => {
|
||||
if (item.type === 'project') {
|
||||
return (
|
||||
<ProjectTreeNode key={item.name} name={item.name} children={{ __type__: 'project', __name__: item.name, composites: item.composites }} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} />
|
||||
<ProjectTreeNode key={item.name} name={item.name} children={{ __type__: 'project', __name__: item.name, composites: item.composites }} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} onDeleteCanvas={onDeleteCanvas} />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ProjectTreeNode key={item.name} name={item.name} children={{ __type__: 'composite', __name__: item.name, tree: item.tree || {}, pageId: item.pageId }} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} />
|
||||
<ProjectTreeNode key={item.name} name={item.name} children={{ __type__: 'composite', __name__: item.name, tree: item.tree || {}, pageId: item.pageId }} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} onDeleteCanvas={onDeleteCanvas} />
|
||||
);
|
||||
}
|
||||
})
|
||||
@@ -2266,6 +2308,7 @@
|
||||
}, [pages, library, syncAllCompositeTrees]);
|
||||
|
||||
const selectedNode = useMemo(() => currentNodes.find(n => n.selected), [currentNodes]);
|
||||
const openTabs = useMemo(() => pages.filter(page => !page.isClosed), [pages]);
|
||||
|
||||
const selectInstanceInPage = useCallback((pageName, instanceName) => {
|
||||
if (!pageName || !instanceName) return;
|
||||
@@ -2294,7 +2337,7 @@
|
||||
const existing = prev.find(p => p.name === name && p.type === 'project');
|
||||
if (existing) {
|
||||
setActivePageId(existing.id);
|
||||
return prev;
|
||||
return prev.map(p => p.id === existing.id ? { ...p, isClosed: false } : p);
|
||||
}
|
||||
const newProjectPage = {
|
||||
id: Date.now().toString() + Math.random().toString(36).substr(2, 5),
|
||||
@@ -2319,12 +2362,13 @@
|
||||
const existing = prev.find(p => p.name === name && p.type === 'composite');
|
||||
if (existing) {
|
||||
setActivePageId(existing.id);
|
||||
return prev;
|
||||
return prev.map(p => p.id === existing.id ? { ...p, isClosed: false } : p);
|
||||
}
|
||||
const newComposite = {
|
||||
id: Date.now().toString() + Math.random().toString(36).substr(2, 5),
|
||||
name: name,
|
||||
type: 'composite',
|
||||
isClosed: false,
|
||||
nodes: [
|
||||
{
|
||||
id: 'page-port',
|
||||
@@ -2413,6 +2457,7 @@
|
||||
id: Date.now().toString() + Math.random().toString(36).substr(2, 5),
|
||||
name: cellName,
|
||||
type: 'composite',
|
||||
isClosed: false,
|
||||
nodes: [
|
||||
{
|
||||
id: 'page-port',
|
||||
@@ -2438,17 +2483,62 @@
|
||||
|
||||
const closePage = useCallback((pageId) => {
|
||||
setPages(prev => {
|
||||
const pageToClose = prev.find(p => p.id === pageId);
|
||||
const filtered = prev.filter(p => p.id !== pageId);
|
||||
const closed = prev.map(p => p.id === pageId ? { ...p, isClosed: true } : p);
|
||||
if (activePageId === pageId) {
|
||||
const idx = prev.findIndex(p => p.id === pageId);
|
||||
const nextActive = filtered[Math.min(idx, filtered.length - 1)] || null;
|
||||
const openPages = closed.filter(p => !p.isClosed);
|
||||
const nextActive = openPages[Math.min(idx, openPages.length - 1)] || openPages[openPages.length - 1] || null;
|
||||
setActivePageId(nextActive ? nextActive.id : null);
|
||||
}
|
||||
return filtered;
|
||||
return closed;
|
||||
});
|
||||
}, [activePageId]);
|
||||
|
||||
const deleteCanvas = useCallback((cellName) => {
|
||||
if (!cellName) return;
|
||||
if (!window.confirm(`Delete canvas "${cellName}" from this project?`)) return;
|
||||
const pageToDelete = pages.find(p => p.type === 'composite' && p.name === cellName);
|
||||
setPages(prev => {
|
||||
const withoutCell = prev
|
||||
.filter(p => !(p.type === 'composite' && p.name === cellName))
|
||||
.map(p => {
|
||||
if (p.type !== 'project') return p;
|
||||
return {
|
||||
...p,
|
||||
nodes: p.nodes.filter(node => node.data?.componentName !== cellName),
|
||||
edges: p.edges.filter(edge => {
|
||||
const removedNodeIds = new Set(p.nodes.filter(node => node.data?.componentName === cellName).map(node => node.id));
|
||||
return !removedNodeIds.has(edge.source) && !removedNodeIds.has(edge.target);
|
||||
})
|
||||
};
|
||||
});
|
||||
if (activePageId === pageToDelete?.id) {
|
||||
const nextActive = withoutCell.find(p => !p.isClosed) || withoutCell[0] || null;
|
||||
setActivePageId(nextActive ? nextActive.id : null);
|
||||
}
|
||||
return withoutCell;
|
||||
});
|
||||
setProjectCompositeMap(prev => {
|
||||
const next = {};
|
||||
Object.entries(prev).forEach(([project, cells]) => {
|
||||
next[project] = cells.filter(name => name !== cellName);
|
||||
});
|
||||
return next;
|
||||
});
|
||||
setStandaloneComposites(prev => prev.filter(name => name !== cellName));
|
||||
setCompositeTrees(prev => {
|
||||
const next = { ...prev };
|
||||
delete next[cellName];
|
||||
return next;
|
||||
});
|
||||
fetch(`/api/projects/${encodeURIComponent(currentProjectName)}/cells/${encodeURIComponent(cellName)}`, {
|
||||
method: 'DELETE'
|
||||
}).then(response => {
|
||||
if (response.ok) addLog(`Deleted canvas "${cellName}".`);
|
||||
else addLog(`Canvas "${cellName}" was removed locally, but file delete failed.`);
|
||||
}).catch(() => addLog(`Canvas "${cellName}" was removed locally, but file delete failed.`));
|
||||
}, [pages, activePageId, currentProjectName, addLog]);
|
||||
|
||||
const switchPage = useCallback((pageId) => {
|
||||
setActivePageId(pageId);
|
||||
}, []);
|
||||
@@ -2680,7 +2770,15 @@
|
||||
|
||||
const projectTreeItems = useMemo(() => {
|
||||
const items = [];
|
||||
const projectPages = pages.filter(p => p.type === 'project');
|
||||
const projectPagesByName = new Map();
|
||||
pages
|
||||
.filter(p => p.type === 'project')
|
||||
.forEach(project => {
|
||||
if (!projectPagesByName.has(project.name) || project.id === activePageId) {
|
||||
projectPagesByName.set(project.name, project);
|
||||
}
|
||||
});
|
||||
const projectPages = Array.from(projectPagesByName.values());
|
||||
projectPages.forEach(project => {
|
||||
const projectNodeItems = project.nodes
|
||||
.filter(node => node.id !== 'page-port' && node.data?.componentName)
|
||||
@@ -2732,7 +2830,7 @@
|
||||
});
|
||||
});
|
||||
return items;
|
||||
}, [pages, library, projectCompositeMap, standaloneComposites, compositeTrees]);
|
||||
}, [pages, library, projectCompositeMap, standaloneComposites, compositeTrees, activePageId]);
|
||||
|
||||
const libraryWithCells = useMemo(() => {
|
||||
const cellEntries = {};
|
||||
@@ -2904,6 +3002,7 @@ ${bundlesBlock}`;
|
||||
onOpenProject={openProject}
|
||||
onSelectInstance={selectInstanceInPage}
|
||||
onRenameCanvas={renameCanvas}
|
||||
onDeleteCanvas={deleteCanvas}
|
||||
projectExpanded={projectExpanded}
|
||||
onProjectToggle={handleProjectToggle}
|
||||
projectTreeRef={projectTreeContainerRef}
|
||||
@@ -2919,7 +3018,7 @@ ${bundlesBlock}`;
|
||||
<div className="canvas-tab" style={{ cursor: 'pointer' }} onClick={createCell}>
|
||||
+ Cell
|
||||
</div>
|
||||
{pages.map(page => (
|
||||
{openTabs.map(page => (
|
||||
<div key={page.id} className={`canvas-tab ${page.id === activePageId ? 'active' : ''}`} onClick={() => switchPage(page.id)}>
|
||||
<EditableCanvasTabName page={page} active={page.id === activePageId} onRename={renameCanvas} />
|
||||
<button onClick={(e) => { e.stopPropagation(); closePage(page.id); }}>x</button>
|
||||
|
||||
Reference in New Issue
Block a user