feat: Add useEffect and NavigationSubItem component
All checks were successful
Update Memento Dev on VPS / deploy (push) Successful in 49s
All checks were successful
Update Memento Dev on VPS / deploy (push) Successful in 49s
This commit is contained in:
parent
965534659e
commit
6695c7c023
@ -2,7 +2,7 @@ import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/solid";
|
|||||||
import { usePageContext } from "vike-react/usePageContext";
|
import { usePageContext } from "vike-react/usePageContext";
|
||||||
import { Link } from "@/components/common/Link";
|
import { Link } from "@/components/common/Link";
|
||||||
import { navigation } from "@/lib/navigation";
|
import { navigation } from "@/lib/navigation";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
type NavigationItemProps = {
|
type NavigationItemProps = {
|
||||||
@ -50,33 +50,88 @@ function NavigationItem(props: NavigationItemProps) {
|
|||||||
{isOpened && (
|
{isOpened && (
|
||||||
<ul
|
<ul
|
||||||
role="list"
|
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-4"
|
className="!mt-0 ml-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) => (
|
{props.section.links.map((link) => (
|
||||||
<li key={link.href} className="relative">
|
<li key={link.href} className="relative">
|
||||||
|
<NavigationSubItem
|
||||||
|
link={link}
|
||||||
|
onLinkClick={props.onLinkClick}
|
||||||
|
isOpened={link.href === urlPathname || link.subitems?.some((subitem) => subitem.href === urlPathname)}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type NavigationSubItemProps = {
|
||||||
|
link: (typeof navigation)[number]["links"][number];
|
||||||
|
onLinkClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||||
|
isOpened?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function NavigationSubItem(props: NavigationSubItemProps) {
|
||||||
|
const [isOpened, setIsOpened] = useState(props.isOpened);
|
||||||
|
const { urlPathname } = usePageContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsOpened(
|
||||||
|
props.link.href === urlPathname || props.link.subitems?.some((subitem) => subitem.href === urlPathname),
|
||||||
|
);
|
||||||
|
}, [urlPathname, props.link]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className="pl-2 flex cursor-pointer">
|
||||||
|
{props.link.subitems.length > 0 && (
|
||||||
|
<span
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
setIsOpened((prev) => !prev);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onClick={() => setIsOpened((prev) => !prev)}
|
||||||
|
>
|
||||||
|
{isOpened ? (
|
||||||
|
<ChevronUpIcon className="inline-block h-5 w-5 text-slate-400" />
|
||||||
|
) : (
|
||||||
|
<ChevronDownIcon className="inline-block h-5 w-5 text-slate-400" />
|
||||||
|
)}
|
||||||
|
<span className="sr-only">{isOpened ? "Masquer" : "Afficher"}</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={link.href}
|
href={props.link.href}
|
||||||
onClick={props.onLinkClick}
|
onClick={props.onLinkClick}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"block w-full pl-3.5 before:pointer-events-none before:absolute before:-left-1 before:h-1.5 before:w-1.5 before:rounded-full",
|
"block pl-2 w-full before:pointer-events-none before:absolute before:-left-1 before:h-1.5 before:w-1.5 before:rounded-full",
|
||||||
{ "before:top-1/2 before:-translate-y-1/2": !link.subitems },
|
{ "before:top-1/2 before:-translate-y-1/2": !props.link.subitems },
|
||||||
{ "before:top-3 before:-translate-y-1/2": link.subitems },
|
{ "before:top-3 before:-translate-y-1/2 font-semibold": props.link.subitems },
|
||||||
link.href === urlPathname || link.subitems?.some((subitem) => subitem.href === urlPathname)
|
props.link.href !== urlPathname && "before:hidden",
|
||||||
? "font-semibold text-violet-500 before:bg-violet-500"
|
isOpened
|
||||||
: "text-slate-500 before:hidden before:bg-slate-300 hover:text-slate-600 hover:before:block dark:text-slate-400 dark:before:bg-slate-700 dark:hover:text-slate-300",
|
? "text-violet-500 before:bg-violet-500"
|
||||||
|
: "text-slate-500 before:bg-slate-300 hover:text-slate-600 hover:before:block dark:text-slate-400 dark:before:bg-slate-700 dark:hover:text-slate-300",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{link.title}
|
{props.link.title}
|
||||||
{link.subitems.length > 0 && (
|
{props.link.subitems.length > 0 && (
|
||||||
<span className="text-slate-400 dark:text-slate-500"> ({link.subitems.length})</span>
|
<span className="text-slate-400 dark:text-slate-500"> ({props.link.subitems.length})</span>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
{link.subitems && (
|
</span>
|
||||||
|
{props.link.subitems && isOpened && (
|
||||||
<ul
|
<ul
|
||||||
role="list"
|
role="list"
|
||||||
className="ml-4 border-l-2 border-slate-100 lg:space-y-1 lg:border-slate-200 dark:border-slate-800 mb-4"
|
className="ml-4 border-l-2 border-slate-100 lg:space-y-1 lg:border-slate-200 dark:border-slate-800 mb-4"
|
||||||
>
|
>
|
||||||
{link.subitems.map((subitem) => (
|
{props.link.subitems.map((subitem) => (
|
||||||
<li key={subitem.href} className="relative">
|
<li key={subitem.href} className="relative">
|
||||||
<Link
|
<Link
|
||||||
href={subitem.href}
|
href={subitem.href}
|
||||||
@ -94,10 +149,6 @@ function NavigationItem(props: NavigationItemProps) {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,14 @@ export type NavigationSection = {
|
|||||||
links: NavigationLink[];
|
links: NavigationLink[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NavigationOG = Partial<{
|
||||||
|
image: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type NavigationLink = {
|
export type NavigationLink = {
|
||||||
title: string;
|
title: string;
|
||||||
href: string;
|
href: string;
|
||||||
|
og?: NavigationOG;
|
||||||
subitems: NavigationSubItem[];
|
subitems: NavigationSubItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -101,6 +106,7 @@ export const navigation: NavigationSection[] = [
|
|||||||
{
|
{
|
||||||
title: "Merise",
|
title: "Merise",
|
||||||
href: "/docs/merise",
|
href: "/docs/merise",
|
||||||
|
og: { image: "/merise/og.webp" },
|
||||||
subitems: [
|
subitems: [
|
||||||
{ title: "Introduction", href: "/docs/merise" },
|
{ title: "Introduction", href: "/docs/merise" },
|
||||||
{ title: "Dictionnaire de données", href: "/docs/merise/dictionnaire-de-donnees" },
|
{ title: "Dictionnaire de données", href: "/docs/merise/dictionnaire-de-donnees" },
|
||||||
@ -112,3 +118,16 @@ export const navigation: NavigationSection[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function findNavigationLink(namespace: string, href: string) {
|
||||||
|
const currentUrl = `/${namespace}/${href}`.replace(/\/+/g, "/").replace(/\/$/, "");
|
||||||
|
|
||||||
|
const links = navigation.flatMap((section) => section.links);
|
||||||
|
const subitems = links.flatMap((link) => link.subitems);
|
||||||
|
const allLinks = new Set([...links, ...subitems]);
|
||||||
|
const foundLink = Array.from(allLinks).find((link) => link.href === currentUrl);
|
||||||
|
|
||||||
|
console.log({ allLinks, currentUrl });
|
||||||
|
|
||||||
|
return foundLink;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import logoUrl from "../assets/logo.svg";
|
import logoUrl from "@/assets/logo.svg";
|
||||||
|
|
||||||
export default function HeadDefault() {
|
export default function HeadDefault() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -23,6 +23,8 @@ export default {
|
|||||||
class: "flex min-h-full bg-white dark:bg-slate-900",
|
class: "flex min-h-full bg-white dark:bg-slate-900",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
image: null,
|
||||||
|
|
||||||
// prerender: true,
|
// prerender: true,
|
||||||
prefetchStaticAssets: "hover",
|
prefetchStaticAssets: "hover",
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { PageContext } from "vike/types";
|
import type { PageContext } from "vike/types";
|
||||||
|
|
||||||
import { snippetsService } from "@/services/SnippetsService";
|
import { snippetsService } from "@/services/SnippetsService";
|
||||||
|
import { findNavigationLink } from "@/lib/navigation";
|
||||||
import { docsService } from "@/services/DocsService";
|
import { docsService } from "@/services/DocsService";
|
||||||
import { readingTime } from "reading-time-estimator";
|
import { readingTime } from "reading-time-estimator";
|
||||||
import { useConfig } from "vike-react/useConfig";
|
import { useConfig } from "vike-react/useConfig";
|
||||||
@ -15,6 +16,7 @@ export async function data(pageContext: PageContext) {
|
|||||||
const { key } = pageContext.routeParams;
|
const { key } = pageContext.routeParams;
|
||||||
|
|
||||||
const doc = await docsService.getDoc("docs", key);
|
const doc = await docsService.getDoc("docs", key);
|
||||||
|
const link = findNavigationLink("docs", key);
|
||||||
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
throw render(404);
|
throw render(404);
|
||||||
@ -25,6 +27,7 @@ export async function data(pageContext: PageContext) {
|
|||||||
config({
|
config({
|
||||||
title: buildTitle(doc.title),
|
title: buildTitle(doc.title),
|
||||||
description: doc.description,
|
description: doc.description,
|
||||||
|
image: link?.og?.image || "notfound",
|
||||||
});
|
});
|
||||||
|
|
||||||
docsService.transform(doc);
|
docsService.transform(doc);
|
||||||
|
|||||||
BIN
app/public/merise/og.webp
Normal file
BIN
app/public/merise/og.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
Loading…
Reference in New Issue
Block a user