Files
simplenote-web/public/js/components/sidebar.js
Hiro d7bb018c83 fix: prevent event propagation issues in sidebar and editor
- Add type=button to Cancel/Save buttons in editor to prevent form submission
- Add href=# and return false to quick-links to prevent default anchor behavior
- StopPropagation was already present in sidebar handlers (verified)
- Document view buttons already had type=button (verified)

These fixes ensure:
- Library/tag clicks don't bubble up and trigger wrong handlers
- Quick-link buttons don't cause unexpected navigation
- Editor buttons don't accidentally submit forms
2026-03-28 12:45:22 +00:00

100 lines
3.7 KiB
JavaScript

// Sidebar Component
export function renderSidebar({ libraries, tags, selectedLibrary, selectedTag, onSelectLibrary, onSelectTag, onHome }) {
const buildLibraryTree = (libs, parentId = null, depth = 0) => {
return libs
.filter(l => l.parentId === parentId)
.map(lib => {
const children = libs.filter(l => l.parentId === lib.id);
const hasChildren = children.length > 0;
const isSelected = selectedLibrary === lib.id;
return `
<div class="tree-node">
<div class="tree-item ${isSelected ? 'active' : ''}" data-action="library" data-library-id="${lib.id}">
<span class="tree-toggle ${hasChildren ? 'expanded' : ''}" style="padding-left:${depth * 12}px">
${hasChildren ? '▶' : ''}
</span>
<span class="icon">📁</span>
<span class="label">${escapeHtml(lib.name)}</span>
</div>
${hasChildren ? `<div class="tree-children">${buildLibraryTree(libraries, lib.id, depth + 1)}</div>` : ''}
</div>
`;
})
.join('');
};
// Store callbacks in a way that's safe and doesn't rely on inline script execution
const callbacks = {
onSelectLibrary,
onSelectTag,
onHome
};
return `
<aside class="sidebar">
<div class="sidebar-scroll">
<div class="sidebar-section">
<h3>📚 Libraries</h3>
<div class="library-tree">
<div class="tree-item ${!selectedLibrary ? 'active' : ''}" data-action="home">
<span class="icon">🏠</span>
<span class="label">All Documents</span>
</div>
${buildLibraryTree(libraries)}
</div>
</div>
<div class="sidebar-section">
<h3>🏷️ Tags</h3>
<div class="tag-list">
${tags.map(tag => `
<div class="tag-item ${selectedTag === tag.name ? 'active' : ''}" data-action="tag" data-tag="${escapeHtml(tag.name)}">
<span>#${escapeHtml(tag.name)}</span>
<span class="tag-count">${tag.count}</span>
</div>
`).join('')}
</div>
</div>
<div class="quick-links">
<a class="quick-link" data-action="home">📋 All Documents</a>
<a class="quick-link" href="#" onclick="window.showNewDocModal(); return false;">📄 New Document</a>
<a class="quick-link" href="#" onclick="window.showNewLibraryModal(); return false;">📁 New Library</a>
</div>
</div>
</aside>
<script>
(function() {
var callbacks = window.__sidebarCallbacks;
document.querySelectorAll('[data-action="home"]').forEach(function(el) {
el.addEventListener('click', function(e) {
e.stopPropagation();
if (callbacks && callbacks.onHome) callbacks.onHome();
});
});
document.querySelectorAll('[data-action="library"]').forEach(function(el) {
el.addEventListener('click', function(e) {
e.stopPropagation();
var id = this.getAttribute('data-library-id');
if (callbacks && callbacks.onSelectLibrary) callbacks.onSelectLibrary(id);
});
});
document.querySelectorAll('[data-action="tag"]').forEach(function(el) {
el.addEventListener('click', function(e) {
e.stopPropagation();
var tag = this.getAttribute('data-tag');
if (callbacks && callbacks.onSelectTag) callbacks.onSelectTag(tag);
});
});
})();
</script>
`;
}
function escapeHtml(str) {
if (!str) return '';
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}