Files
docmd-io--docmd/packages/ui/templates/navigation.ejs
T
wehub-resource-sync 6db8fca185
docmd CI verification / verify (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:31:55 +08:00

99 lines
3.9 KiB
Plaintext

<%# ---------------------------------------------------------------
# docmd : the zero-config documentation engine.
# @website https://docmd.io
# [docmd-source] - Please do not remove this header.
# ---------------------------------------------------------------
%>
<nav class="sidebar-nav" aria-label="<%= typeof t === 'function' ? t('mainNavigation') : 'Main Navigation' %>">
<ul>
<%
// normalizePath: used ONLY for active-state comparison (not for href generation).
// All href generation goes through buildRelativeUrl() which delegates to the
// centralized URL engine in @docmd/parser.
function normalizePath(p) {
if (!p || p === '#') return '#';
if (p.startsWith('http')) return p;
let path = p.replace(/^(\.\/|\/)+/, '');
path = path.replace(/(\/index\.html|index\.html|\/README\.md|README\.md|\.html|\.md)$/i, '');
path = path.replace(/\/$/, '');
return '/' + path;
}
function isChildActive(item, currentPath) { if
(!item.children) return false; const currentNorm=normalizePath(currentPath); return item.children.some(child=> {
const childNorm = normalizePath(child.path);
if (childNorm === currentNorm) return true;
return isChildActive(child, currentPath);
});
}
function renderNav(items) {
if (!items || !Array.isArray(items)) return;
items.forEach(item => {
const isExternal = item.external || false;
const hasChildren = item.children && item.children.length > 0;
const isInteractive = hasChildren && item.collapsible !== false;
const itemNorm = normalizePath(item.path);
const currentNorm = normalizePath(currentPagePath);
const isDummyLink = !item.path || item.path === '#' || item.path === 'javascript:;';
const isActive = itemNorm === currentNorm;
const isParentOfActive = !isActive && isChildActive(item, currentPagePath);
const isOpen = isInteractive
? (isParentOfActive || (isActive && isInteractive))
: true;
const liClasses = [];
if (isActive) liClasses.push('active');
if (isParentOfActive) liClasses.push('active-parent');
if (isInteractive) liClasses.push('collapsible');
if (hasChildren && isOpen) liClasses.push('expanded');
if (hasChildren) liClasses.push('nav-group');
let href = item.path || '#';
if (!isExternal && !isDummyLink) {
href = buildRelativeUrl(href);
}
%>
<li class="<%= liClasses.join(' ') %>" <% if(isInteractive) { %> aria-expanded="<%= isOpen %>" <% } %>>
<% if (isDummyLink) { %>
<span class="nav-label <%= hasChildren ? 'nav-group' : '' %>">
<% if (item.icon) { %> <%- renderIcon(item.icon) %>
<% } %>
<span class="nav-item-title">
<%= item.title %>
</span>
<% if (isInteractive) { %>
<span class="collapse-icon-wrapper"><%- renderIcon('chevron-right', { class: 'collapse-icon'
}) %></span>
<% } %>
</span>
<% } else { %>
<a href="<%= href %>" class="<%= isActive ? 'active' : '' %> <%= hasChildren ? 'nav-group' : '' %>"
<%=isExternal ? 'target="_blank" rel="noopener"' : '' %>>
<% if (item.icon) { %> <%- renderIcon(item.icon) %>
<% } %>
<span class="nav-item-title">
<%= item.title %>
</span>
<% if (isInteractive) { %>
<span class="collapse-icon-wrapper"><%- renderIcon('chevron-right', { class: 'collapse-icon'
}) %></span>
<% } %>
<% if (isExternal) { %> <%- renderIcon('external-link', { class: 'nav-external-icon' }) %>
<% } %>
</a>
<% } %>
<% if (hasChildren) { %>
<ul class="submenu">
<% renderNav(item.children); %>
</ul>
<% } %>
</li>
<% }); } renderNav(navItems); %>
</ul>
</nav>