feat: Update navigation structure and add subitems

This commit is contained in:
Gauthier Daniels 2025-04-13 21:16:47 +02:00
parent 82258addeb
commit 717ca15a83
3 changed files with 108 additions and 43 deletions

View File

@ -50,7 +50,7 @@ function NavigationItem(props: NavigationItemProps) {
{isOpened && (
<ul
role="list"
className="mt-2 space-y-2 border-l-2 border-slate-100 lg:mt-4 lg:space-y-4 lg:border-slate-200 dark:border-slate-800 mb-9"
className="mt-2 space-y-2 border-l-2 border-slate-100 lg:mt-4 lg:space-y-4 lg:border-slate-200 dark:border-slate-800 mb-4"
>
{props.section.links.map((link) => (
<li key={link.href} className="relative">
@ -67,7 +67,9 @@ function NavigationItem(props: NavigationItemProps) {
)}
>
{link.title}
{link.subitems && <span className="text-slate-400 dark:text-slate-500"> ({link.subitems.length})</span>}
{link.subitems.length > 0 && (
<span className="text-slate-400 dark:text-slate-500"> ({link.subitems.length})</span>
)}
</Link>
{link.subitems && (
<ul
@ -125,11 +127,12 @@ export function Navigation({
return (
<nav className={clsx("text-base lg:text-sm", className)}>
<ul role="list" className="space-y-4">
{firstSections.map((section) => (
<li key={section.title}>
<NavigationItem section={section} onLinkClick={onLinkClick} />
</li>
))}
<li>
<h2 className="font-display font-bold text-base text-slate-900 dark:text-white">{firstSections[0]?.type}</h2>
{firstSections.map((section) => (
<NavigationItem key={section.title} section={section} onLinkClick={onLinkClick} />
))}
</li>
{Object.entries(filteredSections).map(([type, sections]) => (
<li key={type}>
<h2 className="font-display font-bold text-base text-slate-900 dark:text-white">{type}</h2>

View File

@ -1,9 +1,8 @@
import { navigation, NavigationLink, type NavigationSubItem } from "@/lib/navigation";
import { usePageContext } from "vike-react/usePageContext";
import { Link } from "@/components/common/Link";
import clsx from "clsx";
import { navigation } from "@/lib/navigation";
function ArrowIcon(props: React.ComponentPropsWithoutRef<"svg">) {
return (
<svg viewBox="0 0 16 16" aria-hidden="true" {...props}>
@ -23,8 +22,8 @@ function PageLink({
dir?: "previous" | "next";
}) {
const pageCategory = navigation.find((section) => {
return section.links.some((link) => link.href === href);
})!;
return section.links.some((link) => link.href === href || link.subitems.some((subitem) => subitem.href === href));
});
return (
<div {...props}>
@ -40,7 +39,9 @@ function PageLink({
)}
>
<p className="flex flex-col gap-0">
<span className="text-violet-600 dark:text-violet-400 text-sm -mb-3">{pageCategory.title}</span>
{pageCategory && (
<span className="text-violet-600 dark:text-violet-400 text-sm -mb-3">{pageCategory.title}</span>
)}
<span>{title}</span>
</p>
<ArrowIcon className={clsx("h-6 w-6 flex-none fill-current", dir === "previous" && "-scale-x-100")} />
@ -51,12 +52,45 @@ function PageLink({
}
export function PrevNextLinks() {
const allLinks = navigation.flatMap((section) => section.links);
const { urlPathname } = usePageContext();
const allLinks = navigation.flatMap((section) => section.links);
const linkIndex = allLinks.findIndex((link) => link.href === urlPathname);
const previousPage = linkIndex > -1 ? allLinks[linkIndex - 1] : null;
const nextPage = linkIndex > -1 ? allLinks[linkIndex + 1] : null;
let subItemElement: undefined | NavigationSubItem;
const findLinkIndex = (pathname = urlPathname) => {
for (let i = 0; i < allLinks.length; i++) {
const link = allLinks[i];
if (link.href === urlPathname) {
return i;
}
if (link.subitems) {
const subitemIndex = link.subitems.findIndex((subitem) => subitem.href === urlPathname);
if (subitemIndex !== -1) {
subItemElement = link.subitems[subitemIndex];
return i;
}
}
}
};
const linkIndex = findLinkIndex();
if (linkIndex === undefined) return null;
let previousPage: NavigationSubItem | NavigationLink | null = linkIndex > -1 ? allLinks[linkIndex - 1] : null;
let nextPage: NavigationSubItem | NavigationLink | null = linkIndex > -1 ? allLinks[linkIndex + 1] : null;
if (subItemElement !== undefined) {
const subItemIndex = findLinkIndex(subItemElement.href)!;
const currentPage = allLinks[subItemIndex];
const subItemIndexInLink = currentPage.subitems?.findIndex((subitem) => subitem.href === urlPathname);
if (subItemIndexInLink !== undefined && subItemIndexInLink > -1) {
previousPage = currentPage.subitems[subItemIndexInLink - 1];
nextPage = currentPage.subitems[subItemIndexInLink + 1];
}
}
if (!nextPage && !previousPage) {
return null;

View File

@ -4,15 +4,42 @@ const navigationsTypes = {
DOCUMENTATIONS: "📚 Documentations",
};
export const navigation = [
export type NavigationSection = {
title: string;
type: (typeof navigationsTypes)[keyof typeof navigationsTypes];
position: "start" | "auto";
links: NavigationLink[];
};
export type NavigationLink = {
title: string;
href: string;
subitems: NavigationSubItem[];
};
export type NavigationSubItem = {
title: string;
href: string;
};
export const navigation: NavigationSection[] = [
{
title: "Préambule",
type: navigationsTypes.GLOBAL,
position: "start",
links: [
{ title: "Memento Dev", href: "/" },
{ title: "Certifications", href: "/certifications" },
{ title: "Documentations", href: "/docs" },
{ title: "Memento Dev", href: "/", subitems: [] },
{ title: "Certifications", href: "/certifications", subitems: [] },
{ title: "Documentations", href: "/docs", subitems: [] },
],
},
{
title: "Communauté",
type: navigationsTypes.GLOBAL,
position: "start",
links: [
{ title: "Influenceurs", href: "/docs/communaute/influenceurs", subitems: [] },
{ title: "Partages et réutilisations", href: "/docs/communaute/partages", subitems: [] },
],
},
{
@ -20,7 +47,7 @@ export const navigation = [
type: navigationsTypes.CERTIFICATIONS,
position: "auto",
links: [
{ title: "Résumé", href: "/certifications/dwwm" },
{ title: "Résumé", href: "/certifications/dwwm", subitems: [] },
{
title: "Activité Type 1",
href: "/certifications/dwwm/at1",
@ -44,37 +71,38 @@ export const navigation = [
],
},
{
title: "React",
title: "Front-end",
type: navigationsTypes.DOCUMENTATIONS,
position: "auto",
links: [
{ title: "Introduction", href: "/docs/react" },
{ title: "Initialisation", href: "/docs/react/initialisation" },
{ title: "Syntaxe JSX", href: "/docs/react/jsx" },
{ title: "Premier composant", href: "/docs/react/premier-composant" },
{ title: "State et cycle de vie", href: "/docs/react/state-et-cycle-de-vie" },
{ title: "Hooks", href: "/docs/react/hooks" },
{ title: "Le hook useContext", href: "/docs/react/use-context" },
{ title: "Le hook useReducer", href: "/docs/react/use-reducer" },
{
title: "React",
href: "/docs/react",
subitems: [
{ title: "Initialisation", href: "/docs/react/initialisation" },
{ title: "Syntaxe JSX", href: "/docs/react/jsx" },
{ title: "Premier composant", href: "/docs/react/premier-composant" },
{ title: "State et cycle de vie", href: "/docs/react/state-et-cycle-de-vie" },
{ title: "Hooks", href: "/docs/react/hooks" },
{ title: "Le hook useContext", href: "/docs/react/use-context" },
{ title: "Le hook useReducer", href: "/docs/react/use-reducer" },
],
},
],
},
{
title: "Merise",
title: "Base de données",
type: navigationsTypes.DOCUMENTATIONS,
position: "auto",
links: [
{ title: "Introduction", href: "/docs/merise" },
{ title: "Dictionnaire de données", href: "/docs/merise/dictionnaire-de-donnees" },
{ title: "Modèle Conceptuel de Données", href: "/docs/merise/modele-conceptuel-de-donnees" },
],
},
{
title: "Communauté",
type: navigationsTypes.GLOBAL,
position: "start",
links: [
{ title: "Influenceurs", href: "/docs/communaute/influenceurs" },
{ title: "Partages et réutilisations", href: "/docs/communaute/partages" },
{
title: "Merise",
href: "/docs/merise",
subitems: [
{ title: "Dictionnaire de données", href: "/docs/merise/dictionnaire-de-donnees" },
{ title: "Modèle Conceptuel de Données", href: "/docs/merise/modele-conceptuel-de-donnees" },
],
},
],
},
];