2025-07-03
- étoffe le fichier `JOURNAL` avec les nouveaux changements majeurs. - propose une tâche _Justfile_ pour un rechargement à chaud primitif lors de changements CSS. - ne précompresse pas et ne propose plus de versions « legacy » des scripts JS en methodes développement. - appose correctement `aria-current` sur le lien de la page courante dans les deux menus de navigation. - remplace une image statique « Scroll down » avec une animation SVG reposant sur du texte et des chemins. - renomme moultes choses.
This commit is contained in:
parent
b85a03a141
commit
d30b83d093
49 changed files with 830 additions and 359 deletions
|
|
@ -1 +1,4 @@
|
|||
{ "dictionaries": ["fr-fr", "en-gb"], "words": ["oxlint", "Vali", "mobily", "valibot", "GLITCHTIP"] }
|
||||
{
|
||||
"dictionaries": ["fr-fr", "en-gb"],
|
||||
"words": ["GLITCHTIP", "Vali", "fdir", "mobily", "oxlint", "valibot", "zstandard", "Eles", "logtape"]
|
||||
}
|
||||
|
|
|
|||
36
docs/JOURNAL.md
Normal file
36
docs/JOURNAL.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Journal de développement
|
||||
|
||||
## 2025-06-13
|
||||
|
||||
### Informations produit sous forme de grille
|
||||
|
||||
- L'idée est de réimplémenter les informations essentiels du produit (nom, prix), le sélecteur de variation, les textes de détail, de conditions et d'entretien, et le bouton d'ajout au Panier.
|
||||
- Plutôt qu'un encart flottant, contenant tout, il n'y aurait qu'une barre pleine longueur comprenant nom, prix et sélecteur de variation (`.essentiel-produit`).
|
||||
- Il flotterait en bas de l'écran jusqu'à ce se poser à la fin des photos.
|
||||
- Une nouvelle section, statique elle et faisant suite à la fois aux photos et à la barre, s'afficherait en pleine longueur sous forme d'accordéon, avec les anciens onglets comme sections.
|
||||
- Par défaut, la section « Détails » serait développée.
|
||||
- `<details>` et `<summary>` sont aujourd'hui pris en charge par les navigateurs (niveau _Baseline_), mais l'attribut `name` permettant l'ouverture exclusive d'une section par accordéon (p. ex. le développement d'une ferme toutes les autres) n'a que récemment été implémentée (2024 pour _Firefox_).
|
||||
- Une implémentation en _JavaScript_ est donc pour l'instant nécessaire.
|
||||
|
||||
### Remaniement du défilement des photos de la page Produit
|
||||
|
||||
- Les flèches de défilement sont supprimées.
|
||||
- À la place, les photos ne s'affichent plus en pleine longueur.
|
||||
- Elles le sont à ~93%, pour que l'on perçoive sur les bords la photo précédente/suivante et signale à l'Utilisateur qu'il est possible de défiler.
|
||||
|
||||
## 2025-06-30
|
||||
|
||||
- Test de _LogTape_ comme librairie de journalisation.
|
||||
|
||||
## 2025-07-03
|
||||
|
||||
- Création d'une animation SVG avec des `<text>` défilants sur un `<path>`.
|
||||
- Le redimensionnement dynamique du conteneur de l'animation se fait via JavaScript.
|
||||
- Le script récupère la longueur d'une image au redimensionnement de la vue.
|
||||
- Il injecte un attribut CSS `inline-size` dans le HTML de la page correspondant à cette nouvelle longueur.
|
||||
- Il n'est pas encore possible de mettre en pause l'animation au survol de la souris, qui est une bonne pratique pour ce style d'éléments visuels.
|
||||
- Le `<svg>` doit être d'une longueur supérieure au conteneur (ici `120%`) pour que le texte disparaisse de façon progressive, qu'il ne soit pas « coupé » brutalement aux extrémités.
|
||||
- Début de l'implémentation d'une classe `no-js` pour un affichage adapté en cas d'absence de JavaScript.
|
||||
- Développement d'un script `fish` qui lance un onglet Cromite en mode sans-tête avec profile Invité sur le site Haiku Atelier.
|
||||
- Il est possible, en utilisant le débogueur à distance Chromium, de rafraîchir un onglet via le terminal en passant par `fx` et `websocat` pour l'interface _WebSocket_.
|
||||
- Cela offre une forme primitive de « hot reload » (rechargement à chaud) pour éviter d'avoir manuellement taper F5 à chaque changement.
|
||||
|
|
@ -12,16 +12,23 @@
|
|||
- Champs
|
||||
- Utiliser un polyfill pour BroadcastChannel
|
||||
- PAGE SHOP
|
||||
- [ ] Faire apparaître le menu des catégories de Produits quand on scroll vers le haut
|
||||
- PAGE PANIER
|
||||
- Erreur lorsque l'on ajoute deux variations d'un même produit et que l'on essaie d'en supprimer une.
|
||||
- Il semblerait que supprimer une variation supprime toutes les entrées du même produit.
|
||||
- MÉTHODES DE LIVRAISON
|
||||
- [ ] Proposer la livraison à domicile en Belgique et en France pour le coût unique de 8 euros, quel que soit le montant de la commande
|
||||
- [ ] Proposer la livraison aux États-Unis.
|
||||
- PAGE PRODUIT
|
||||
- PIED DE PAGE
|
||||
- TOUTES LES PAGES
|
||||
- Trouver la source des bordures superflues.
|
||||
- Sur Chromium : box-shadow sur <article> ?
|
||||
|
||||
- MODE VACANCES
|
||||
- Pour l'été, superposer à la page SHOP un calque gris sur lequel défile une animation d'un texte indiquant que la boutique est en vacances.
|
||||
- La page doit rester défilable.
|
||||
- MODE NO JS
|
||||
- Laisser la page normalement défilable avec les images les une après les autres quand _JavaScript_ n'est pas présent.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
# Informations produit sous forme de grille
|
||||
|
||||
- L'idée est de réimplémenter les informations essentiels du produit (nom, prix), le sélecteur de variation, les textes de détail, de conditions et d'entretien, et le bouton d'ajout au Panier.
|
||||
- Plutôt qu'un encart flottant, contenant tout, il n'y aurait qu'une barre pleine longueur comprenant nom, prix et sélecteur de variation (`.essentiel-produit`).
|
||||
- Il flotterait en bas de l'écran jusqu'à ce se poser à la fin des photos.
|
||||
- Une nouvelle section, statique elle et faisant suite à la fois aux photos et à la barre, s'afficherait en pleine longueur sous forme d'accordéon, avec les anciens onglets comme sections.
|
||||
- Par défaut, la section « Détails » serait développée.
|
||||
- `<details>` et `<summary>` sont aujourd'hui pris en charge par les navigateurs (niveau _Baseline_), mais l'attribut `name` permettant l'ouverture exclusive d'une section par accordéon (p. ex. le développement d'une ferme toutes les autres) n'a que récemment été implémentée (2024 pour _Firefox_).
|
||||
- Une implémentation en _JavaScript_ est donc pour l'instant nécessaire.
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
# Remaniement du défilement des photos de la page Produit
|
||||
|
||||
- Les flèches de défilement sont supprimées.
|
||||
- À la place, les photos ne s'affichent plus en pleine longueur.
|
||||
- Elles le sont à ~93%, pour que l'on perçoive sur les bords la photo précédente/suivante et signale à l'utilisteur qu'il est possible de défiler.
|
||||
|
|
@ -46,12 +46,8 @@ export default tseslint.config(
|
|||
"@typescript-eslint/no-misused-promises": "off",
|
||||
/* Cette règle empêche l'usage de génériques précisant les types de retour de fonctions. */
|
||||
"@typescript-eslint/no-unnecessary-type-parameters": "off",
|
||||
"@typescript-eslint/no-unused-expressions": [
|
||||
"error",
|
||||
{
|
||||
allowTernary: true,
|
||||
},
|
||||
],
|
||||
// Pour utiliser LogTape.
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
/* Cette règle est doublon avec les règles noUnused* de TypeScript. */
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
/* Cette règle empêche de lever des erreurs génériques (p.ex. `E extends Error`). */
|
||||
|
|
|
|||
31
justfile
31
justfile
|
|
@ -9,18 +9,10 @@ stylelintCacheFile := "stylelintcache"
|
|||
list:
|
||||
@just --list --list-heading 'Recettes disponibles :'\n'' --unsorted
|
||||
|
||||
# Démarre le conteneur ddev
|
||||
start:
|
||||
ddev start
|
||||
|
||||
# Arrête le conteneur ddev
|
||||
stop:
|
||||
ddev stop
|
||||
|
||||
# Met à jour les dépendances composer et npm
|
||||
update:
|
||||
composer update
|
||||
bun update
|
||||
bun update
|
||||
|
||||
# Formatte avec Prettier et dprint
|
||||
format:
|
||||
|
|
@ -76,10 +68,8 @@ build-css:
|
|||
|
||||
# Compile le CSS à chaque changement de fichier
|
||||
watch-css:
|
||||
bunx sass \
|
||||
--update \
|
||||
--watch \
|
||||
"web/app/themes/haiku-atelier-2024/src/sass":"web/app/themes/haiku-atelier-2024/assets/css"
|
||||
@just dev
|
||||
@watchexec -w "web/app/themes/haiku-atelier-2024/src/sass" -- just build-css reload-tab
|
||||
|
||||
# Compile TypeScript en JavaScript
|
||||
build-js:
|
||||
|
|
@ -112,8 +102,17 @@ lint-code-mort:
|
|||
squash-and-push:
|
||||
-jj squash --ignore-immutable && jj bookmark set principale -r @- --allow-backwards && jj git push
|
||||
|
||||
# Compile, analyse statiquement (avec corrections automatiques) et formate le CSS
|
||||
build-lint-format-css:
|
||||
-just build-css
|
||||
# Analyse statiquement, compile et formate le CSS
|
||||
lint-build-format-css:
|
||||
-just lint-css
|
||||
-just build-css
|
||||
-just format
|
||||
|
||||
dev:
|
||||
@/opt/cromite/chrome --remote-debugging-address=127.0.0.1 --remote-debugging-port=9222 --profile-directory=Guest "https://haikuatelier.gcch.local" &
|
||||
|
||||
reload-tab:
|
||||
#!/usr/bin/fish
|
||||
set -f WSURL (curl -s http://127.1:9222/json | fx '.[0].webSocketDebuggerUrl')
|
||||
set -f REQUEST '{ "id": 2, "method": "Page.reload", "params": { "ignoreCache": true, "scriptToEvaluateOnLoad": "" } }'
|
||||
echo $REQUEST | websocat $WSURL
|
||||
|
|
|
|||
|
|
@ -17,13 +17,40 @@ const SRC_TYPESCRIPT_PATHS = new fdir()
|
|||
.crawl(`web/app/themes/${SLUG_THEME}/src/scripts`)
|
||||
.withPromise();
|
||||
|
||||
/* Voir le fichier vite.env.d.ts */
|
||||
// Voir le fichier vite.env.d.ts.
|
||||
const SCHEMA_ENVIRONNEMENT = v.object({
|
||||
VITE_GLITCHTIP_NSD: v.pipe(v.string(), v.url(), v.readonly()),
|
||||
VITE_MODE: v.pipe(v.string(), v.readonly()),
|
||||
VITE_URL: v.pipe(v.string(), v.nonEmpty(), v.url(), v.readonly()),
|
||||
});
|
||||
|
||||
const basePlugins = [
|
||||
// Permet de valider les variables d'environnements définies à partir d'un schéma Valibot
|
||||
valibot(SCHEMA_ENVIRONNEMENT),
|
||||
manifestSRI({ algorithms: ["sha512"] }),
|
||||
nodePolyfills({
|
||||
include: [],
|
||||
protocolImports: true,
|
||||
}),
|
||||
];
|
||||
// Les extensions activées en production.
|
||||
const prodPlugins = [
|
||||
legacy({
|
||||
modernPolyfills: true,
|
||||
modernTargets:
|
||||
"chrome >0 and last 3 years, edge >0 and last 3 years, safari >0 and last 3 years, firefox >0 and last 3 years, and_chr >0 and last 3 years, and_ff >0 and last 3 years, ios >0 and last 3 years",
|
||||
renderLegacyChunks: true,
|
||||
}),
|
||||
compression({
|
||||
algorithms: [
|
||||
"brotliCompress",
|
||||
"gzip",
|
||||
"zstandard",
|
||||
],
|
||||
threshold: 1000,
|
||||
}),
|
||||
];
|
||||
|
||||
export default defineConfig(async ({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), "VITE");
|
||||
|
||||
|
|
@ -32,9 +59,9 @@ export default defineConfig(async ({ mode }) => {
|
|||
build: {
|
||||
assetsDir: ".",
|
||||
emptyOutDir: true,
|
||||
/* Génère un fichier manifeste dans outDir */
|
||||
// Génère un fichier manifeste dans outDir.
|
||||
manifest: true,
|
||||
minify: env.VITE_MODE === "production",
|
||||
minify: env["VITE_MODE"] === "production",
|
||||
outDir: resolve("./web/app/themes/haiku-atelier-2024/assets/js"),
|
||||
reportCompressedSize: true,
|
||||
rollupOptions: {
|
||||
|
|
@ -42,39 +69,17 @@ export default defineConfig(async ({ mode }) => {
|
|||
output: {
|
||||
assetFileNames: "[name].[hash].[extname]",
|
||||
chunkFileNames: "[name].[hash].js",
|
||||
compact: env.VITE_MODE === "production",
|
||||
compact: env["VITE_MODE"] === "production",
|
||||
entryFileNames: "[name].js",
|
||||
validate: true,
|
||||
},
|
||||
treeshake: true,
|
||||
},
|
||||
sourcemap: env.VITE_MODE === "production",
|
||||
sourcemap: env["VITE_MODE"] === "production",
|
||||
target: "es2020",
|
||||
write: true,
|
||||
},
|
||||
mode: env.VITE_MODE ?? "production",
|
||||
plugins: [
|
||||
// Permet de valider les variables d'environnements définies à partir d'un schéma Valibot
|
||||
valibot(SCHEMA_ENVIRONNEMENT),
|
||||
manifestSRI({ algorithms: ["sha512"] }),
|
||||
nodePolyfills({
|
||||
include: [],
|
||||
protocolImports: true,
|
||||
}),
|
||||
legacy({
|
||||
modernPolyfills: true,
|
||||
modernTargets:
|
||||
"chrome >0 and last 3 years, edge >0 and last 3 years, safari >0 and last 3 years, firefox >0 and last 3 years, and_chr >0 and last 3 years, and_ff >0 and last 3 years, ios >0 and last 3 years",
|
||||
renderLegacyChunks: true,
|
||||
}),
|
||||
compression({
|
||||
algorithms: [
|
||||
"brotliCompress",
|
||||
"gzip",
|
||||
"zstandard",
|
||||
],
|
||||
threshold: 1000,
|
||||
}),
|
||||
],
|
||||
mode: env["VITE_MODE"] ?? "production",
|
||||
plugins: env["VITE_MODE"] === "production" ? [...basePlugins, ...prodPlugins] : [...basePlugins],
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
--hauteur-ligne-compacte: 1.1;
|
||||
--hauteur-ligne-rapprochee: 1;
|
||||
/* Espacements entre les lettres */
|
||||
--espacement-inter-lettres-rapproche-m: -1px;
|
||||
--espacement-inter-lettres-rapproche-s: -0.5px;
|
||||
--espacement-inter-lettres-etendu-s: 0.5px;
|
||||
--espacement-inter-lettres-etendu-m: 1px;
|
||||
|
|
@ -66,7 +67,7 @@
|
|||
);
|
||||
--contenu-page-hauteur-minimale-avec-categories: calc(
|
||||
100svh - var(--en-tete-hauteur) - var(--pied-de-page-hauteur)
|
||||
- var(--menu-categories-produits-hauteur)
|
||||
- var(--menu-categories-produits-hauteur)
|
||||
);
|
||||
/* Espacements */
|
||||
--espace-xs: 0.25rem;
|
||||
|
|
@ -109,6 +110,7 @@ html {
|
|||
* 2. Utilise la couleur primaire du site.
|
||||
*/
|
||||
body {
|
||||
overscroll-behavior: none;
|
||||
accent-color: var(--couleur-jaune); /* 2 */
|
||||
background: var(--couleur-gris); /* 1 */
|
||||
}
|
||||
|
|
@ -688,14 +690,17 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
|
|||
/* Dispositions */
|
||||
--liste-puce-cercle-puce-position-horizontale: 3.5ch; /* 3 */
|
||||
}
|
||||
#en-tete .menu-navigation__entree:has(a[aria-current=page]) {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg") center/auto 90% no-repeat;
|
||||
}
|
||||
#en-tete .menu-navigation__entree--courante {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg") center/auto 90% no-repeat;
|
||||
}
|
||||
#en-tete .menu-navigation__entree a {
|
||||
display: inline-block; /* 1 */
|
||||
padding: var(--nav-entree-marges-internes-bloc) var(--nav-entree-marges-internes-ligne); /* 2 */
|
||||
text-align: center; /* 4 */
|
||||
}
|
||||
#en-tete .menu-navigation__entree--courante {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg") center/auto 90% no-repeat;
|
||||
}
|
||||
@media (hover: hover) {
|
||||
#en-tete .menu-navigation__entree:hover {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg") center/auto 90% no-repeat;
|
||||
|
|
@ -1230,9 +1235,6 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
|
|||
.details-produit__textes .section-textuelle:not(:last-of-type) {
|
||||
border-block-end: 1px solid var(--couleur-noir);
|
||||
}
|
||||
.details-produit__textes .section-textuelle:has(button[aria-expanded=true]) button {
|
||||
/* padding: initial; */
|
||||
}
|
||||
.details-produit__textes .section-textuelle:has(button[aria-expanded=false]) .section-textuelle__contenu {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -1263,7 +1265,7 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
|
|||
.details-produit__actions {
|
||||
--section-marges-internes: var(--espace-l);
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--couleur-noir);
|
||||
border-block: 1px solid var(--couleur-noir);
|
||||
background: var(--couleur-jaune);
|
||||
transition: 0.2s background;
|
||||
}
|
||||
|
|
@ -1286,6 +1288,7 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
|
|||
.produits-similaires {
|
||||
--carte-produit-longueur-minimale: 448px;
|
||||
--carte-produit-longueur-maximale: 1000px;
|
||||
--en-tete-flottante-hauteur: calc(1rem + var(--espace-l) * 2 + 1px);
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-areas: "en-tete en-tete en-tete" "produits produits produits";
|
||||
|
|
@ -1297,10 +1300,10 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
|
|||
.produits-similaires header {
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
top: calc(1lh + var(--espace-l) + var(--espace-m));
|
||||
top: var(--en-tete-flottante-hauteur);
|
||||
grid-area: en-tete;
|
||||
width: 100%;
|
||||
padding: var(--espace-l) 0 var(--espace-m);
|
||||
padding: var(--espace-l) 0;
|
||||
color: var(--couleur-blanc);
|
||||
text-align: center;
|
||||
background: var(--couleur-noir);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,45 +1,115 @@
|
|||
@charset "UTF-8";
|
||||
#page-accueil {
|
||||
--hauteur-conteneur: var(--contenu-page-hauteur-minimale-sans-categories);
|
||||
--page-marges-bloc-debut: var(--en-tete-hauteur);
|
||||
--conteneur-marges-internes-ligne: var(--espace-xl);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
min-height: var(--hauteur-conteneur);
|
||||
max-height: var(--hauteur-conteneur);
|
||||
min-block-size: var(--hauteur-conteneur);
|
||||
max-block-size: var(--hauteur-conteneur);
|
||||
margin-top: var(--page-marges-bloc-debut);
|
||||
}
|
||||
#page-accueil .storytelling {
|
||||
overflow-y: scroll;
|
||||
place-items: center;
|
||||
min-height: inherit;
|
||||
max-height: inherit;
|
||||
overscroll-behavior: none;
|
||||
min-block-size: inherit;
|
||||
max-block-size: inherit;
|
||||
}
|
||||
#page-accueil .storytelling__conteneur {
|
||||
overscroll-behavior: inherit;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
min-height: calc(var(--hauteur-conteneur) * 13);
|
||||
padding: 0 var(--espace-xl);
|
||||
place-items: center;
|
||||
min-block-size: calc(var(--hauteur-conteneur) * 13);
|
||||
padding: 0 var(--conteneur-marges-internes-ligne);
|
||||
}
|
||||
#page-accueil .storytelling__animation {
|
||||
--hauteur-animation: 90px;
|
||||
--taille-police: calc(var(--espace-xl) * 2.5);
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
block-size: 100%;
|
||||
margin: auto;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: 1s opacity ease-in-out, 1s visibility ease-in-out;
|
||||
mask-image: linear-gradient(var(--mask-direction, to right), hsla(0, 0%, 0%, 0), hsl(0, 0%, 0%) 20%, hsl(0, 0%, 0%) 80%, hsla(0, 0%, 0%, 0));
|
||||
}
|
||||
#page-accueil .storytelling__animation[hidden] {
|
||||
display: grid !important;
|
||||
/* visibility: hidden;
|
||||
opacity: 0; */
|
||||
transition: 1s opacity ease-in-out, 1s visibility ease-in-out;
|
||||
}
|
||||
#page-accueil .storytelling__animation.no-js {
|
||||
/* visibility: hidden;
|
||||
opacity: 0; */
|
||||
transition: 1s opacity ease-in-out, 1s visibility ease-in-out;
|
||||
}
|
||||
#page-accueil .storytelling__animation .animation-conteneur {
|
||||
overflow: visible;
|
||||
inline-size: 120%;
|
||||
block-size: var(--hauteur-animation);
|
||||
}
|
||||
#page-accueil .storytelling__animation .animation-texte {
|
||||
overflow: visible;
|
||||
font-size: var(--taille-police);
|
||||
font-weight: 600;
|
||||
text-shadow: 4px 4px 0 var(--couleur-blanc);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--espacement-inter-lettres-rapproche-s);
|
||||
}
|
||||
#page-accueil .storytelling__image {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-content: center;
|
||||
width: 100%;
|
||||
min-height: var(--hauteur-conteneur);
|
||||
max-height: var(--hauteur-conteneur);
|
||||
inline-size: max-content;
|
||||
max-inline-size: 100%;
|
||||
min-block-size: var(--hauteur-conteneur);
|
||||
max-block-size: var(--hauteur-conteneur);
|
||||
}
|
||||
#page-accueil .storytelling__image[data-cache] {
|
||||
display: none;
|
||||
#page-accueil .storytelling__image[data-caché] {
|
||||
display: none !important;
|
||||
}
|
||||
#page-accueil .storytelling__image picture {
|
||||
max-height: inherit;
|
||||
max-block-size: inherit;
|
||||
}
|
||||
#page-accueil .storytelling__image img {
|
||||
scale: 0.9;
|
||||
max-height: inherit;
|
||||
scale: 0.95;
|
||||
max-block-size: inherit;
|
||||
margin: auto;
|
||||
object-fit: contain;
|
||||
background: transparent;
|
||||
}
|
||||
@media (scripting: none) {
|
||||
#page-accueil .storytelling__animation {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
@media (width <= 700px) {
|
||||
#page-accueil {
|
||||
--conteneur-marges-internes-ligne: var(--espace-l);
|
||||
}
|
||||
}
|
||||
@media (width <= 500px) {
|
||||
#page-accueil {
|
||||
--conteneur-marges-internes-ligne: var(--espace-m);
|
||||
}
|
||||
}
|
||||
|
||||
@supports (-moz-appearance: none) {
|
||||
#page-accueil .storytelling__animation {
|
||||
--taille-police: calc(var(--espace-xl) * 2.2);
|
||||
}
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=page-accueil.css.map */
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"version":3,"sourceRoot":"","sources":["../../../src/sass/pages/page-accueil.scss"],"names":[],"mappings":"AAEA;EAEE;EAGA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA","file":"page-accueil.css"}
|
||||
{"version":3,"sourceRoot":"","sources":["../../../src/sass/pages/page-accueil.scss"],"names":[],"mappings":";AAEA;EAEE;EAGA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAIF;EACE;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAQA;EACE;AAEA;AAAA;EAEA;;AAIF;AACE;AAAA;EAEA;;AAGF;EACE;EAGA;EACA;;AAGF;EACE;EAGA;EACA;EAGA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAMN;EACE;IACE;;;AAIJ;EAjIF;IAkII;;;AAGF;EArIF;IAsII;;;;AAKJ;EACE;IACE","file":"page-accueil.css"}
|
||||
|
|
@ -1 +1 @@
|
|||
#page-accueil{--hauteur-conteneur:var(--contenu-page-hauteur-minimale-sans-categories);--page-marges-bloc-debut:var(--en-tete-hauteur);min-height:var(--hauteur-conteneur);max-height:var(--hauteur-conteneur);margin-top:var(--page-marges-bloc-debut);flex-flow:column;display:flex;overflow:hidden}#page-accueil .storytelling{min-height:inherit;max-height:inherit;place-items:center;overflow-y:scroll}#page-accueil .storytelling__conteneur{min-height:calc(var(--hauteur-conteneur)*13);padding:0 var(--espace-xl);flex-flow:column;display:flex}#page-accueil .storytelling__image{width:100%;min-height:var(--hauteur-conteneur);max-height:var(--hauteur-conteneur);align-content:center;position:sticky;top:0}#page-accueil .storytelling__image[data-cache]{display:none}#page-accueil .storytelling__image picture{max-height:inherit}#page-accueil .storytelling__image img{max-height:inherit;object-fit:contain;background:0 0;margin:auto;scale:.9}
|
||||
#page-accueil{--hauteur-conteneur:var(--contenu-page-hauteur-minimale-sans-categories);--page-marges-bloc-debut:var(--en-tete-hauteur);--conteneur-marges-internes-ligne:var(--espace-xl);min-block-size:var(--hauteur-conteneur);max-block-size:var(--hauteur-conteneur);margin-top:var(--page-marges-bloc-debut);flex-flow:column;display:flex;overflow:hidden}#page-accueil .storytelling{overscroll-behavior:none;min-block-size:inherit;max-block-size:inherit;overflow-y:scroll}#page-accueil .storytelling__conteneur{overscroll-behavior:inherit;min-block-size:calc(var(--hauteur-conteneur)*13);padding:0 var(--conteneur-marges-internes-ligne);flex-flow:column;place-items:center;display:flex}#page-accueil .storytelling__animation{--hauteur-animation:90px;--taille-police:calc(var(--espace-xl)*2.5);pointer-events:none;z-index:3;visibility:visible;opacity:1;block-size:100%;mask-image:linear-gradient(var(--mask-direction,to right),#0000,#000 20%,#000 80%,#0000);place-content:center;place-items:center;margin:auto;transition:opacity 1s ease-in-out,visibility 1s ease-in-out;display:grid;position:absolute;top:0;left:0;right:0;overflow:hidden}#page-accueil .storytelling__animation[hidden]{transition:opacity 1s ease-in-out,visibility 1s ease-in-out;display:grid!important}#page-accueil .storytelling__animation.no-js{transition:opacity 1s ease-in-out,visibility 1s ease-in-out}#page-accueil .storytelling__animation .animation-conteneur{inline-size:120%;block-size:var(--hauteur-animation);overflow:visible}#page-accueil .storytelling__animation .animation-texte{font-size:var(--taille-police);text-shadow:4px 4px 0 var(--couleur-blanc);text-transform:uppercase;letter-spacing:var(--espacement-inter-lettres-rapproche-s);font-weight:600;overflow:visible}#page-accueil .storytelling__image{inline-size:max-content;max-inline-size:100%;min-block-size:var(--hauteur-conteneur);max-block-size:var(--hauteur-conteneur);align-content:center;position:sticky;top:0}#page-accueil .storytelling__image[data-caché]{display:none!important}#page-accueil .storytelling__image picture{max-block-size:inherit}#page-accueil .storytelling__image img{max-block-size:inherit;object-fit:contain;background:0 0;margin:auto;scale:.95}@media (scripting:none){#page-accueil .storytelling__animation{visibility:hidden}}@media (width<=700px){#page-accueil{--conteneur-marges-internes-ligne:var(--espace-l)}}@media (width<=500px){#page-accueil{--conteneur-marges-internes-ligne:var(--espace-m)}}@supports ((-moz-appearance:none)){#page-accueil .storytelling__animation{--taille-police:calc(var(--espace-xl)*2.2)}}
|
||||
25
web/app/themes/haiku-atelier-2024/src/gleam/README.md
Normal file
25
web/app/themes/haiku-atelier-2024/src/gleam/README.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# haiku_gleam
|
||||
|
||||
[](https://hex.pm/packages/haiku_gleam)
|
||||
[](https://hexdocs.pm/haiku_gleam/)
|
||||
|
||||
```sh
|
||||
gleam add haiku_gleam@1
|
||||
```
|
||||
|
||||
```gleam
|
||||
import haiku_gleam
|
||||
|
||||
pub fn main() -> Nil {
|
||||
// TODO: An example of the project in use
|
||||
}
|
||||
```
|
||||
|
||||
Further documentation can be found at <https://hexdocs.pm/haiku_gleam>.
|
||||
|
||||
## Development
|
||||
|
||||
```sh
|
||||
gleam run # Run the project
|
||||
gleam test # Run the tests
|
||||
```
|
||||
11
web/app/themes/haiku-atelier-2024/src/gleam/gleam.toml
Normal file
11
web/app/themes/haiku-atelier-2024/src/gleam/gleam.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
name = "haiku_gleam"
|
||||
target = "javascript"
|
||||
version = "0.0.1"
|
||||
|
||||
[dependencies]
|
||||
gleam_javascript = ">= 1.0.0 and < 2.0.0"
|
||||
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
|
||||
plinth = ">= 0.6.1 and < 1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
gleeunit = ">= 1.0.0 and < 2.0.0"
|
||||
16
web/app/themes/haiku-atelier-2024/src/gleam/manifest.toml
Normal file
16
web/app/themes/haiku-atelier-2024/src/gleam/manifest.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# This file was generated by Gleam
|
||||
# You typically do not need to edit this file
|
||||
|
||||
packages = [
|
||||
{ name = "gleam_javascript", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "EF6C77A506F026C6FB37941889477CD5E4234FCD4337FF0E9384E297CB8F97EB" },
|
||||
{ name = "gleam_json", version = "3.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "5BA154440B22D9800955B1AB854282FA37B97F30F409D76B0824D0A60C934188" },
|
||||
{ name = "gleam_stdlib", version = "0.60.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "621D600BB134BC239CB2537630899817B1A42E60A1D46C5E9F3FAE39F88C800B" },
|
||||
{ name = "gleeunit", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D33B7736CF0766ED3065F64A1EBB351E72B2E8DE39BAFC8ADA0E35E92A6A934F" },
|
||||
{ name = "plinth", version = "0.6.1", build_tools = ["gleam"], requirements = ["gleam_javascript", "gleam_json", "gleam_stdlib"], otp_app = "plinth", source = "hex", outer_checksum = "02D6421A27795CDC5C3A452240E21D5D3CB8217219020BB247917132E36CC8F1" },
|
||||
]
|
||||
|
||||
[requirements]
|
||||
gleam_javascript = { version = ">= 1.0.0 and < 2.0.0" }
|
||||
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
|
||||
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
|
||||
plinth = { version = ">= 0.6.1 and < 1.0.0" }
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import gleam/io
|
||||
|
||||
pub fn main() -> Nil {
|
||||
io.println("Hello from haiku_gleam!")
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* @param {IntersectionObserverInit} options
|
||||
* @param {IntersectionObserverCallback} callback
|
||||
* @returns {IntersectionObserver}
|
||||
*/
|
||||
export const new_intersection_observer = (options, callback) => {
|
||||
return new IntersectionObserver(callback, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {IntersectionObserver} observer
|
||||
* @param {Element} element
|
||||
*/
|
||||
export const observe_element = (observer, element) => {
|
||||
observer.observe(element);
|
||||
};
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import gleam/option.{type Option}
|
||||
import plinth/browser/document
|
||||
import plinth/browser/element
|
||||
|
||||
pub type IntersectionObserver
|
||||
|
||||
pub type ObservableElement {
|
||||
Element(element.Element)
|
||||
Document(document.Document)
|
||||
}
|
||||
|
||||
pub type Threshold {
|
||||
Threshold(values: List(Int))
|
||||
}
|
||||
|
||||
pub type IntersectionObserverOptions {
|
||||
IntersectionObserverOptions(
|
||||
root: ObservableElement,
|
||||
root_margin: Option(String),
|
||||
threshold: Threshold,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import gleeunit
|
||||
|
||||
pub fn main() -> Nil {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
// gleeunit test functions end in `_test`
|
||||
pub fn hello_world_test() {
|
||||
let name = "Joe"
|
||||
let greeting = "Hello, " <> name <> "!"
|
||||
|
||||
assert greeting == "Hello, Joe!"
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
--hauteur-ligne-rapprochee: 1;
|
||||
|
||||
/* Espacements entre les lettres */
|
||||
--espacement-inter-lettres-rapproche-m: -1px;
|
||||
--espacement-inter-lettres-rapproche-s: -0.5px;
|
||||
--espacement-inter-lettres-etendu-s: 0.5px;
|
||||
--espacement-inter-lettres-etendu-m: 1px;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ html {
|
|||
* 2. Utilise la couleur primaire du site.
|
||||
*/
|
||||
body {
|
||||
overscroll-behavior: none;
|
||||
accent-color: var(--couleur-jaune); /* 2 */
|
||||
background: var(--couleur-gris); /* 1 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,18 @@
|
|||
/* Dispositions */
|
||||
--liste-puce-cercle-puce-position-horizontale: 3.5ch; /* 3 */
|
||||
|
||||
// BASELINE001: Marchera seulement pour les navigateurs > 2023.
|
||||
&:has(a[aria-current="page"]) {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg")
|
||||
center/auto 90% no-repeat;
|
||||
}
|
||||
|
||||
// COMPAT001: Pour les navigateurs < 2023.
|
||||
&--courante {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg")
|
||||
center/auto 90% no-repeat;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block; /* 1 */
|
||||
padding: var(--nav-entree-marges-internes-bloc)
|
||||
|
|
@ -93,11 +105,6 @@
|
|||
text-align: center; /* 4 */
|
||||
}
|
||||
|
||||
&--courante {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg")
|
||||
center/auto 90% no-repeat;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
background: url("/app/themes/haiku-atelier-2024/assets/img/icons/cloud-penche.svg")
|
||||
|
|
|
|||
|
|
@ -182,13 +182,6 @@
|
|||
border-block-end: 1px solid var(--couleur-noir);
|
||||
}
|
||||
|
||||
// La section est dépliée.
|
||||
&:has(button[aria-expanded="true"]) {
|
||||
button {
|
||||
/* padding: initial; */
|
||||
}
|
||||
}
|
||||
|
||||
// La section est fermée.
|
||||
&:has(button[aria-expanded="false"]) .section-textuelle__contenu {
|
||||
display: none;
|
||||
|
|
@ -233,7 +226,7 @@
|
|||
--section-marges-internes: var(--espace-l);
|
||||
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--couleur-noir);
|
||||
border-block: 1px solid var(--couleur-noir);
|
||||
background: var(--couleur-jaune);
|
||||
transition: 0.2s background;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
// Dimensions
|
||||
--carte-produit-longueur-minimale: 448px;
|
||||
--carte-produit-longueur-maximale: 1000px;
|
||||
--en-tete-flottante-hauteur: calc(1rem + var(--espace-l) * 2 + 1px);
|
||||
|
||||
position: relative;
|
||||
display: grid;
|
||||
|
|
@ -18,10 +19,10 @@
|
|||
header {
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
top: calc(1lh + var(--espace-l) + var(--espace-m));
|
||||
top: var(--en-tete-flottante-hauteur);
|
||||
grid-area: en-tete;
|
||||
width: 100%;
|
||||
padding: var(--espace-l) 0 var(--espace-m);
|
||||
padding: var(--espace-l) 0;
|
||||
color: var(--couleur-blanc);
|
||||
text-align: center;
|
||||
background: var(--couleur-noir);
|
||||
|
|
|
|||
|
|
@ -6,50 +6,141 @@
|
|||
|
||||
// Marges
|
||||
--page-marges-bloc-debut: var(--en-tete-hauteur);
|
||||
--conteneur-marges-internes-ligne: var(--espace-xl);
|
||||
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
min-height: var(--hauteur-conteneur);
|
||||
max-height: var(--hauteur-conteneur);
|
||||
min-block-size: var(--hauteur-conteneur);
|
||||
max-block-size: var(--hauteur-conteneur);
|
||||
margin-top: var(--page-marges-bloc-debut);
|
||||
|
||||
.storytelling {
|
||||
overflow-y: scroll;
|
||||
place-items: center;
|
||||
min-height: inherit;
|
||||
max-height: inherit;
|
||||
overscroll-behavior: none;
|
||||
min-block-size: inherit;
|
||||
max-block-size: inherit;
|
||||
|
||||
&__conteneur {
|
||||
overscroll-behavior: inherit;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
min-height: calc(var(--hauteur-conteneur) * 13);
|
||||
padding: 0 var(--espace-xl);
|
||||
place-items: center;
|
||||
min-block-size: calc(var(--hauteur-conteneur) * 13);
|
||||
padding: 0 var(--conteneur-marges-internes-ligne);
|
||||
}
|
||||
|
||||
// Texte animé indiquant à l'Utilisateur de défiler vers le bas.
|
||||
&__animation {
|
||||
--hauteur-animation: 90px;
|
||||
--taille-police: calc(var(--espace-xl) * 2.5);
|
||||
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
block-size: 100%;
|
||||
margin: auto;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: 1s opacity ease-in-out, 1s visibility ease-in-out;
|
||||
mask-image: linear-gradient(
|
||||
var(--mask-direction, to right),
|
||||
hsl(0deg 0% 0% / 0%),
|
||||
hsl(0deg 0% 0% / 100%) 20%,
|
||||
hsl(0deg 0% 0% / 100%) 80%,
|
||||
hsl(0deg 0% 0% / 0%)
|
||||
);
|
||||
|
||||
&[hidden] {
|
||||
display: grid !important;
|
||||
|
||||
/* visibility: hidden;
|
||||
opacity: 0; */
|
||||
transition: 1s opacity ease-in-out, 1s visibility ease-in-out;
|
||||
}
|
||||
|
||||
// N'affiche rien si JavaScript n'est pas activé.
|
||||
&.no-js {
|
||||
/* visibility: hidden;
|
||||
opacity: 0; */
|
||||
transition: 1s opacity ease-in-out, 1s visibility ease-in-out;
|
||||
}
|
||||
|
||||
.animation-conteneur {
|
||||
overflow: visible;
|
||||
|
||||
// Nécessaire pour que les lettres apparaissent « en douceur » dans la vue.
|
||||
inline-size: 120%;
|
||||
block-size: var(--hauteur-animation);
|
||||
}
|
||||
|
||||
.animation-texte {
|
||||
overflow: visible;
|
||||
|
||||
// TODO: Pourquoi y-a-t'il une telle difference de rendu entre Chromium et FF ?
|
||||
font-size: var(--taille-police);
|
||||
font-weight: 600;
|
||||
|
||||
// TODO: Pourquoi y-a-t'il une telle difference de rendu entre Chromium et FF ?
|
||||
text-shadow: 4px 4px 0 var(--couleur-blanc);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: var(--espacement-inter-lettres-rapproche-s);
|
||||
}
|
||||
}
|
||||
|
||||
&__image {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-content: center;
|
||||
width: 100%;
|
||||
min-height: var(--hauteur-conteneur);
|
||||
max-height: var(--hauteur-conteneur);
|
||||
inline-size: max-content;
|
||||
max-inline-size: 100%;
|
||||
min-block-size: var(--hauteur-conteneur);
|
||||
max-block-size: var(--hauteur-conteneur);
|
||||
|
||||
&[data-cache] {
|
||||
display: none;
|
||||
&[data-caché] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
picture {
|
||||
max-height: inherit;
|
||||
max-block-size: inherit;
|
||||
}
|
||||
|
||||
img {
|
||||
scale: 0.9;
|
||||
max-height: inherit;
|
||||
scale: 0.95;
|
||||
max-block-size: inherit;
|
||||
margin: auto;
|
||||
object-fit: contain;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Désactive l'animation si JavaScript n'est pas disponible.
|
||||
@media (scripting: none) {
|
||||
.storytelling__animation {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 700px) {
|
||||
--conteneur-marges-internes-ligne: var(--espace-l);
|
||||
}
|
||||
|
||||
@media (width <= 500px) {
|
||||
--conteneur-marges-internes-ligne: var(--espace-m);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Réduit la taille de la police de l'animation sur FF car elle apparaît plus grande...
|
||||
@supports (-moz-appearance: none) {
|
||||
#page-accueil .storytelling__animation {
|
||||
--taille-police: calc(var(--espace-xl) * 2.2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/** Constantes de valeurs pour la manipulation du DOM : sélecteurs et attributs. */
|
||||
|
||||
export const ATTRIBUT_ACTIF = "data-actif";
|
||||
export const ATTRIBUT_ARIA_CONTROLS = "aria-controls";
|
||||
export const ATTRIBUT_ARIA_EXPANDED = "aria-expanded";
|
||||
export const ATTRIBUT_ARIA_HIDDEN = "aria-hidden";
|
||||
export const ATTRIBUT_ARIA_SELECTED = "aria-selected";
|
||||
export const ATTRIBUT_CACHE = "data-cache";
|
||||
export const ATTRIBUT_ACTIF = "data-actif";
|
||||
export const ATTRIBUT_CACHÉ = "data-caché";
|
||||
export const ATTRIBUT_CHARGEMENT = "data-chargement";
|
||||
export const ATTRIBUT_CLE_PANIER = "data-cle-panier";
|
||||
export const ATTRIBUT_CODE_PROMO_PRESENT = "data-code-promo-present";
|
||||
|
|
@ -24,61 +24,61 @@ export const ATTRIBUT_PRIX = "data-prix";
|
|||
export const ATTRIBUT_TABINDEX = "tabindex";
|
||||
|
||||
// En-tête
|
||||
export const SELECTEUR_BOUTON_MENU_MOBILE = "#bouton-menu-mobile";
|
||||
export const SELECTEUR_BOUTON_PANIER = ".compte-panier a[rel='cart']";
|
||||
export const SELECTEUR_ENTREE_MENU_CATEGORIES_PRODUITS = "#menu-categories-produits ul li a";
|
||||
export const SELECTEUR_FLECHE_DROITE_CATEGORIES_PRODUITS = "#fleche-defilement-categories-produits-droite";
|
||||
export const SELECTEUR_FLECHE_GAUCHE_CATEGORIES_PRODUITS = "#fleche-defilement-categories-produits-gauche";
|
||||
export const SELECTEUR_MENU_CATEGORIES_PRODUITS = "#menu-categories-produits";
|
||||
export const SELECTEUR_MENU_MOBILE = "#menu-mobile";
|
||||
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_MENU_CATEGORIES_PRODUITS = "#menu-categories-produits";
|
||||
export const DOM_MENU_MOBILE = "#menu-mobile";
|
||||
|
||||
// Panier
|
||||
export const SELECTEUR_BOUTON_ACTIONS_FORMULAIRE = "#panneau-informations-client .panneau__pied-de-page button";
|
||||
export const SELECTEUR_BOUTON_ADDITION_QUANTITE = "button.detail-produit__actions__addition";
|
||||
export const SELECTEUR_BOUTON_CODE_PROMO = "#panneau-panier #bouton-code-promo";
|
||||
export const SELECTEUR_BOUTON_SEPARATION_ADRESSES = "#separation-adresses";
|
||||
export const SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE = "button.detail-produit__actions__soustraction";
|
||||
export const SELECTEUR_BOUTON_SUPPRESSION_PANIER = "button.detail-produit__actions__suppression";
|
||||
export const SELECTEUR_CHAMP_CODE_PROMO = "#panneau-panier #champ-code-promo";
|
||||
export const SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER = "input";
|
||||
export const SELECTEUR_CONTENEUR_METHODES_LIVRAISON = "#panneau-panier #choix-methode-livraison";
|
||||
export const SELECTEUR_ENSEMBLE_CODE_PROMO = "#panneau-panier #ensemble-code-promo";
|
||||
export const SELECTEUR_ENTREES_PANIER = "article";
|
||||
export const SELECTEUR_FORMULAIRE_FACTURATION = "#panneau-informations-client .panneau__formulaires__facturation";
|
||||
export const SELECTEUR_FORMULAIRE_LIVRAISON = "#panneau-informations-client .panneau__formulaires__livraison";
|
||||
export const SELECTEUR_FORMULAIRE_PANIER = "#panneau-informations-client form";
|
||||
export const SELECTEUR_INSTRUCTIONS_CLIENT = "#panneau-panier #instructions-client";
|
||||
export const SELECTEUR_MESSAGE_CODE_PROMO = "#panneau-panier .panneau__instructions-code-promo__code-promo__message";
|
||||
export const SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES = "#panneau-informations-client #message-formulaire-adresses";
|
||||
export const SELECTEUR_PRIX_LIGNE_PANIER = ".detail-produit__nom-prix span";
|
||||
export const SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT = "#panneau-panier #sous-total-livraison strong";
|
||||
export const SELECTEUR_SOUS_TOTAL_LIVRAISON_PRESTATAIRE = "#panneau-panier #sous-total-livraison span";
|
||||
export const SELECTEUR_SOUS_TOTAL_PRODUITS = "#panneau-panier #sous-total-produits strong";
|
||||
export const SELECTEUR_TOTAL_PANIER = "#panneau-panier .panneau__pied-de-page p span";
|
||||
export const SELECTEUR_TOTAL_REDUCTION = "#panneau-panier #sous-total-reduction";
|
||||
export const SELECTEUR_TOTAL_REDUCTION_VALEUR = "#panneau-panier #sous-total-reduction strong";
|
||||
export const DOM_BOUTON_ACTIONS_FORMULAIRE = "#panneau-informations-client .panneau__pied-de-page button";
|
||||
export const DOM_BOUTON_ADDITION_QUANTITE = "button.detail-produit__actions__addition";
|
||||
export const DOM_BOUTON_CODE_PROMO = "#panneau-panier #bouton-code-promo";
|
||||
export const DOM_BOUTON_SEPARATION_ADRESSES = "#separation-adresses";
|
||||
export const DOM_BOUTON_SOUSTRACTION_QUANTITE = "button.detail-produit__actions__soustraction";
|
||||
export const DOM_BOUTON_SUPPRESSION_PANIER = "button.detail-produit__actions__suppression";
|
||||
export const DOM_CHAMP_CODE_PROMO = "#panneau-panier #champ-code-promo";
|
||||
export const DOM_CHAMP_QUANTITE_LIGNE_PANIER = "input";
|
||||
export const DOM_CONTENEUR_METHODES_LIVRAISON = "#panneau-panier #choix-methode-livraison";
|
||||
export const DOM_ENSEMBLE_CODE_PROMO = "#panneau-panier #ensemble-code-promo";
|
||||
export const DOM_ENTREES_PANIER = "article";
|
||||
export const DOM_FORMULAIRE_FACTURATION = "#panneau-informations-client .panneau__formulaires__facturation";
|
||||
export const DOM_FORMULAIRE_LIVRAISON = "#panneau-informations-client .panneau__formulaires__livraison";
|
||||
export const DOM_FORMULAIRE_PANIER = "#panneau-informations-client form";
|
||||
export const DOM_INSTRUCTIONS_CLIENT = "#panneau-panier #instructions-client";
|
||||
export const DOM_MESSAGE_CODE_PROMO = "#panneau-panier .panneau__instructions-code-promo__code-promo__message";
|
||||
export const DOM_MESSAGE_FORMULAIRE_ADRESSES = "#panneau-informations-client #message-formulaire-adresses";
|
||||
export const DOM_PRIX_LIGNE_PANIER = ".detail-produit__nom-prix span";
|
||||
export const DOM_SOUS_TOTAL_LIVRAISON_COUT = "#panneau-panier #sous-total-livraison strong";
|
||||
export const DOM_SOUS_TOTAL_LIVRAISON_PRESTATAIRE = "#panneau-panier #sous-total-livraison span";
|
||||
export const DOM_SOUS_TOTAL_PRODUITS = "#panneau-panier #sous-total-produits strong";
|
||||
export const DOM_TOTAL_PANIER = "#panneau-panier .panneau__pied-de-page p span";
|
||||
export const DOM_TOTAL_REDUCTION = "#panneau-panier #sous-total-reduction";
|
||||
export const DOM_TOTAL_REDUCTION_VALEUR = "#panneau-panier #sous-total-reduction strong";
|
||||
|
||||
// Accueil
|
||||
export const SELECTEUR_CONTENEUR_STORYTELLING = ".storytelling";
|
||||
export const SELECTEUR_IMAGES_STORYTELLING = ".storytelling__image";
|
||||
export const DOM_CONTENEUR_ANIMATION = ".storytelling__animation";
|
||||
export const DOM_CONTENEUR_STORYTELLING = ".storytelling";
|
||||
export const DOM_GARDE_FOU_JS = "no-js";
|
||||
export const DOM_IMAGES_STORYTELLING = ".storytelling__image";
|
||||
|
||||
// Boutique
|
||||
export const SELECTEUR_BOUTON_PLUS_PRODUITS = "#page-boutique #bouton-plus-de-produits";
|
||||
export const SELECTEUR_GRILLE_PRODUITS = "#page-boutique .grille-produits";
|
||||
export const DOM_BOUTON_PLUS_PRODUITS = "#page-boutique #bouton-plus-de-produits";
|
||||
export const DOM_GRILLE_PRODUITS = "#page-boutique .grille-produits";
|
||||
|
||||
// À propos
|
||||
export const CLASS_BOITE_TEXTE = "boite-texte";
|
||||
export const CLASS_BOUTON_FERMETURE_BOITE_TEXTE = "boite-texte__bouton-fermeture";
|
||||
export const CLASS_EPINGLE = "epingle";
|
||||
export const SELECTEUR_BOITE_TEXTE = `.${CLASS_BOITE_TEXTE}`;
|
||||
export const SELECTEUR_BOUTON_FERMETURE_BOITE_TEXTE = `.${CLASS_BOUTON_FERMETURE_BOITE_TEXTE}`;
|
||||
export const SELECTEUR_CONTENEUR_STORYTELLING_A_PROPOS = ".storytelling__conteneur";
|
||||
export const SELECTEUR_EPINGLE = `.${CLASS_EPINGLE}`;
|
||||
export const DOM_BOITE_TEXTE = `.${CLASS_BOITE_TEXTE}`;
|
||||
export const DOM_BOUTON_FERMETURE_BOITE_TEXTE = `.${CLASS_BOUTON_FERMETURE_BOITE_TEXTE}`;
|
||||
export const DOM_CONTENEUR_STORYTELLING_A_PROPOS = ".storytelling__conteneur";
|
||||
export const DOM_EPINGLE = `.${CLASS_EPINGLE}`;
|
||||
|
||||
// Produit
|
||||
export const SELECTEUR_BOUTON_AJOUT_PANIER = "#bouton-ajout-panier";
|
||||
export const SELECTEUR_CONTENEUR_PANIER = "#page-panier";
|
||||
export const SELECTEUR_PRIX_PRODUIT = ".selecteur-produit__prix";
|
||||
export const SELECTEUR_SELECTEUR_QUANTITE = "#selecteur-variation";
|
||||
export const SELECTEUR_BOUTONS_ACCORDEON = ".section-textuelle button";
|
||||
export const SELECTEUR_CONTENUS_ACCORDEON = ".section-textuelle__contenu";
|
||||
export const DOM_BOUTON_AJOUT_PANIER = "#bouton-ajout-panier";
|
||||
export const DOM_CONTENEUR_PANIER = "#page-panier";
|
||||
export const DOM_PRIX_PRODUIT = ".selecteur-produit__prix";
|
||||
export const DOM_DOM_QUANTITE = "#selecteur-variation";
|
||||
export const DOM_BOUTONS_ACCORDEON = ".section-textuelle button";
|
||||
export const DOM_CONTENUS_ACCORDEON = ".section-textuelle__contenu";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { configureSync, getConsoleSink, getLogger } from "@logtape/logtape";
|
||||
import chalk from "chalk";
|
||||
import log, { type Logger } from "loglevel";
|
||||
import prefix from "loglevel-plugin-prefix";
|
||||
|
|
@ -19,3 +20,17 @@ prefix.apply(logger, {
|
|||
return `${chalk.gray(`[${timestamp}]`)} ${colors[level.toUpperCase()](level)}`;
|
||||
},
|
||||
});
|
||||
|
||||
const HAIKU_ATELIER_LOGGER = "haiku-atelier";
|
||||
configureSync({
|
||||
loggers: [
|
||||
{
|
||||
category: HAIKU_ATELIER_LOGGER,
|
||||
lowestLevel: "debug",
|
||||
sinks: ["console"],
|
||||
},
|
||||
],
|
||||
sinks: { console: getConsoleSink() },
|
||||
});
|
||||
|
||||
export const nuLogger = getLogger(HAIKU_ATELIER_LOGGER);
|
||||
|
|
@ -4,12 +4,12 @@ import { Either, identity, Left, Right } from "purify-ts";
|
|||
import type { ParentElement } from "./types/dom.d.ts";
|
||||
|
||||
import { ATTRIBUT_CHARGEMENT, ATTRIBUT_DESACTIVE } from "../constantes/dom.ts";
|
||||
import { logger } from "../logging.ts";
|
||||
import { logger } from "../journalisation.ts";
|
||||
import { lanceAnimationCycleLoading } from "./animations.ts";
|
||||
import {
|
||||
BadRequestError,
|
||||
creeSyntaxError,
|
||||
ERREUR_SELECTEUR_INEXISTANT,
|
||||
ERREUR_DOM_INEXISTANT,
|
||||
ERREUR_SYNTAXE_INVALIDE,
|
||||
ForbiddenError,
|
||||
NotFoundError,
|
||||
|
|
@ -26,9 +26,7 @@ export const recupereElementAvecSelecteur =
|
|||
// Transforme le Left en une erreur plus sympathique
|
||||
.mapLeft(_ => creeSyntaxError(ERREUR_SYNTAXE_INVALIDE(selecteur)))
|
||||
// Retourne une SyntaxError si l'Élément est null
|
||||
.chain((e: E | null) =>
|
||||
G.isNotNullable(e) ? Right(e) : Left(creeSyntaxError(ERREUR_SELECTEUR_INEXISTANT(selecteur)))
|
||||
);
|
||||
.chain((e: E | null) => G.isNotNullable(e) ? Right(e) : Left(creeSyntaxError(ERREUR_DOM_INEXISTANT(selecteur))));
|
||||
|
||||
export const getDOMElementsWithSelector =
|
||||
(parent: ParentElement) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, Array<E>> =>
|
||||
|
|
@ -38,7 +36,7 @@ export const getDOMElementsWithSelector =
|
|||
// Transforme le Left en une erreur plus sympathique
|
||||
.mapLeft(_ => creeSyntaxError(ERREUR_SYNTAXE_INVALIDE(selecteur)))
|
||||
// Retourne une SyntaxError si le tableau est vide
|
||||
.chain((e: Array<E>) => A.isEmpty(e) ? Left(creeSyntaxError(ERREUR_SELECTEUR_INEXISTANT(selecteur))) : Right(e));
|
||||
.chain((e: Array<E>) => A.isEmpty(e) ? Left(creeSyntaxError(ERREUR_DOM_INEXISTANT(selecteur))) : Right(e));
|
||||
|
||||
export const recupereElementOuLeve = <E extends Element = Element>(elementOuErreur: Either<SyntaxError, E>): E =>
|
||||
elementOuErreur.caseOf({
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { ErreurAdresseInvalide } from "./erreurs/adresses";
|
|||
|
||||
/* Messages d'erreur */
|
||||
export const ERREUR_SYNTAXE_INVALIDE = (selecteur: string): string => `Le selecteur "${selecteur}" est invalide`;
|
||||
export const ERREUR_SELECTEUR_INEXISTANT = (selecteur: string): string =>
|
||||
export const ERREUR_DOM_INEXISTANT = (selecteur: string): string =>
|
||||
`La requête "${selecteur}" n'a retourné aucun Élément.`;
|
||||
|
||||
/* Création d'erreurs */
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import { WCStoreCartUpdateCustomerArgsSchema } from "../lib/schemas/api/cart-upd
|
|||
import { estWCAddressError } from "../lib/schemas/api/erreurs";
|
||||
import { WCV3OrdersArgsSchema, WCV3OrderSchema } from "../lib/schemas/api/v3/orders";
|
||||
import { safeSchemaParse } from "../lib/validation";
|
||||
import { logger } from "../logging";
|
||||
import { logger } from "../journalisation.ts";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
import { getShippingRatesLS } from "./scripts-page-panier-local-storage";
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
ATTRIBUT_CODE_PROMO_PRESENT,
|
||||
ATTRIBUT_DESACTIVE,
|
||||
ATTRIBUT_HIDDEN,
|
||||
SELECTEUR_BOUTON_CODE_PROMO,
|
||||
DOM_BOUTON_CODE_PROMO,
|
||||
} from "../constantes/dom";
|
||||
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages";
|
||||
import { lanceAnimationCycleLoading } from "../lib/animations";
|
||||
|
|
@ -59,7 +59,7 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
.with(
|
||||
{
|
||||
cible: P.when((cible: EventTarget | null) =>
|
||||
targetMatchesSelector<HTMLButtonElement>(cible, SELECTEUR_BOUTON_CODE_PROMO)
|
||||
targetMatchesSelector<HTMLButtonElement>(cible, DOM_BOUTON_CODE_PROMO)
|
||||
),
|
||||
codePromoPresent: false,
|
||||
valeurCodePromo: P.string,
|
||||
|
|
@ -170,7 +170,7 @@ export const initialiseElementsCodePromo = (): void => {
|
|||
// Un code promo est présent sous forme de chaîne
|
||||
.with(
|
||||
{
|
||||
cible: P.when(cible => targetMatchesSelector<HTMLButtonElement>(cible, SELECTEUR_BOUTON_CODE_PROMO)),
|
||||
cible: P.when(cible => targetMatchesSelector<HTMLButtonElement>(cible, DOM_BOUTON_CODE_PROMO)),
|
||||
codePromoPresent: true,
|
||||
valeurCodePromo: P.string,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
import {
|
||||
SELECTEUR_BOUTON_ACTIONS_FORMULAIRE,
|
||||
SELECTEUR_BOUTON_CODE_PROMO,
|
||||
SELECTEUR_BOUTON_SEPARATION_ADRESSES,
|
||||
SELECTEUR_CHAMP_CODE_PROMO,
|
||||
SELECTEUR_CONTENEUR_METHODES_LIVRAISON,
|
||||
SELECTEUR_CONTENEUR_PANIER,
|
||||
SELECTEUR_ENSEMBLE_CODE_PROMO,
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
SELECTEUR_FORMULAIRE_FACTURATION,
|
||||
SELECTEUR_FORMULAIRE_PANIER,
|
||||
SELECTEUR_INSTRUCTIONS_CLIENT,
|
||||
SELECTEUR_MESSAGE_CODE_PROMO,
|
||||
SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES,
|
||||
SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT,
|
||||
SELECTEUR_SOUS_TOTAL_PRODUITS,
|
||||
SELECTEUR_TOTAL_PANIER,
|
||||
SELECTEUR_TOTAL_REDUCTION,
|
||||
SELECTEUR_TOTAL_REDUCTION_VALEUR,
|
||||
DOM_BOUTON_ACTIONS_FORMULAIRE,
|
||||
DOM_BOUTON_CODE_PROMO,
|
||||
DOM_BOUTON_SEPARATION_ADRESSES,
|
||||
DOM_CHAMP_CODE_PROMO,
|
||||
DOM_CONTENEUR_METHODES_LIVRAISON,
|
||||
DOM_CONTENEUR_PANIER,
|
||||
DOM_ENSEMBLE_CODE_PROMO,
|
||||
DOM_ENTREES_PANIER,
|
||||
DOM_FORMULAIRE_FACTURATION,
|
||||
DOM_FORMULAIRE_PANIER,
|
||||
DOM_INSTRUCTIONS_CLIENT,
|
||||
DOM_MESSAGE_CODE_PROMO,
|
||||
DOM_MESSAGE_FORMULAIRE_ADRESSES,
|
||||
DOM_SOUS_TOTAL_LIVRAISON_COUT,
|
||||
DOM_SOUS_TOTAL_PRODUITS,
|
||||
DOM_TOTAL_PANIER,
|
||||
DOM_TOTAL_REDUCTION,
|
||||
DOM_TOTAL_REDUCTION_VALEUR,
|
||||
} from "../constantes/dom";
|
||||
import { mustGetEleInDocument, recupereElementsDocumentEither } from "../lib/dom";
|
||||
|
||||
export const E = {
|
||||
BOUTON_ACTIONS_FORMULAIRE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_ACTIONS_FORMULAIRE),
|
||||
BOUTON_CODE_PROMO: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_CODE_PROMO),
|
||||
BOUTON_SEPARATION_ADRESSES: mustGetEleInDocument<HTMLInputElement>(SELECTEUR_BOUTON_SEPARATION_ADRESSES),
|
||||
CHAMP_CODE_PROMO: mustGetEleInDocument<HTMLInputElement>(SELECTEUR_CHAMP_CODE_PROMO),
|
||||
CONTENEUR_METHODES_LIVRAISON: mustGetEleInDocument<HTMLFieldSetElement>(SELECTEUR_CONTENEUR_METHODES_LIVRAISON),
|
||||
CONTENEUR_PANIER: mustGetEleInDocument<HTMLElement>(SELECTEUR_CONTENEUR_PANIER),
|
||||
ENSEMBLE_CODE_PROMO: mustGetEleInDocument<HTMLFormElement>(SELECTEUR_ENSEMBLE_CODE_PROMO),
|
||||
BOUTON_ACTIONS_FORMULAIRE: mustGetEleInDocument<HTMLButtonElement>(DOM_BOUTON_ACTIONS_FORMULAIRE),
|
||||
BOUTON_CODE_PROMO: mustGetEleInDocument<HTMLButtonElement>(DOM_BOUTON_CODE_PROMO),
|
||||
BOUTON_SEPARATION_ADRESSES: mustGetEleInDocument<HTMLInputElement>(DOM_BOUTON_SEPARATION_ADRESSES),
|
||||
CHAMP_CODE_PROMO: mustGetEleInDocument<HTMLInputElement>(DOM_CHAMP_CODE_PROMO),
|
||||
CONTENEUR_METHODES_LIVRAISON: mustGetEleInDocument<HTMLFieldSetElement>(DOM_CONTENEUR_METHODES_LIVRAISON),
|
||||
CONTENEUR_PANIER: mustGetEleInDocument<HTMLElement>(DOM_CONTENEUR_PANIER),
|
||||
ENSEMBLE_CODE_PROMO: mustGetEleInDocument<HTMLFormElement>(DOM_ENSEMBLE_CODE_PROMO),
|
||||
ENTREES_PANIER: recupereElementsDocumentEither<HTMLElement>(
|
||||
SELECTEUR_ENTREES_PANIER,
|
||||
DOM_ENTREES_PANIER,
|
||||
),
|
||||
FORMULAIRE_FACTURATION: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_FORMULAIRE_FACTURATION),
|
||||
FORMULAIRE_PANIER: mustGetEleInDocument<HTMLFormElement>(SELECTEUR_FORMULAIRE_PANIER),
|
||||
INSTRUCTIONS_CLIENT: mustGetEleInDocument<HTMLTextAreaElement>(SELECTEUR_INSTRUCTIONS_CLIENT),
|
||||
MESSAGE_ADRESSES: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES),
|
||||
MESSAGE_CODE_PROMO: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_MESSAGE_CODE_PROMO),
|
||||
SOUS_TOTAL_LIVRAISON_VALEUR: mustGetEleInDocument<HTMLElement>(SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT),
|
||||
SOUS_TOTAL_PRODUITS: mustGetEleInDocument<HTMLElement>(SELECTEUR_SOUS_TOTAL_PRODUITS),
|
||||
SOUS_TOTAL_PRODUITS_VALEUR: mustGetEleInDocument<HTMLElement>(SELECTEUR_SOUS_TOTAL_PRODUITS),
|
||||
SOUS_TOTAL_REDUCTION: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
SOUS_TOTAL_REDUCTION_VALEUR: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
TOTAL_PANIER: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_TOTAL_PANIER),
|
||||
TOTAL_PANIER_VALEUR: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_PANIER),
|
||||
TOTAL_REDUCTION_LIGNE: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_TOTAL_REDUCTION),
|
||||
TOTAL_REDUCTION_VALEUR: mustGetEleInDocument<HTMLSpanElement>(SELECTEUR_TOTAL_REDUCTION_VALEUR),
|
||||
FORMULAIRE_FACTURATION: mustGetEleInDocument<HTMLDivElement>(DOM_FORMULAIRE_FACTURATION),
|
||||
FORMULAIRE_PANIER: mustGetEleInDocument<HTMLFormElement>(DOM_FORMULAIRE_PANIER),
|
||||
INSTRUCTIONS_CLIENT: mustGetEleInDocument<HTMLTextAreaElement>(DOM_INSTRUCTIONS_CLIENT),
|
||||
MESSAGE_ADRESSES: mustGetEleInDocument<HTMLParagraphElement>(DOM_MESSAGE_FORMULAIRE_ADRESSES),
|
||||
MESSAGE_CODE_PROMO: mustGetEleInDocument<HTMLParagraphElement>(DOM_MESSAGE_CODE_PROMO),
|
||||
SOUS_TOTAL_LIVRAISON_VALEUR: mustGetEleInDocument<HTMLElement>(DOM_SOUS_TOTAL_LIVRAISON_COUT),
|
||||
SOUS_TOTAL_PRODUITS: mustGetEleInDocument<HTMLElement>(DOM_SOUS_TOTAL_PRODUITS),
|
||||
SOUS_TOTAL_PRODUITS_VALEUR: mustGetEleInDocument<HTMLElement>(DOM_SOUS_TOTAL_PRODUITS),
|
||||
SOUS_TOTAL_REDUCTION: mustGetEleInDocument<HTMLSpanElement>(DOM_TOTAL_REDUCTION_VALEUR),
|
||||
SOUS_TOTAL_REDUCTION_VALEUR: mustGetEleInDocument<HTMLSpanElement>(DOM_TOTAL_REDUCTION_VALEUR),
|
||||
TOTAL_PANIER: mustGetEleInDocument<HTMLParagraphElement>(DOM_TOTAL_PANIER),
|
||||
TOTAL_PANIER_VALEUR: mustGetEleInDocument<HTMLSpanElement>(DOM_TOTAL_PANIER),
|
||||
TOTAL_REDUCTION_LIGNE: mustGetEleInDocument<HTMLDivElement>(DOM_TOTAL_REDUCTION),
|
||||
TOTAL_REDUCTION_VALEUR: mustGetEleInDocument<HTMLSpanElement>(DOM_TOTAL_REDUCTION_VALEUR),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { ADRESSES_MAJ, CODE_PROMO_MAJ, SHIPPING_RATES_UPDATED, TOTALS_UPDATED }
|
|||
import { reporteEtJournaliseErreur } from "../lib/erreurs";
|
||||
import { formateEnEuros } from "../lib/nombres";
|
||||
import { eitherSetSessionStorage } from "../lib/session-storage";
|
||||
import { logger } from "../logging";
|
||||
import { logger } from "../journalisation.ts";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
import { generateShippingRatesHTML } from "./scripts-page-panier-methodes-livraison";
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { formateEnEuros } from "../lib/nombres";
|
|||
import { find } from "../lib/safe-arrays";
|
||||
import { WCStoreCartTotalsSchema } from "../lib/schemas/api/cart";
|
||||
import { getSessionStorageByKey } from "../lib/session-storage";
|
||||
import { logger } from "../logging";
|
||||
import { logger } from "../journalisation.ts";
|
||||
import { E } from "./scripts-page-panier-elements";
|
||||
import { getShippingRatesLS } from "./scripts-page-panier-local-storage";
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ import { ROUTE_API_MAJ_ARTICLE_PANIER, ROUTE_API_RETIRE_ARTICLE_PANIER } from ".
|
|||
import {
|
||||
ATTRIBUT_CLE_PANIER,
|
||||
ATTRIBUT_DESACTIVE,
|
||||
SELECTEUR_BOUTON_ADDITION_QUANTITE,
|
||||
SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE,
|
||||
SELECTEUR_BOUTON_SUPPRESSION_PANIER,
|
||||
SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
DOM_BOUTON_ADDITION_QUANTITE,
|
||||
DOM_BOUTON_SOUSTRACTION_QUANTITE,
|
||||
DOM_BOUTON_SUPPRESSION_PANIER,
|
||||
DOM_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
} from "../constantes/dom";
|
||||
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages";
|
||||
import { mustGetEleInParent } from "../lib/dom";
|
||||
|
|
@ -51,10 +51,10 @@ type CartEntryInteractiveElements = {
|
|||
const getCartEntryInteractiveEles = (entree: HTMLElement): CartEntryInteractiveElements => {
|
||||
const mustGetEle = mustGetEleInParent(entree);
|
||||
return {
|
||||
additionButton: mustGetEle<HTMLButtonElement>(SELECTEUR_BOUTON_ADDITION_QUANTITE),
|
||||
deletionButton: mustGetEle<HTMLButtonElement>(SELECTEUR_BOUTON_SUPPRESSION_PANIER),
|
||||
quantityInput: mustGetEle<HTMLInputElement>(SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER),
|
||||
substractionButton: mustGetEle<HTMLButtonElement>(SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
additionButton: mustGetEle<HTMLButtonElement>(DOM_BOUTON_ADDITION_QUANTITE),
|
||||
deletionButton: mustGetEle<HTMLButtonElement>(DOM_BOUTON_SUPPRESSION_PANIER),
|
||||
quantityInput: mustGetEle<HTMLInputElement>(DOM_CHAMP_QUANTITE_LIGNE_PANIER),
|
||||
substractionButton: mustGetEle<HTMLButtonElement>(DOM_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
.with(P.nullish, () => console.error(event.target))
|
||||
// Clic sur le Bouton d'addition
|
||||
.when(
|
||||
(target: EventTarget) => (target as HTMLElement).matches(SELECTEUR_BOUTON_ADDITION_QUANTITE),
|
||||
(target: EventTarget) => (target as HTMLElement).matches(DOM_BOUTON_ADDITION_QUANTITE),
|
||||
(): void => {
|
||||
void EitherAsync
|
||||
.liftEither(
|
||||
|
|
@ -174,7 +174,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
)
|
||||
// Bouton de soustraction
|
||||
.when(
|
||||
(cible: EventTarget) => (cible as HTMLElement).matches(SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
(cible: EventTarget) => (cible as HTMLElement).matches(DOM_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
(): void => {
|
||||
Maybe
|
||||
// Nécessaire pour que l'on ait une valeur à incrémenter
|
||||
|
|
@ -258,7 +258,7 @@ export const initialiseActionsEntreesPanier = (): void => {
|
|||
)
|
||||
// Bouton de suppression
|
||||
.when(
|
||||
(cible: EventTarget) => (cible as HTMLElement).matches(SELECTEUR_BOUTON_SUPPRESSION_PANIER),
|
||||
(cible: EventTarget) => (cible as HTMLElement).matches(DOM_BOUTON_SUPPRESSION_PANIER),
|
||||
(): void => {
|
||||
Maybe
|
||||
// TODO: Pourquoi ?
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import type { MessageMajBoutonPanier } from "./lib/types/messages";
|
||||
|
||||
import { ATTRIBUT_CONTIENT_ARTICLES, SELECTEUR_BOUTON_PANIER } from "./constantes/dom.ts";
|
||||
import { ATTRIBUT_CONTIENT_ARTICLES, DOM_BOUTON_PANIER } from "./constantes/dom.ts";
|
||||
import { NOM_CANAL_BOUTON_PANIER } from "./constantes/messages.ts";
|
||||
import { mustGetEleInDocument } from "./lib/dom.ts";
|
||||
import { valideMessageMajBoutonPanier } from "./lib/messages.ts";
|
||||
|
|
@ -16,7 +16,7 @@ import { valideMessageMajBoutonPanier } from "./lib/messages.ts";
|
|||
*/
|
||||
const initialiseBoutonPanier = (): void => {
|
||||
/** Le « Bouton » vers le Panier avec un indicateur de la quantité de Produits ajoutés. */
|
||||
const BOUTON_PANIER: HTMLAnchorElement = mustGetEleInDocument<HTMLAnchorElement>(SELECTEUR_BOUTON_PANIER);
|
||||
const BOUTON_PANIER: HTMLAnchorElement = mustGetEleInDocument<HTMLAnchorElement>(DOM_BOUTON_PANIER);
|
||||
const CANAL_BOUTON_PANIER: BroadcastChannel = new BroadcastChannel(NOM_CANAL_BOUTON_PANIER);
|
||||
|
||||
CANAL_BOUTON_PANIER.onmessage = (evenementMessage: MessageEvent<unknown>): void => {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,11 @@ import { pipe } from "@mobily/ts-belt";
|
|||
import { head as arrayHead } from "@mobily/ts-belt/Array";
|
||||
import { tap as optionTap } from "@mobily/ts-belt/Option";
|
||||
|
||||
import {
|
||||
ATTRIBUT_ACTIF,
|
||||
ATTRIBUT_ARIA_HIDDEN,
|
||||
ATTRIBUT_TABINDEX,
|
||||
SELECTEUR_BOUTON_MENU_MOBILE,
|
||||
} from "./constantes/dom";
|
||||
import { ATTRIBUT_ACTIF, ATTRIBUT_ARIA_HIDDEN, ATTRIBUT_TABINDEX, DOM_BOUTON_MENU_MOBILE } from "./constantes/dom";
|
||||
import { mustGetEleInDocument } from "./lib/dom";
|
||||
|
||||
const E = {
|
||||
BOUTON_MENU_MOBILE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_MENU_MOBILE),
|
||||
BOUTON_MENU_MOBILE: mustGetEleInDocument<HTMLButtonElement>(DOM_BOUTON_MENU_MOBILE),
|
||||
BOUTON_RETOUR_SOMMET: mustGetEleInDocument<HTMLButtonElement>("#bouton-retour-haut"),
|
||||
CORPS_HTML: mustGetEleInDocument<HTMLBodyElement>("body"),
|
||||
IMAGE_BOUTON: mustGetEleInDocument<HTMLImageElement>("#bouton-retour-haut img"),
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
import { A } from "@mobily/ts-belt";
|
||||
import { match } from "ts-pattern";
|
||||
|
||||
import { SELECTEUR_ENTREE_MENU_CATEGORIES_PRODUITS, SELECTEUR_MENU_CATEGORIES_PRODUITS } from "./constantes/dom.ts";
|
||||
import { DOM_ENTREE_MENU_CATEGORIES_PRODUITS, DOM_MENU_CATEGORIES_PRODUITS } from "./constantes/dom.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", (): void => {
|
||||
const MENU_CATEGORIES_PRODUITS: HTMLElement = mustGetEleInDocument(SELECTEUR_MENU_CATEGORIES_PRODUITS);
|
||||
const MENU_CATEGORIES_PRODUITS: HTMLElement = mustGetEleInDocument(DOM_MENU_CATEGORIES_PRODUITS);
|
||||
const ENTREES_MENU_CATEGORIES_PRODUITS: Array<HTMLAnchorElement> = mustGetElesInDocument(
|
||||
SELECTEUR_ENTREE_MENU_CATEGORIES_PRODUITS,
|
||||
DOM_ENTREE_MENU_CATEGORIES_PRODUITS,
|
||||
);
|
||||
|
||||
A.forEachWithIndex(
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
import { A, O, pipe } from "@mobily/ts-belt";
|
||||
import A11yDialog from "a11y-dialog";
|
||||
|
||||
import { ATTRIBUT_MENU_MOBILE_ACTIVE, SELECTEUR_BOUTON_MENU_MOBILE, SELECTEUR_MENU_MOBILE } from "./constantes/dom.ts";
|
||||
import { ATTRIBUT_MENU_MOBILE_ACTIVE, DOM_BOUTON_MENU_MOBILE, DOM_MENU_MOBILE } from "./constantes/dom.ts";
|
||||
import { mustGetEleInDocument } from "./lib/dom.ts";
|
||||
|
||||
// Éléments d'intérêt
|
||||
const E = {
|
||||
BOUTON_MENU_MOBILE: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_MENU_MOBILE),
|
||||
BOUTON_MENU_MOBILE: mustGetEleInDocument<HTMLButtonElement>(DOM_BOUTON_MENU_MOBILE),
|
||||
CORPS_HTML: mustGetEleInDocument<HTMLBodyElement>("body"),
|
||||
MENU_MOBILE: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_MENU_MOBILE),
|
||||
MENU_MOBILE: mustGetEleInDocument<HTMLDivElement>(DOM_MENU_MOBILE),
|
||||
};
|
||||
|
||||
const initialiseBoutonMenuMobile = (): void => {
|
||||
|
|
|
|||
|
|
@ -9,20 +9,20 @@ import {
|
|||
ATTRIBUT_ID_ENSEMBLE_EPINGLE_BOITE,
|
||||
CLASS_BOUTON_FERMETURE_BOITE_TEXTE,
|
||||
CLASS_EPINGLE,
|
||||
SELECTEUR_BOITE_TEXTE,
|
||||
SELECTEUR_CONTENEUR_STORYTELLING_A_PROPOS,
|
||||
SELECTEUR_EPINGLE,
|
||||
DOM_BOITE_TEXTE,
|
||||
DOM_CONTENEUR_STORYTELLING_A_PROPOS,
|
||||
DOM_EPINGLE,
|
||||
} from "./constantes/dom.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
|
||||
/** Le Conteneur des images du storytelling. */
|
||||
const CONTENEUR_STORYTELLING = mustGetEleInDocument<HTMLElement>(
|
||||
SELECTEUR_CONTENEUR_STORYTELLING_A_PROPOS,
|
||||
DOM_CONTENEUR_STORYTELLING_A_PROPOS,
|
||||
);
|
||||
/** */
|
||||
const EPINGLES = mustGetElesInDocument<HTMLButtonElement>(SELECTEUR_EPINGLE);
|
||||
const EPINGLES = mustGetElesInDocument<HTMLButtonElement>(DOM_EPINGLE);
|
||||
/** */
|
||||
const BOITES_TEXTE = mustGetElesInDocument<HTMLDivElement>(SELECTEUR_BOITE_TEXTE);
|
||||
const BOITES_TEXTE = mustGetElesInDocument<HTMLDivElement>(DOM_BOITE_TEXTE);
|
||||
/** */
|
||||
const ENSEMBLES_EPINGLES_BOITES_TEXTE = new Map<string, [HTMLButtonElement, HTMLDivElement]>();
|
||||
A.forEachWithIndex(EPINGLES, (index, epingle) => {
|
||||
|
|
|
|||
|
|
@ -3,78 +3,131 @@
|
|||
import { A, O, pipe } from "@mobily/ts-belt";
|
||||
|
||||
import {
|
||||
ATTRIBUT_ARIA_HIDDEN,
|
||||
ATTRIBUT_CACHE,
|
||||
SELECTEUR_CONTENEUR_STORYTELLING,
|
||||
SELECTEUR_IMAGES_STORYTELLING,
|
||||
ATTRIBUT_CACHÉ,
|
||||
ATTRIBUT_HIDDEN,
|
||||
DOM_CONTENEUR_ANIMATION,
|
||||
DOM_CONTENEUR_STORYTELLING,
|
||||
DOM_GARDE_FOU_JS,
|
||||
DOM_IMAGES_STORYTELLING,
|
||||
} from "./constantes/dom.ts";
|
||||
import { nuLogger } from "./journalisation.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument } from "./lib/dom.ts";
|
||||
import { estEntreDeuxNombres } from "./lib/nombres.ts";
|
||||
|
||||
const initialiseScrollStorytelling = (): void => {
|
||||
const E = {
|
||||
/** Le conteneur des images du storytelling. */
|
||||
CONTENEUR_STORYTELLING: mustGetEleInDocument<HTMLElement>(".storytelling__conteneur"),
|
||||
/** Les images du storytelling. */
|
||||
IMAGES_STORYTELLING: mustGetElesInDocument<HTMLDivElement>(SELECTEUR_IMAGES_STORYTELLING),
|
||||
/** Le bloc contenant le storytelling. */
|
||||
STORYTELLING: mustGetEleInDocument<HTMLElement>(SELECTEUR_CONTENEUR_STORYTELLING),
|
||||
};
|
||||
const E = {
|
||||
/** Le bloc contenant l'animation. */
|
||||
CONTENEUR_ANIMATION: mustGetEleInDocument<HTMLDivElement>(DOM_CONTENEUR_ANIMATION),
|
||||
/** Le conteneur des images du storytelling. */
|
||||
CONTENEUR_STORYTELLING: mustGetEleInDocument<HTMLElement>(".storytelling__conteneur"),
|
||||
/** Les images du storytelling. */
|
||||
IMAGES_STORYTELLING: mustGetElesInDocument<HTMLDivElement>(DOM_IMAGES_STORYTELLING),
|
||||
/** Le bloc contenant le storytelling. */
|
||||
STORYTELLING: mustGetEleInDocument<HTMLElement>(DOM_CONTENEUR_STORYTELLING),
|
||||
};
|
||||
|
||||
/**
|
||||
* Retire la classe garde-fou `.js` cachant les éléments nécessitant JavaScript pour s'afficher/fonctionner correctement.
|
||||
*/
|
||||
const retireClasseGardeFouJs = (): void => {
|
||||
E.CONTENEUR_ANIMATION.classList.remove(DOM_GARDE_FOU_JS);
|
||||
};
|
||||
|
||||
const initDefilementStorytelling = (): void => {
|
||||
/** La hauteur d'une image du storytelling. */
|
||||
let hauteurImage = E.IMAGES_STORYTELLING.at(0)?.getBoundingClientRect().height ?? 0;
|
||||
let dimensionsImage = {
|
||||
height: E.IMAGES_STORYTELLING.at(0)?.getBoundingClientRect().height ?? 0,
|
||||
width: E.IMAGES_STORYTELLING.at(0)?.getBoundingClientRect().width ?? 0,
|
||||
};
|
||||
/** La position du défilement (en pixels) du Conteneur des images du storytelling. */
|
||||
let positionDefilementConteneur = 0;
|
||||
|
||||
nuLogger.debug`initStorytellingScroll | dimensionsImages ${dimensionsImage.height}`;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
* Bascule la visibilité d'une image en
|
||||
* @param image
|
||||
* @param visible
|
||||
* @param estVisible
|
||||
*/
|
||||
const changeVisibiliteImage = (image: HTMLDivElement, visible: boolean) => {
|
||||
image.toggleAttribute(ATTRIBUT_CACHE, visible);
|
||||
image.toggleAttribute(ATTRIBUT_ARIA_HIDDEN, visible);
|
||||
const basculeVisibilitéImage = (image: HTMLDivElement, estVisible: boolean) => {
|
||||
image.toggleAttribute(ATTRIBUT_CACHÉ, estVisible);
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
const majDimensions = (): void => {
|
||||
hauteurImage = pipe(
|
||||
A.getBy(E.IMAGES_STORYTELLING, (i: HTMLDivElement) => !i.hasAttribute(ATTRIBUT_CACHE)),
|
||||
O.map((i: HTMLDivElement) => i.getBoundingClientRect().height),
|
||||
O.getWithDefault(0),
|
||||
const majDimensionsStorytelling = (): void => {
|
||||
dimensionsImage = pipe(
|
||||
A.getBy(E.IMAGES_STORYTELLING, (i: HTMLDivElement) => !i.hasAttribute(ATTRIBUT_CACHÉ)),
|
||||
O.map((i: HTMLDivElement) => ({
|
||||
height: i.getBoundingClientRect().height,
|
||||
width: i.getBoundingClientRect().width,
|
||||
})),
|
||||
O.getWithDefault({ height: 0, width: 0 }),
|
||||
);
|
||||
E.CONTENEUR_STORYTELLING.style.minHeight = `${String(hauteurImage * E.IMAGES_STORYTELLING.length + 61)}px`;
|
||||
E.CONTENEUR_STORYTELLING.style.maxHeight = `${String(hauteurImage * E.IMAGES_STORYTELLING.length + 61)}px`;
|
||||
nuLogger.debug`majDimensions | dimensionsImage ${dimensionsImage}`;
|
||||
|
||||
// Adapte la longueur du conteneur d'animation à la nouvelle longueur d'une image.
|
||||
E.CONTENEUR_ANIMATION.style.inlineSize = `${String(dimensionsImage.width)}px`;
|
||||
|
||||
// Adapte la hauteur du conteneur des images pour un défilement « seamless ».
|
||||
const nouvelleHauteurMax = `${String(dimensionsImage.height * E.IMAGES_STORYTELLING.length + 61)}px`;
|
||||
E.CONTENEUR_STORYTELLING.style.minHeight = nouvelleHauteurMax;
|
||||
E.CONTENEUR_STORYTELLING.style.maxHeight = nouvelleHauteurMax;
|
||||
nuLogger.debug`majDimensions | nouvelleHauteurMax ${nouvelleHauteurMax}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
const majImages = (): void => {
|
||||
// Met à jour la position du défilement dans le Conteneur
|
||||
const majVisibilitéImagesStorytelling = (): void => {
|
||||
// Met à jour la position du défilement dans le Conteneur.
|
||||
positionDefilementConteneur = E.STORYTELLING.scrollTop;
|
||||
|
||||
// Met à jour l'attribut de visibilité des images en fonction du défilement
|
||||
// Met à jour l'attribut de visibilité des images en fonction du défilement.
|
||||
E.IMAGES_STORYTELLING.forEach((image: HTMLDivElement, index: number): void => {
|
||||
const debutYImage = hauteurImage * index;
|
||||
const finYImage = hauteurImage * (index + 1);
|
||||
const debutYImage = dimensionsImage.height * index;
|
||||
const finYImage = dimensionsImage.height * (index + 1);
|
||||
|
||||
changeVisibiliteImage(image, !estEntreDeuxNombres(positionDefilementConteneur, debutYImage, finYImage));
|
||||
basculeVisibilitéImage(image, !estEntreDeuxNombres(positionDefilementConteneur, debutYImage, finYImage));
|
||||
});
|
||||
};
|
||||
|
||||
// Initialise l'Observateur de Redimensionnement (ResizeObserver)
|
||||
// Initialise l'Observateur de Redimensionnement (ResizeObserver).
|
||||
new ResizeObserver((): void => {
|
||||
majDimensions();
|
||||
majImages();
|
||||
majDimensionsStorytelling();
|
||||
majVisibilitéImagesStorytelling();
|
||||
}).observe(E.STORYTELLING);
|
||||
|
||||
// Initialise la mise à jour des images au défilement sur le Conteneur
|
||||
E.STORYTELLING.addEventListener("scroll", (): void => majImages());
|
||||
// Initialise la mise à jour des images au défilement sur le Conteneur.
|
||||
E.STORYTELLING.addEventListener("scroll", (): void => majVisibilitéImagesStorytelling());
|
||||
};
|
||||
|
||||
const initGestionAnimation = (): void => {
|
||||
pipe(
|
||||
A.at(E.IMAGES_STORYTELLING, 0),
|
||||
O.tap(img => {
|
||||
const options: IntersectionObserverInit = {
|
||||
root: null,
|
||||
rootMargin: "0px",
|
||||
threshold: 0,
|
||||
};
|
||||
const callback = (entries: Array<IntersectionObserverEntry>) => {
|
||||
A.forEach(entries, e => {
|
||||
e.intersectionRatio === 1
|
||||
? E.CONTENEUR_ANIMATION.removeAttribute(ATTRIBUT_HIDDEN)
|
||||
: E.CONTENEUR_ANIMATION.setAttribute(ATTRIBUT_HIDDEN, "");
|
||||
|
||||
nuLogger.debug`initGestionAnimation | estCache ${e.intersectionRatio === 1} | ${e}`;
|
||||
});
|
||||
};
|
||||
|
||||
new IntersectionObserver(callback, options).observe(img);
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", (): void => {
|
||||
initialiseScrollStorytelling();
|
||||
retireClasseGardeFouJs();
|
||||
initDefilementStorytelling();
|
||||
initGestionAnimation();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import {
|
|||
ATTRIBUT_HIDDEN,
|
||||
ATTRIBUT_ID_CATEGORIE_PRODUITS,
|
||||
ATTRIBUT_PAGE,
|
||||
SELECTEUR_BOUTON_PLUS_PRODUITS,
|
||||
SELECTEUR_GRILLE_PRODUITS,
|
||||
DOM_BOUTON_PLUS_PRODUITS,
|
||||
DOM_GRILLE_PRODUITS,
|
||||
} from "./constantes/dom.ts";
|
||||
import { lanceAnimationCycleLoading } from "./lib/animations.ts";
|
||||
import { html, mustGetEleInDocument } from "./lib/dom.ts";
|
||||
|
|
@ -43,8 +43,8 @@ const PRODUCTS_PER_PAGE = 12;
|
|||
|
||||
// Éléments d'intérêt
|
||||
const E = {
|
||||
BOUTON_PLUS_DE_PRODUITS: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_PLUS_PRODUITS),
|
||||
GRILLE_PRODUITS: mustGetEleInDocument<HTMLDivElement>(SELECTEUR_GRILLE_PRODUITS),
|
||||
BOUTON_PLUS_DE_PRODUITS: mustGetEleInDocument<HTMLButtonElement>(DOM_BOUTON_PLUS_PRODUITS),
|
||||
GRILLE_PRODUITS: mustGetEleInDocument<HTMLDivElement>(DOM_GRILLE_PRODUITS),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ import {
|
|||
ATTRIBUT_CONTIENT_ARTICLES,
|
||||
ATTRIBUT_DESACTIVE,
|
||||
ATTRIBUT_HIDDEN,
|
||||
SELECTEUR_BOUTON_ADDITION_QUANTITE,
|
||||
SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE,
|
||||
SELECTEUR_BOUTON_SUPPRESSION_PANIER,
|
||||
SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
SELECTEUR_PRIX_LIGNE_PANIER,
|
||||
DOM_BOUTON_ADDITION_QUANTITE,
|
||||
DOM_BOUTON_SOUSTRACTION_QUANTITE,
|
||||
DOM_BOUTON_SUPPRESSION_PANIER,
|
||||
DOM_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
DOM_PRIX_LIGNE_PANIER,
|
||||
} from "./constantes/dom.ts";
|
||||
import { NOM_CANAL_BOUTON_PANIER, NOM_CANAL_CONTENU_PANIER } from "./constantes/messages.ts";
|
||||
import { getDOMElementsWithSelector, recupereElementAvecSelecteur, recupereElementOuLeve } from "./lib/dom.ts";
|
||||
|
|
@ -73,10 +73,10 @@ const majEtatsActivationBoutons = (entrees: Array<HTMLElement>): void =>
|
|||
const recupereElementDansEntree = recupereElementDansEntreePanierOuLeve(entree);
|
||||
|
||||
const elements: ElementsEntreePanier = {
|
||||
boutonAddition: recupereElementDansEntree<HTMLButtonElement>(SELECTEUR_BOUTON_ADDITION_QUANTITE),
|
||||
boutonSoustraction: recupereElementDansEntree<HTMLButtonElement>(SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
boutonSuppression: recupereElementDansEntree<HTMLButtonElement>(SELECTEUR_BOUTON_SUPPRESSION_PANIER),
|
||||
champQuantite: recupereElementDansEntree<HTMLInputElement>(SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER),
|
||||
boutonAddition: recupereElementDansEntree<HTMLButtonElement>(DOM_BOUTON_ADDITION_QUANTITE),
|
||||
boutonSoustraction: recupereElementDansEntree<HTMLButtonElement>(DOM_BOUTON_SOUSTRACTION_QUANTITE),
|
||||
boutonSuppression: recupereElementDansEntree<HTMLButtonElement>(DOM_BOUTON_SUPPRESSION_PANIER),
|
||||
champQuantite: recupereElementDansEntree<HTMLInputElement>(DOM_CHAMP_QUANTITE_LIGNE_PANIER),
|
||||
};
|
||||
|
||||
Number(elements.champQuantite?.value) === 1
|
||||
|
|
@ -115,9 +115,9 @@ const initialiseMajContenuPanier = (): void => {
|
|||
const recupereElementDansEntree = recupereElementDansEntreePanierOuLeve(entree);
|
||||
|
||||
// Récupère les Éléments à mettre à jour
|
||||
const prixLigne = recupereElementDansEntree<HTMLSpanElement>(SELECTEUR_PRIX_LIGNE_PANIER);
|
||||
const prixLigne = recupereElementDansEntree<HTMLSpanElement>(DOM_PRIX_LIGNE_PANIER);
|
||||
const champQuantite = recupereElementDansEntree<HTMLInputElement>(
|
||||
SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
DOM_CHAMP_QUANTITE_LIGNE_PANIER,
|
||||
);
|
||||
|
||||
// Met à jour les valeurs
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ import {
|
|||
ATTRIBUT_DESACTIVE,
|
||||
ATTRIBUT_HIDDEN,
|
||||
ATTRIBUT_PRIX,
|
||||
SELECTEUR_BOUTON_AJOUT_PANIER,
|
||||
SELECTEUR_BOUTONS_ACCORDEON,
|
||||
SELECTEUR_CONTENUS_ACCORDEON,
|
||||
SELECTEUR_PRIX_PRODUIT,
|
||||
SELECTEUR_SELECTEUR_QUANTITE,
|
||||
DOM_BOUTON_AJOUT_PANIER,
|
||||
DOM_BOUTONS_ACCORDEON,
|
||||
DOM_CONTENUS_ACCORDEON,
|
||||
DOM_PRIX_PRODUIT,
|
||||
DOM_DOM_QUANTITE,
|
||||
} from "./constantes/dom.ts";
|
||||
import { lanceAnimationCycleLoading } from "./lib/animations.ts";
|
||||
import { mustGetEleInDocument, mustGetElesInDocument, recupereElementDocumentEither } from "./lib/dom.ts";
|
||||
|
|
@ -61,11 +61,11 @@ const deplieToutesSections = (ensembleLiensContenus: Array<EnsembleLienContenu>)
|
|||
|
||||
// Éléments d'intérêt
|
||||
const E = {
|
||||
BOUTON_AJOUT_PANIER: mustGetEleInDocument<HTMLButtonElement>(SELECTEUR_BOUTON_AJOUT_PANIER),
|
||||
BOUTONS_ACCORDEON: mustGetElesInDocument<HTMLAnchorElement>(SELECTEUR_BOUTONS_ACCORDEON),
|
||||
CONTENUS_ACCORDEON: mustGetElesInDocument<HTMLDivElement>(SELECTEUR_CONTENUS_ACCORDEON),
|
||||
PRIX_PRODUIT: mustGetEleInDocument<HTMLParagraphElement>(SELECTEUR_PRIX_PRODUIT),
|
||||
SELECTEUR_VARIATION: recupereElementDocumentEither<HTMLSelectElement>(SELECTEUR_SELECTEUR_QUANTITE),
|
||||
BOUTON_AJOUT_PANIER: mustGetEleInDocument<HTMLButtonElement>(DOM_BOUTON_AJOUT_PANIER),
|
||||
BOUTONS_ACCORDEON: mustGetElesInDocument<HTMLAnchorElement>(DOM_BOUTONS_ACCORDEON),
|
||||
CONTENUS_ACCORDEON: mustGetElesInDocument<HTMLDivElement>(DOM_CONTENUS_ACCORDEON),
|
||||
PRIX_PRODUIT: mustGetEleInDocument<HTMLParagraphElement>(DOM_PRIX_PRODUIT),
|
||||
DOM_VARIATION: recupereElementDocumentEither<HTMLSelectElement>(DOM_DOM_QUANTITE),
|
||||
};
|
||||
|
||||
const gereAccordeonDetailsProduit = (): void => {
|
||||
|
|
@ -99,7 +99,7 @@ const gereAccordeonDetailsProduit = (): void => {
|
|||
});
|
||||
|
||||
// Ajoute des Écouteurs d'Événements
|
||||
E.SELECTEUR_VARIATION.ifRight((selecteur): void =>
|
||||
E.DOM_VARIATION.ifRight((selecteur): void =>
|
||||
selecteur.addEventListener("change", (evenement: Event): void => {
|
||||
const cibleSelecteur: Maybe<HTMLSelectElement> = Maybe
|
||||
.fromNullable(evenement.target)
|
||||
|
|
@ -125,7 +125,7 @@ const gereAccordeonDetailsProduit = (): void => {
|
|||
const ajouteProduitAuPanier = (): void => {
|
||||
// Construis les arguments de la requête au backend
|
||||
const argsRequete: WCStoreCartAddItemArgs = {
|
||||
id: E.SELECTEUR_VARIATION
|
||||
id: E.DOM_VARIATION
|
||||
.map((selecteur: HTMLSelectElement): number => Number(selecteur.value))
|
||||
// Récupère l'ID du Produit de la Page pour les Produits simples
|
||||
.orDefault(ETATS_PAGE.idProduit),
|
||||
|
|
|
|||
|
|
@ -2,21 +2,117 @@
|
|||
{% import "macros/images.twig" as images %}
|
||||
|
||||
{% block contenu %}
|
||||
<main id="page-accueil">
|
||||
<main
|
||||
id="page-accueil"
|
||||
aria-label="Scroll down to navigate through the pictures"
|
||||
>
|
||||
<div class="storytelling">
|
||||
<div class="storytelling__conteneur">
|
||||
<div
|
||||
class="storytelling__image"
|
||||
data-index="0"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll0", "", 903, 1080, "image-scroll0")
|
||||
}}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="storytelling__conteneur"
|
||||
inert
|
||||
>
|
||||
{# Animation #}
|
||||
<div class="storytelling__animation no-js">
|
||||
<svg
|
||||
class="animation-conteneur"
|
||||
height="90px"
|
||||
preserveAspectRatio="xMidYMin"
|
||||
viewBox="0 0 1200 90"
|
||||
width="100%"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
y="50%"
|
||||
class="animation-texte"
|
||||
>
|
||||
<path
|
||||
d="m0 0c600-90 600 90 1200 0"
|
||||
fill="transparent"
|
||||
id="curve-1"
|
||||
/>
|
||||
|
||||
{# TODO: Créer une fonction pour générer les images #}
|
||||
<text dominant-baseline="middle">
|
||||
<textPath
|
||||
id="text-path-1"
|
||||
xlink:href="#curve-1"
|
||||
>
|
||||
Scroll down
|
||||
</textPath>
|
||||
<animate
|
||||
attributeName="startOffset"
|
||||
dur="5s"
|
||||
fill="remove"
|
||||
from="-50%"
|
||||
to="0%"
|
||||
repeatCount="indefinite"
|
||||
xlink:href="#text-path-1"
|
||||
/>
|
||||
</text>
|
||||
|
||||
<text dominant-baseline="middle">
|
||||
<textPath
|
||||
id="text-path-2"
|
||||
xlink:href="#curve-1"
|
||||
>
|
||||
Scroll down
|
||||
</textPath>
|
||||
<animate
|
||||
attributeName="startOffset"
|
||||
dur="5s"
|
||||
fill="remove"
|
||||
from="0%"
|
||||
to="50%"
|
||||
repeatCount="indefinite"
|
||||
xlink:href="#text-path-2"
|
||||
/>
|
||||
</text>
|
||||
|
||||
<text dominant-baseline="middle">
|
||||
<textPath
|
||||
id="text-path-3"
|
||||
xlink:href="#curve-1"
|
||||
>
|
||||
Scroll down
|
||||
</textPath>
|
||||
<animate
|
||||
attributeName="startOffset"
|
||||
dur="5s"
|
||||
fill="remove"
|
||||
from="50%"
|
||||
to="100%"
|
||||
repeatCount="indefinite"
|
||||
xlink:href="#text-path-3"
|
||||
/>
|
||||
</text>
|
||||
|
||||
<text dominant-baseline="middle">
|
||||
<textPath
|
||||
id="text-path-4"
|
||||
xlink:href="#curve-1"
|
||||
>
|
||||
Scroll down
|
||||
</textPath>
|
||||
<animate
|
||||
attributeName="startOffset"
|
||||
dur="5s"
|
||||
fill="remove"
|
||||
from="100%"
|
||||
to="150%"
|
||||
repeatCount="indefinite"
|
||||
xlink:href="#text-path-4"
|
||||
/>
|
||||
</text>
|
||||
</svg>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{# Images #}
|
||||
<div
|
||||
class="storytelling__image"
|
||||
data-index="1"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll1", "", 903, 1080, "image-scroll1")
|
||||
|
|
@ -24,10 +120,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="2"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll2", "", 903, 1080, "image-scroll2")
|
||||
|
|
@ -35,10 +131,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="3"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll3", "", 903, 1080, "image-scroll3")
|
||||
|
|
@ -46,10 +142,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="4"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll4", "", 903, 1080, "image-scroll4")
|
||||
|
|
@ -57,10 +153,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="5"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll5", "", 903, 1080, "image-scroll5")
|
||||
|
|
@ -68,10 +164,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="6"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll6", "", 903, 1080, "image-scroll6")
|
||||
|
|
@ -79,10 +175,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="7"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll7", "", 903, 1080, "image-scroll7")
|
||||
|
|
@ -90,10 +186,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="8"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll8", "", 903, 1080, "image-scroll8")
|
||||
|
|
@ -101,10 +197,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="9"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll9", "", 903, 1080, "image-scroll9")
|
||||
|
|
@ -112,10 +208,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="10"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll10", "", 903, 1080, "image-scroll10")
|
||||
|
|
@ -123,10 +219,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="11"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll11", "", 903, 1080, "image-scroll11")
|
||||
|
|
@ -134,10 +230,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="12"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll12", "", 903, 1080, "image-scroll12")
|
||||
|
|
@ -145,10 +241,10 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
class="storytelling__image"
|
||||
data-cache
|
||||
data-caché
|
||||
data-index="13"
|
||||
tabindex="-1"
|
||||
>
|
||||
{{
|
||||
images.genere_source_img_multi_formats("#{ site.theme.link }/assets/img/storytelling/scroll13", "", 903, 1080, "image-scroll13")
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
id="bouton-plus-de-produits"
|
||||
type="button"
|
||||
>
|
||||
Show more products
|
||||
Show more
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
</button>
|
||||
</section>
|
||||
|
||||
{# TODO: Utiliser un Menu WordPress #}
|
||||
{# TODO: Utiliser des <span> À L'INTÉRIEUR de <li> #}
|
||||
{# TODO: Utiliser un Menu WordPress ? #}
|
||||
<nav
|
||||
class="menu-navigation"
|
||||
id="menu-navigation-en-tete"
|
||||
|
|
@ -28,6 +27,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ page_courante == pages.home.lien ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.home.lien }}"
|
||||
>
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ est_page_boutique ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.shop.lien }}"
|
||||
>
|
||||
|
|
@ -52,6 +53,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ page_courante == pages.about.lien ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.about.lien }}"
|
||||
>
|
||||
|
|
@ -65,6 +67,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ page_courante == pages.contact.lien ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.contact.lien }}"
|
||||
>
|
||||
|
|
@ -110,6 +113,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ page_courante == pages.home.lien ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.home.lien }}"
|
||||
>
|
||||
|
|
@ -123,6 +127,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ est_page_boutique ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.shop.lien }}"
|
||||
>
|
||||
|
|
@ -136,6 +141,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ page_courante == pages.about.lien ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.about.lien }}"
|
||||
>
|
||||
|
|
@ -149,6 +155,7 @@
|
|||
>
|
||||
<span>
|
||||
<a
|
||||
{{ page_courante == pages.contact.lien ? "aria-current=page" : ""}}
|
||||
class="lien-menu"
|
||||
href="{{ pages.contact.lien }}"
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue