Compare commits
No commits in common. "2a0283785a923d59d28a9b9181fae1c5e1a9bbe2" and "e4f11527230d4e7be162dde9480e367d5d3091bd" have entirely different histories.
2a0283785a
...
e4f1152723
@ -18,20 +18,24 @@ function ArrowIcon(props: JSX.IntrinsicElements["svg"]) {
|
|||||||
type PageLinkProps = Omit<JSX.IntrinsicElements["div"], "dir" | "title"> & {
|
type PageLinkProps = Omit<JSX.IntrinsicElements["div"], "dir" | "title"> & {
|
||||||
title: string;
|
title: string;
|
||||||
href: string;
|
href: string;
|
||||||
dir: "previous" | "next";
|
dir?: "previous" | "next";
|
||||||
};
|
};
|
||||||
|
|
||||||
function PageLink(props: PageLinkProps) {
|
function PageLink(props: PageLinkProps) {
|
||||||
const getPageCategory = () =>
|
const getPageCategory = () =>
|
||||||
navigation.find((section) => {
|
navigation.find((section) => {
|
||||||
return section.links.some(
|
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 (
|
return (
|
||||||
<div {...cleanProps(props, "dir", "title", "href", "subitems")}>
|
<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">
|
<dd class="mt-1">
|
||||||
<Link
|
<Link
|
||||||
href={props.href}
|
href={props.href}
|
||||||
@ -42,11 +46,18 @@ function PageLink(props: PageLinkProps) {
|
|||||||
>
|
>
|
||||||
<p class="flex flex-col gap-0">
|
<p class="flex flex-col gap-0">
|
||||||
{getPageCategory() && (
|
{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>
|
<span class="leading-4">{props.title}</span>
|
||||||
</p>
|
</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>
|
</Link>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@ -82,7 +93,9 @@ export function PrevNextLinks() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getNeighboringLinks = () => {
|
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];
|
if (linkIndex === -1) return [null, null];
|
||||||
|
|
||||||
const previousPage = allLinks[linkIndex - 1] || null;
|
const previousPage = allLinks[linkIndex - 1] || null;
|
||||||
@ -99,10 +112,18 @@ export function PrevNextLinks() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<dl class="mt-12 mx-4 lg:mr-0 flex gap-4 border-t border-slate-200 pt-6">
|
<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] && (
|
{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>
|
</dl>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
import type { SearchResult } from "@/services/FlexSearchService";
|
import type { SearchResult } from "@/services/FlexSearchService";
|
||||||
import type { JSX, Accessor, Setter } from "solid-js";
|
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 { Highlighter } from "solid-highlight-words";
|
||||||
import { useDebounce } from "@/hooks/useDebounce";
|
import { useDebounce } from "@/hooks/useDebounce";
|
||||||
@ -47,9 +53,21 @@ function LoadingIcon(props: JSX.IntrinsicElements["svg"]) {
|
|||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
|
<svg viewBox="0 0 20 20" fill="none" aria-hidden="true" {...props}>
|
||||||
<circle cx="10" cy="10" r="5.5" stroke-linejoin="round" />
|
<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>
|
<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 stop-color="currentColor" />
|
||||||
<stop offset="1" stop-color="currentColor" stop-opacity="0" />
|
<stop offset="1" stop-color="currentColor" stop-opacity="0" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
@ -113,15 +131,14 @@ function SearchResultItem(props: { result: SearchResult; query: string }) {
|
|||||||
|
|
||||||
const getHierarchy = (): string[] => {
|
const getHierarchy = (): string[] => {
|
||||||
const sectionTitle = navigation.find((section) => {
|
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;
|
})?.title;
|
||||||
|
|
||||||
return [sectionTitle, props.result.pageTitle].filter((x): x is string => typeof x === "string");
|
return [sectionTitle, props.result.pageTitle].filter(
|
||||||
};
|
(x): x is string => typeof x === "string",
|
||||||
|
);
|
||||||
const handleNavigate = (url: string) => {
|
|
||||||
navigate(`/${url}`.replace(/\/+/g, "/"));
|
|
||||||
close();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -130,11 +147,21 @@ function SearchResultItem(props: { result: SearchResult; query: string }) {
|
|||||||
aria-labelledby={`${id}-hierarchy ${id}-title`}
|
aria-labelledby={`${id}-hierarchy ${id}-title`}
|
||||||
tab-index={0}
|
tab-index={0}
|
||||||
onKeyDown={(event) => {
|
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} />
|
<HighlightQuery text={props.result.title} query={props.query} />
|
||||||
</div>
|
</div>
|
||||||
{getHierarchy().length > 0 && (
|
{getHierarchy().length > 0 && (
|
||||||
@ -149,7 +176,9 @@ function SearchResultItem(props: { result: SearchResult; query: string }) {
|
|||||||
<HighlightQuery text={item} query={props.query} />
|
<HighlightQuery text={item} query={props.query} />
|
||||||
<span
|
<span
|
||||||
class={
|
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 (
|
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 class="fixed inset-0 bg-slate-900/50 backdrop-blur-sm" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -302,7 +335,9 @@ export function Search() {
|
|||||||
onClick={() => setIsOpened(true)}
|
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" />
|
<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 && (
|
{modifierKey && (
|
||||||
<kbd class="ml-auto hidden font-medium text-slate-400 md:block">
|
<kbd class="ml-auto hidden font-medium text-slate-400 md:block">
|
||||||
<kbd class="font-sans">{modifierKey()}</kbd>
|
<kbd class="font-sans">{modifierKey()}</kbd>
|
||||||
|
|||||||
@ -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.
|
- **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">
|
<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
|
Il existe de nombreux schémas qui expliquent le design pattern MVC mais ils ne sont pas tous corrects.
|
||||||
sont corrects... mais certains ne s'appliquent pas à tous les frameworks et architectures.
|
Enfin, si, ils sont corrects... mais certains ne s'appliquent pas à tous les frameworks et architectures.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
Pour t'aider à mieux te représenter un schéma MVC avec les ordres de flux de données et de contrôle :
|
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 ?">
|
<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.
|
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.
|
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 ?">
|
<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
|
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.
|
||||||
moments de la circulation de la donnée. Si tu as déjà utilisé Express, tu as probablement déjà utilisé un middleware
|
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.
|
||||||
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>
|
||||||
|
|
||||||
<Callout type="note" title="Le cas de React (ou Vue, Angular, Solid, etc.)">
|
<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.
|
Pense à être cohérent en ce qui concerne la langue utilisée.
|
||||||
|
|
||||||
<Callout type="warning" title="Pas de franglais !">
|
<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
|
Évite de mélanger plusieurs langues dans tes nommages.
|
||||||
tu choisis de travailler en anglais, reste en anglais.
|
Si tu choisis de travailler en français, reste en français.
|
||||||
|
Si tu choisis de travailler en anglais, reste en anglais.
|
||||||
</Callout>
|
</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.
|
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 |
Loading…
Reference in New Issue
Block a user