wip + convertis images
This commit is contained in:
parent
cb467aac2b
commit
ffadb1644c
2742 changed files with 898 additions and 607 deletions
|
|
@ -0,0 +1,42 @@
|
|||
import { Option, pipe } from "effect";
|
||||
import { Array as EffectArray } from "effect";
|
||||
import { NonEmptyReadonlyArray } from "effect/Array";
|
||||
import { getOptionOrThrowWithError } from "./utils";
|
||||
|
||||
/** Type union des parents possibles pour un `querySelector`. */
|
||||
export type ParentElement = Document | Element;
|
||||
|
||||
export const getFirstSelectorFromParent =
|
||||
(parent: ParentElement) => <E extends Element = Element>(selector: string): Option.Option<NonNullable<E>> =>
|
||||
Option.fromNullishOr(parent.querySelector<E>(selector));
|
||||
|
||||
export const getFirstSelectorFromDocument = <E extends Element = Element>(
|
||||
selector: string,
|
||||
): Option.Option<NonNullable<E>> => getFirstSelectorFromParent(document)<E>(selector);
|
||||
|
||||
export const getFirstSelectorFromDocumentOrThrow = <E extends Element = Element>(selector: string): NonNullable<E> =>
|
||||
pipe(
|
||||
getFirstSelectorFromDocument<E>(selector),
|
||||
getOptionOrThrowWithError(`Il n'y a pas d'Élément dans le Document avec le sélecteur suivant : ${selector}.`),
|
||||
);
|
||||
|
||||
export const getAllSelectorFromParent =
|
||||
(parent: ParentElement) => <E extends Element = Element>(selector: string): Option.Option<NonEmptyReadonlyArray<E>> =>
|
||||
pipe(
|
||||
parent.querySelectorAll<E>(selector),
|
||||
// Convertis NodeListOf en Array.
|
||||
Array.from<E>,
|
||||
(xs: Array<E>) => Option.liftPredicate(EffectArray.isReadonlyArrayNonEmpty)(xs),
|
||||
);
|
||||
|
||||
export const getAllSelectorFromDocument = <E extends Element = Element>(
|
||||
selector: string,
|
||||
): Option.Option<NonEmptyReadonlyArray<E>> => getAllSelectorFromParent(document)<E>(selector);
|
||||
|
||||
export const getAllSelectorFromDocumentOrThrow = <E extends Element = Element>(
|
||||
selector: string,
|
||||
): NonEmptyReadonlyArray<E> =>
|
||||
pipe(
|
||||
getAllSelectorFromDocument<E>(selector),
|
||||
getOptionOrThrowWithError(`Il n'y a pas d'Éléments dans le Document avec le sélecteur suivant : ${selector}.`),
|
||||
);
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { pipe, Option } from "effect";
|
||||
|
||||
export const getOptionOrThrowWithError = (message: string) => <T>(option: Option.Option<T>): T =>
|
||||
pipe(
|
||||
option,
|
||||
Option.getOrThrowWith(() => new Error(message)),
|
||||
);
|
||||
|
|
@ -26,7 +26,7 @@ export const ATTRIBUT_TABINDEX = "tabindex";
|
|||
// En-tête
|
||||
export const DOM_BOUTON_MENU_MOBILE = "#bouton-menu-mobile";
|
||||
export const DOM_BOUTON_PANIER = ".compte-panier a[rel='cart']";
|
||||
export const DOM_ENTREE_MENU_CATEGORIES_PRODUITS = "#menu-categories-produits ul li a";
|
||||
export const DOM_ENTREES_MENU_CATEGORIES_PRODUITS = "#menu-categories-produits ul li a";
|
||||
export const DOM_MENU_CATEGORIES_PRODUITS = "#menu-categories-produits";
|
||||
export const DOM_MENU_MOBILE = "#menu-mobile";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,10 @@
|
|||
import { pipe } from "@mobily/ts-belt";
|
||||
import { EitherAsync } from "purify-ts";
|
||||
import { match, P } from "ts-pattern";
|
||||
import { type GenericSchema, parse } from "valibot";
|
||||
import { match } from "ts-pattern";
|
||||
|
||||
import type { HttpCodeErrors, SimplifiedResponse } from "./types/reseau";
|
||||
|
||||
import { ENTETE_WC_NONCE } from "../constantes/api.ts";
|
||||
import {
|
||||
BadRequestError,
|
||||
ErreurInconnue,
|
||||
ForbiddenError,
|
||||
leveBadRequestError,
|
||||
leveErreur,
|
||||
leveNotFoundError,
|
||||
leveUnauthorizedError,
|
||||
NotFoundError,
|
||||
ServerError,
|
||||
UnauthorizedError,
|
||||
type UnknownError,
|
||||
} from "./erreurs.ts";
|
||||
import { estWCError } from "./schemas/api/erreurs.ts";
|
||||
import { BadRequestError, ForbiddenError, NotFoundError, ServerError, UnauthorizedError } from "./erreurs.ts";
|
||||
|
||||
// Types
|
||||
|
||||
|
|
@ -149,22 +134,6 @@ export const prefilledPostBackend =
|
|||
export const safeFetch = (f: Promise<Response>): EitherAsync<DOMException | TypeError, Response> =>
|
||||
EitherAsync<DOMException | TypeError, Response>(async () => await f);
|
||||
|
||||
// TODO: Ne traite pas du tout les Erreurs
|
||||
// TODO: Utiliser un Either
|
||||
export const traiteReponseBackendWCSelonCodesHTTP = <R, S extends GenericSchema<R>>(
|
||||
corpsReponse: unknown,
|
||||
schemaReponse: S,
|
||||
): R =>
|
||||
match(corpsReponse)
|
||||
// Réponses problématiques
|
||||
.with({ body: P.select(), status: 400 }, estWCError, leveBadRequestError)
|
||||
.with({ body: P.select(), status: 401 }, estWCError, leveUnauthorizedError)
|
||||
.with({ body: P.select(), status: 404 }, estWCError, leveNotFoundError)
|
||||
// Réponse OK (201)
|
||||
.with(P._, corpsOkInconnu => parse<S>(schemaReponse, corpsOkInconnu))
|
||||
// Réponses inconnues
|
||||
.otherwise(e => pipe(e, ErreurInconnue, leveErreur<UnknownError>));
|
||||
|
||||
// Réponses Simplifiées
|
||||
export const newPartialResponse = async (reponse: Response): Promise<SimplifiedResponse> => {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,35 +1,39 @@
|
|||
/** Scripts pour le Menu des Catégories de Produits */
|
||||
|
||||
import { A } from "@mobily/ts-belt";
|
||||
import { match } from "ts-pattern";
|
||||
import { Array as EffectArray, Match, Predicate } from "effect";
|
||||
|
||||
import { DOM_ENTREE_MENU_CATEGORIES_PRODUITS, DOM_MENU_CATEGORIES_PRODUITS } from "./constantes/dom.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
import { DOM_ENTREES_MENU_CATEGORIES_PRODUITS, DOM_MENU_CATEGORIES_PRODUITS } from "./constantes/dom.ts";
|
||||
import { getAllSelectorFromDocumentOrThrow, getFirstSelectorFromDocumentOrThrow } from "../scripts-effect/lib/dom.ts";
|
||||
|
||||
// Initialise les attributs HTML pour l'affichage initiale des flèches de défilement du menu de catégories de Produits.
|
||||
document.addEventListener("DOMContentLoaded", (): void => {
|
||||
const MENU_CATEGORIES_PRODUITS: HTMLElement = mustGetEleInDocument(DOM_MENU_CATEGORIES_PRODUITS);
|
||||
const ENTREES_MENU_CATEGORIES_PRODUITS: Array<HTMLAnchorElement> = mustGetElesInDocument(
|
||||
DOM_ENTREE_MENU_CATEGORIES_PRODUITS,
|
||||
const productsCategoriesMenu: HTMLElement = getFirstSelectorFromDocumentOrThrow<HTMLElement>(
|
||||
DOM_MENU_CATEGORIES_PRODUITS,
|
||||
);
|
||||
const menuEntries: ReadonlyArray<HTMLAnchorElement> = getAllSelectorFromDocumentOrThrow(
|
||||
DOM_ENTREES_MENU_CATEGORIES_PRODUITS,
|
||||
);
|
||||
|
||||
A.forEachWithIndex(
|
||||
[ENTREES_MENU_CATEGORIES_PRODUITS.at(0), ENTREES_MENU_CATEGORIES_PRODUITS.at(-1)],
|
||||
(index, entreeMenu): void => {
|
||||
if (!entreeMenu) return;
|
||||
const firstAndLastEntries: Array<(HTMLAnchorElement | undefined)> = [menuEntries.at(0), menuEntries.at(-1)];
|
||||
|
||||
new IntersectionObserver(
|
||||
A.forEach(entree => {
|
||||
// Ne déclenche rien si le scroll n'est pas horizontal
|
||||
if (entree.boundingClientRect.top <= 0) return;
|
||||
match([entree.isIntersecting, index])
|
||||
.with([true, 0], () => MENU_CATEGORIES_PRODUITS.removeAttribute("data-entrees-presentes-debut"))
|
||||
.with([true, 1], () => MENU_CATEGORIES_PRODUITS.removeAttribute("data-entrees-presentes-fin"))
|
||||
.with([false, 0], () => MENU_CATEGORIES_PRODUITS.setAttribute("data-entrees-presentes-debut", ""))
|
||||
.with([false, 1], () => MENU_CATEGORIES_PRODUITS.setAttribute("data-entrees-presentes-fin", ""))
|
||||
.run();
|
||||
}),
|
||||
{ root: null, threshold: 0.9 },
|
||||
).observe(entreeMenu);
|
||||
},
|
||||
);
|
||||
// Créé un nouvel Observer pour la première et dernière entrée.
|
||||
EffectArray.forEach(firstAndLastEntries, (menuEntry, _index) => {
|
||||
if (Predicate.isUndefined(menuEntry)) return;
|
||||
|
||||
new IntersectionObserver(
|
||||
EffectArray.forEach(intersectionEntry => {
|
||||
// Ne déclenche rien si le scroll n'est pas horizontal
|
||||
if (intersectionEntry.boundingClientRect.top <= 0) return;
|
||||
|
||||
Match.value([intersectionEntry.isIntersecting]).pipe(
|
||||
Match.when([true, 0], () => productsCategoriesMenu.removeAttribute("data-entrees-presentes-debut")),
|
||||
Match.when([true, 1], () => productsCategoriesMenu.removeAttribute("data-entrees-presentes-fin")),
|
||||
Match.when([false, 0], () => productsCategoriesMenu.setAttribute("data-entrees-presentes-debut", "")),
|
||||
Match.when([false, 1], () => productsCategoriesMenu.setAttribute("data-entrees-presentes-fin", "")),
|
||||
Match.orElse(() => {}),
|
||||
);
|
||||
}),
|
||||
{ root: null, threshold: 0.9 },
|
||||
).observe(menuEntry);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue