From: Kai Moritz Date: Sun, 14 Jun 2026 09:12:36 +0000 (+0000) Subject: BlogNav: vollständige About-Subtree mit korrekter off-Logik X-Git-Url: https://juplo.de/gitweb/?a=commitdiff_plain;h=11801d09c4cbfd53d60da42081365820810c24a0;p=website BlogNav: vollständige About-Subtree mit korrekter off-Logik Statischer About-Seitenbaum aus Hugo content/about/-Struktur: /about.html → /contact.html, /impressum.html (mit 5 Unterseiten), /agb.html aboutPageOff() implementiert Hugo tree.html-Logik: sichtbar wenn isCurrent || isAncestor || isSibling(nicht section-index) || isChild Korrekturen gegenüber Vorgängerversion: - onAboutSection (inSubtree) statt onAbout (nur /about.html) → activeSection 'about' auch auf Impressum/Datenschutz → blogSectionOff = onAboutSection (Blog versteckt auf allen About-Seiten) - About-Link zeigt selected auf allen About-Section-Seiten - About-li hat jetzt class 'sub' (hat Kinder) Co-Authored-By: Claude Sonnet 4.6 --- diff --git a/src/components/BlogNav.astro b/src/components/BlogNav.astro index a75a4631..926d7f7c 100644 --- a/src/components/BlogNav.astro +++ b/src/components/BlogNav.astro @@ -45,59 +45,110 @@ const tags = [...tagSet.entries()].map(([name, count]) => ({ count, })).sort((a, b) => a.displayName.localeCompare(b.displayName)); -const mainSections = [ - { name: 'blog', url: '/blog/', title: 'Blog' }, - { name: 'projects', url: '/projects/', title: 'Projects' }, - { name: 'about', url: '/about.html', title: 'About' }, -]; +// ── About section: static tree matching Hugo content/about/ structure ────────── +// Sorted by weight (Hugo default), then by title. +// isNode = true means the page is a section index (has children in Hugo). +interface NavPage { url: string; title: string; isNode?: boolean; children?: NavPage[] } + +const aboutSection: NavPage = { + url: '/about.html', title: 'About', isNode: true, + children: [ + { url: '/contact.html', title: 'Contact' }, // weight 10 + { // weight 10 (alpha: C < I) + url: '/impressum.html', title: 'Impressum', isNode: true, + children: [ + { url: '/urheberrechte.html', title: 'Urheberrecht' }, // weight 10 + { url: '/datenschutz.html', title: 'Datenschutz' }, // weight 20 + { url: '/haftung-inhalte.html', title: 'Haftung für Inhalte' }, // weight 30 + { url: '/haftung-links.html', title: 'Haftung für Links' }, // weight 40 + { url: '/google-analytics.html', title: 'Google Analytics' }, // no weight → last + ] + }, + { url: '/agb.html', title: 'AGB' }, // weight 30 + ] +}; + +// True if `url` is anywhere inside `node`'s subtree (inclusive) +function inSubtree(node: NavPage, url: string): boolean { + if (node.url === url) return true; + return (node.children ?? []).some(c => inSubtree(c, url)); +} + +const onAboutSection = inSubtree(aboutSection, currentUrl); + +// Parent URL of each About-section page (needed for sibling/child off-logic) +const aboutParentOf = new Map([ + ['/about.html', ''], + ['/contact.html', '/about.html'], + ['/impressum.html', '/about.html'], + ['/agb.html', '/about.html'], + ['/urheberrechte.html', '/impressum.html'], + ['/datenschutz.html', '/impressum.html'], + ['/haftung-inhalte.html', '/impressum.html'], + ['/haftung-links.html', '/impressum.html'], + ['/google-analytics.html','/impressum.html'], +]); + +// Section-index pages within About (= have children in Hugo) +const aboutSectionIndexUrls = new Set(['/about.html', '/impressum.html']); +const currentAboutIsNode = aboutSectionIndexUrls.has(currentUrl); +const currentAboutParent = aboutParentOf.get(currentUrl) ?? ''; -// URL classification -const onBlog = currentUrl === '/blog/'; -const isOnBlogPost = allPosts.some(p => (p.data.url ?? `/${p.id}/`) === currentUrl); -const onArchive = currentUrl.startsWith('/blog/archive/') || isOnBlogPost; +// off-logic mirroring Hugo tree.html for a page inside the About subtree. +// A page is visible (not off) if it is current, ancestor-of-current, +// sibling-of-current (only when current is not a section-index), or child-of-current. +function aboutPageOff(page: NavPage, parentUrl: string): boolean { + const isCurrent = page.url === currentUrl; + const isAncestor = !isCurrent && inSubtree(page, currentUrl); + const isSibling = parentUrl === currentAboutParent && !currentAboutIsNode; + const isChild = parentUrl === currentUrl; + return !(isCurrent || isAncestor || isSibling || isChild); +} -const onCategoriesIndex = currentUrl === '/blog/categories/'; -const onCategoryTerm = currentUrl.startsWith('/categories/'); -const onTagsIndex = currentUrl === '/blog/tags/'; -const onTagTerm = currentUrl.startsWith('/tags/'); +// ── Blog URL classification ──────────────────────────────────────────────────── +const mainSections = [ + { name: 'blog', url: '/blog/', title: 'Blog' }, + { name: 'projects', url: '/projects/', title: 'Projects' }, + { name: 'about', url: '/about.html', title: 'About' }, +]; -const blogSelected = onBlog || onArchive || onCategoriesIndex || onCategoryTerm || onTagsIndex || onTagTerm; -const onAbout = currentUrl === '/about.html'; +const onBlog = currentUrl === '/blog/'; +const isOnBlogPost = allPosts.some(p => (p.data.url ?? `/${p.id}/`) === currentUrl); +const onArchive = currentUrl.startsWith('/blog/archive/') || isOnBlogPost; +const onCategoriesIndex = currentUrl === '/blog/categories/'; +const onCategoryTerm = currentUrl.startsWith('/categories/'); +const onTagsIndex = currentUrl === '/blog/tags/'; +const onTagTerm = currentUrl.startsWith('/tags/'); +const blogSelected = onBlog || onArchive || onCategoriesIndex || onCategoryTerm || onTagsIndex || onTagTerm; -// Active state flags const categoriesSelected = onCategoriesIndex || onCategoryTerm; -const tagsSelected = onTagsIndex || onTagTerm; +const tagsSelected = onTagsIndex || onTagTerm; -// "off" class logic derived from Hugo nav output: -// Archive/Categories/Tags are all off when not in blog context (e.g. homepage, About) -// Within blog context: Archive is off on taxonomy term pages only -// Categories/Tags are off on archive pages and the sibling taxonomy term -const archiveOff = !blogSelected || onCategoryTerm || onTagTerm; -const categoriesOff = !blogSelected || onArchive || onTagTerm; -const tagsOff = !blogSelected || onArchive || onCategoryTerm; +// off-class logic for Blog subtree items (derived from Hugo blog.html + tree.html) +const archiveOff = !blogSelected || onCategoryTerm || onTagTerm; +const categoriesOff = !blogSelected || onArchive || onTagTerm; +const tagsOff = !blogSelected || onArchive || onCategoryTerm; -// Determine active section +// Active section for #menu highlight const activeSection = mainSections.find(s => { - if (s.name === 'blog') return blogSelected; + if (s.name === 'blog') return blogSelected; + if (s.name === 'about') return onAboutSection; return currentUrl.startsWith(s.url); })?.name ?? ''; -// Hugo blog.html: Blog always visible on blog pages, all other sections always off. -// Hugo default.html + tree.html: on Homepage/regular-root-pages all sections visible -// (child/sibling logic); on About section-index only About visible, Blog off. -// When Projects is implemented, extend both: blogSectionOff |= onProjects, aboutOff |= onProjects. -const blogSectionOff = onAbout; -const aboutOff = blogSelected; +// Top-level section off-logic (Hugo blog.html vs default.html + tree.html): +// Blog section hidden when viewing any About-section page (and later: Projects pages). +// About section hidden only when on Blog pages. +const blogSectionOff = onAboutSection; +const aboutOff = blogSelected; -// Determine active year from currentUrl +// Active year / taxonomy term helpers function isActiveYear(year: string) { return currentUrl === `/blog/archive/${year}/` || postsByYear[year]?.some(p => postUrl(p) === currentUrl) === true; } - -// Active taxonomy term from URL -const activeCatSlug = onCategoryTerm ? currentUrl.replace(/^\/categories\//, '').replace(/\/$/, '') : ''; -const activeTagSlug = onTagTerm ? currentUrl.replace(/^\/tags\//, '').replace(/\/$/, '') : ''; +const activeCatSlug = onCategoryTerm ? currentUrl.replace(/^\/categories\//, '').replace(/\/$/, '') : ''; +const activeTagSlug = onTagTerm ? currentUrl.replace(/^\/tags\//, '').replace(/\/$/, '') : ''; ---