Compare commits

..

No commits in common. "2a0283785a923d59d28a9b9181fae1c5e1a9bbe2" and "e4f11527230d4e7be162dde9480e367d5d3091bd" have entirely different histories.

4 changed files with 393 additions and 338 deletions

View File

@ -18,20 +18,24 @@ function ArrowIcon(props: JSX.IntrinsicElements["svg"]) {
type PageLinkProps = Omit<JSX.IntrinsicElements["div"], "dir" | "title"> & {
title: string;
href: string;
dir: "previous" | "next";
dir?: "previous" | "next";
};
function PageLink(props: PageLinkProps) {
const getPageCategory = () =>
navigation.find((section) => {
return section.links.some(
(link) => link.href === props.href || link.subitems.some((subitem) => subitem.href === props.href),
(link) =>
link.href === props.href ||
link.subitems.some((subitem) => subitem.href === props.href),
);
});
return (
<div {...cleanProps(props, "dir", "title", "href", "subitems")}>
<dt class="font-display text-sm font-medium text-slate-900">{props.dir === "next" ? "Suivant" : "Précédent"}</dt>
<dt class="font-display text-sm font-medium text-slate-900">
{props.dir === "next" ? "Suivant" : "Précédent"}
</dt>
<dd class="mt-1">
<Link
href={props.href}
@ -42,11 +46,18 @@ function PageLink(props: PageLinkProps) {
>
<p class="flex flex-col gap-0">
{getPageCategory() && (
<span class="text-violet-600 text-sm mb-1 leading-3">{getPageCategory()?.title}</span>
<span class="text-violet-600 text-sm mb-1 leading-3">
{getPageCategory()?.title}
</span>
)}
<span class="leading-4">{props.title}</span>
</p>
<ArrowIcon class={clsx("h-6 w-6 flex-none fill-current", props.dir === "previous" && "-scale-x-100")} />
<ArrowIcon
class={clsx(
"h-6 w-6 flex-none fill-current",
props.dir === "previous" && "-scale-x-100",
)}
/>
</Link>
</dd>
</div>
@ -82,7 +93,9 @@ export function PrevNextLinks() {
});
const getNeighboringLinks = () => {
const linkIndex = allLinks.findIndex((link) => link.href === pageContext.urlPathname);
const linkIndex = allLinks.findIndex(
(link) => link.href === pageContext.urlPathname,
);
if (linkIndex === -1) return [null, null];
const previousPage = allLinks[linkIndex - 1] || null;
@ -99,10 +112,18 @@ export function PrevNextLinks() {
return (
<dl class="mt-12 mx-4 lg:mr-0 flex gap-4 border-t border-slate-200 pt-6">
{getNeighboringLinks()[0] && <PageLink dir="previous" {...(getNeighboringLinks()[0] as NavigationSubItem)} />}
{getNeighboringLinks()[0] && (
<PageLink
dir="previous"
{...(getNeighboringLinks()[0] as NavigationSubItem)}
/>
)}
{getNeighboringLinks()[1] && (
<PageLink class="ml-auto text-right" dir="next" {...(getNeighboringLinks()[1] as NavigationSubItem)} />
<PageLink
class="ml-auto text-right"
{...(getNeighboringLinks()[1] as NavigationSubItem)}
/>
)}
</dl>
);

View File

@ -1,7 +1,13 @@
import type { SearchResult } from "@/services/FlexSearchService";
import type { JSX, Accessor, Setter } from "solid-js";
import { createContext, useContext, For, createEffect, createSignal } from "solid-js";
import {
createContext,
useContext,
For,
createEffect,
createSignal,
} from "solid-js";
import { Highlighter } from "solid-highlight-words";
import { useDebounce } from "@/hooks/useDebounce";
@ -47,9 +53,21 @@ function LoadingIcon(props: JSX.IntrinsicElements["svg"]) {
return (
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
<circle cx="10" cy="10" r="5.5" stroke-linejoin="round" />
<path stroke={`url(#${id})`} stroke-linecap="round" stroke-linejoin="round" d="M15.5 10a5.5 5.5 0 1 0-5.5 5.5" />
<path
stroke={`url(#${id})`}
stroke-linecap="round"
stroke-linejoin="round"
d="M15.5 10a5.5 5.5 0 1 0-5.5 5.5"
/>
<defs>
<linearGradient id={id} x1="13" x2="9.5" y1="9" y2="15" gradientUnits="userSpaceOnUse">
<linearGradient
id={id}
x1="13"
x2="9.5"
y1="9"
y2="15"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="currentColor" />
<stop offset="1" stop-color="currentColor" stop-opacity="0" />
</linearGradient>
@ -113,15 +131,14 @@ function SearchResultItem(props: { result: SearchResult; query: string }) {
const getHierarchy = (): string[] => {
const sectionTitle = navigation.find((section) => {
return section.links.find((link) => link.href === props.result.url.split("#")[0]);
return section.links.find(
(link) => link.href === props.result.url.split("#")[0],
);
})?.title;
return [sectionTitle, props.result.pageTitle].filter((x): x is string => typeof x === "string");
};
const handleNavigate = (url: string) => {
navigate(`/${url}`.replace(/\/+/g, "/"));
close();
return [sectionTitle, props.result.pageTitle].filter(
(x): x is string => typeof x === "string",
);
};
return (
@ -130,11 +147,21 @@ function SearchResultItem(props: { result: SearchResult; query: string }) {
aria-labelledby={`${id}-hierarchy ${id}-title`}
tab-index={0}
onKeyDown={(event) => {
if (event.key === "Enter") handleNavigate(props.result.url);
if (event.key === "Enter") {
navigate(props.result.url);
close();
}
}}
onClick={() => {
navigate(props.result.url);
close();
}}
onClick={() => handleNavigate(props.result.url)}
>
<div id={`${id}-title`} aria-hidden="true" class="text-sm text-slate-700 group-aria-selected:text-violet-600">
<div
id={`${id}-title`}
aria-hidden="true"
class="text-sm text-slate-700 group-aria-selected:text-violet-600"
>
<HighlightQuery text={props.result.title} query={props.query} />
</div>
{getHierarchy().length > 0 && (
@ -149,7 +176,9 @@ function SearchResultItem(props: { result: SearchResult; query: string }) {
<HighlightQuery text={item} query={props.query} />
<span
class={
itemIndex() === getHierarchy().length - 1 ? "sr-only" : "mx-2 text-slate-300 dark:text-slate-700"
itemIndex() === getHierarchy().length - 1
? "sr-only"
: "mx-2 text-slate-300 dark:text-slate-700"
}
>
/
@ -219,7 +248,11 @@ function SearchDialog(props: { class?: string }) {
return (
<>
<Dialog isOpen={isOpened()} onClose={close} class={clsx("fixed inset-0 z-50", props.class)}>
<Dialog
isOpen={isOpened()}
onClose={close}
class={clsx("fixed inset-0 z-50", props.class)}
>
<div class="fixed inset-0 bg-slate-900/50 backdrop-blur-sm" />
<div
@ -302,7 +335,9 @@ export function Search() {
onClick={() => setIsOpened(true)}
>
<SearchIcon class="h-5 w-5 flex-none fill-slate-400 group-hover:fill-slate-500 md:group-hover:fill-slate-400" />
<span class="sr-only md:not-sr-only md:ml-2 md:text-slate-500">Rechercher...</span>
<span class="sr-only md:not-sr-only md:ml-2 md:text-slate-500">
Rechercher...
</span>
{modifierKey && (
<kbd class="ml-auto hidden font-medium text-slate-400 md:block">
<kbd class="font-sans">{modifierKey()}</kbd>

View File

@ -30,13 +30,13 @@ Le design pattern MVC est un modèle d'architecture logicielle qui sépare les d
- **Contrôleur** : fait le lien entre le modèle et la vue. Il contient la logique métier de l'application.
<Callout type="warning" title="Les schémas disponibles en ligne">
Il existe de nombreux schémas qui expliquent le design pattern MVC mais ils ne sont pas tous corrects. Enfin, si, ils
sont corrects... mais certains ne s'appliquent pas à tous les frameworks et architectures.
Il existe de nombreux schémas qui expliquent le design pattern MVC mais ils ne sont pas tous corrects.
Enfin, si, ils sont corrects... mais certains ne s'appliquent pas à tous les frameworks et architectures.
</Callout>
Pour t'aider à mieux te représenter un schéma MVC avec les ordres de flux de données et de contrôle :
<Image alt="Schéma MVC pour une application web basique" src="/images/patterns/mvc.webp" class="max-h-96 mx-auto" />
<Image alt="Schéma MVC pour une application web basique" src="/patterns/mvc.webp" class="max-h-96 mx-auto" />
<Callout type="question" title="Pourquoi la Vue ne retourne pas directement au client ?">
La vue ne retourne pas directement au client car elle doit passer par le contrôleur.
@ -49,10 +49,8 @@ Pour t'aider à mieux te représenter un schéma MVC avec les ordres de flux de
Le concept est simple : chaque partie de l'application a un **rôle bien défini** et ne doit pas empiéter sur le rôle des autres.
<Callout type="question" title="Et si j'ai des middlewares ?">
Dans la majorité des cas, les middlewares s'exécutent avant le contrôleur même si on peut en avoir à différents
moments de la circulation de la donnée. Si tu as déjà utilisé Express, tu as probablement déjà utilisé un middleware
pour vérifier si l'utilisateur est connecté avant de lui afficher une page qui est réservée aux utilisateurs
connectés.
Dans la majorité des cas, les middlewares s'exécutent avant le contrôleur même si on peut en avoir à différents moments de la circulation de la donnée.
Si tu as déjà utilisé Express, tu as probablement déjà utilisé un middleware pour vérifier si l'utilisateur est connecté avant de lui afficher une page qui est réservée aux utilisateurs connectés.
</Callout>
<Callout type="note" title="Le cas de React (ou Vue, Angular, Solid, etc.)">
@ -76,8 +74,9 @@ Si tu fais un projet personnel, tu peux définir les tiennes, du moment que tu e
Pense à être cohérent en ce qui concerne la langue utilisée.
<Callout type="warning" title="Pas de franglais !">
Évite de mélanger plusieurs langues dans tes nommages. Si tu choisis de travailler en français, reste en français. Si
tu choisis de travailler en anglais, reste en anglais.
Évite de mélanger plusieurs langues dans tes nommages.
Si tu choisis de travailler en français, reste en français.
Si tu choisis de travailler en anglais, reste en anglais.
</Callout>
D'ailleurs, je te recommande chaudement de travailler en anglais ne serait-ce que pour te familiariser avec la langue de Shakespeare qui est, on le rappelle, la langue la plus répandue dans le monde de l'informatique.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB