2024-09-06

This commit is contained in:
gcch 2024-09-06 10:47:20 +02:00
commit 082202007b
58 changed files with 247957 additions and 494 deletions

22
.vscode/settings.json vendored
View file

@ -3,10 +3,30 @@
"controle", "controle",
"controles", "controles",
"defini", "defini",
"deplie",
"etats",
"ETATS",
"exts", "exts",
"gere", "gere",
"glitchtip",
"GLITCHTIP",
"leve",
"requete",
"selecteur",
"selecteurs",
"Selectionne", "Selectionne",
"souleve",
"tabpanel", "tabpanel",
"tete" "tete"
] ],
"djlint.pythonPath": "/home/gcch/.local/share/pipxu/venvs/8/bin/python",
"djlint.useVenv": false,
"stylelint.enable": true,
"stylelint.packageManager": "pnpm",
"twiggy.autoInsertSpaces": true,
"twiggy.framework": "ignore",
"twiggy.inlayHints.block": true,
"twiggy.inlayHints.macro": true,
"twiggy.inlayHints.macroArguments": true,
"typescript.tsdk": "node_modules/typescript/lib"
} }

View file

@ -55,7 +55,8 @@
"wpackagist-plugin/polylang": "^3.6", "wpackagist-plugin/polylang": "^3.6",
"wpackagist-plugin/query-monitor": "^3.16", "wpackagist-plugin/query-monitor": "^3.16",
"mnsami/composer-custom-directory-installer": "^2.0", "mnsami/composer-custom-directory-installer": "^2.0",
"htmlburger/carbon-fields": "^3.6" "htmlburger/carbon-fields": "^3.6",
"wpackagist-plugin/wp-openapi": "^1.0"
}, },
"require-dev": { "require-dev": {
"squizlabs/php_codesniffer": "^3.7.1", "squizlabs/php_codesniffer": "^3.7.1",

64
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "028568610be8aa5617070402717dc8a1", "content-hash": "d9349cad6f6e130fcb72a1ed8dc15ed6",
"packages": [ "packages": [
{ {
"name": "composer/installers", "name": "composer/installers",
@ -1465,24 +1465,23 @@
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
"version": "v3.11.0", "version": "v3.12.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/Twig.git", "url": "https://github.com/twigphp/Twig.git",
"reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d" "reference": "4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", "url": "https://api.github.com/repos/twigphp/Twig/zipball/4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea",
"reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", "reference": "4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.2.5", "php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3", "symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8", "symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3", "symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22",
"symfony/polyfill-php81": "^1.29" "symfony/polyfill-php81": "^1.29"
}, },
"require-dev": { "require-dev": {
@ -1529,7 +1528,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/twigphp/Twig/issues", "issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.11.0" "source": "https://github.com/twigphp/Twig/tree/v3.12.0"
}, },
"funding": [ "funding": [
{ {
@ -1541,7 +1540,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-08-08T16:15:16+00:00" "time": "2024-08-29T09:51:12+00:00"
}, },
{ {
"name": "vlucas/phpdotenv", "name": "vlucas/phpdotenv",
@ -1683,15 +1682,15 @@
}, },
{ {
"name": "wpackagist-plugin/woocommerce", "name": "wpackagist-plugin/woocommerce",
"version": "9.2.1", "version": "9.2.3",
"source": { "source": {
"type": "svn", "type": "svn",
"url": "https://plugins.svn.wordpress.org/woocommerce/", "url": "https://plugins.svn.wordpress.org/woocommerce/",
"reference": "tags/9.2.1" "reference": "tags/9.2.3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://downloads.wordpress.org/plugin/woocommerce.9.2.1.zip" "url": "https://downloads.wordpress.org/plugin/woocommerce.9.2.3.zip"
}, },
"require": { "require": {
"composer/installers": "^1.0 || ^2.0" "composer/installers": "^1.0 || ^2.0"
@ -1699,6 +1698,24 @@
"type": "wordpress-plugin", "type": "wordpress-plugin",
"homepage": "https://wordpress.org/plugins/woocommerce/" "homepage": "https://wordpress.org/plugins/woocommerce/"
}, },
{
"name": "wpackagist-plugin/wp-openapi",
"version": "1.0.12",
"source": {
"type": "svn",
"url": "https://plugins.svn.wordpress.org/wp-openapi/",
"reference": "tags/1.0.12"
},
"dist": {
"type": "zip",
"url": "https://downloads.wordpress.org/plugin/wp-openapi.1.0.12.zip"
},
"require": {
"composer/installers": "^1.0 || ^2.0"
},
"type": "wordpress-plugin",
"homepage": "https://wordpress.org/plugins/wp-openapi/"
},
{ {
"name": "wpackagist-theme/twentytwentyfour", "name": "wpackagist-theme/twentytwentyfour",
"version": "1.2", "version": "1.2",
@ -1725,12 +1742,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git", "url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "8938260885863ec2dd9f2aaf9a79ba14e58a92f6" "reference": "f6547941a3b3a96c1b20b77c6ebf126b195f814e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/8938260885863ec2dd9f2aaf9a79ba14e58a92f6", "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f6547941a3b3a96c1b20b77c6ebf126b195f814e",
"reference": "8938260885863ec2dd9f2aaf9a79ba14e58a92f6", "reference": "f6547941a3b3a96c1b20b77c6ebf126b195f814e",
"shasum": "" "shasum": ""
}, },
"conflict": { "conflict": {
@ -1866,8 +1883,9 @@
"dolibarr/dolibarr": "<19.0.2", "dolibarr/dolibarr": "<19.0.2",
"dompdf/dompdf": "<2.0.4", "dompdf/dompdf": "<2.0.4",
"doublethreedigital/guest-entries": "<3.1.2", "doublethreedigital/guest-entries": "<3.1.2",
"drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<10.1.8|>=10.2,<10.2.2", "drupal/core": ">=6,<6.38|>=7,<7.96|>=8,<10.1.8|>=10.2,<10.2.2|==11.9999999.9999999.9999999-dev",
"drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4", "drupal/core-recommended": "==11.9999999.9999999.9999999-dev",
"drupal/drupal": ">=5,<5.11|>=6,<6.38|>=7,<7.80|>=8,<8.9.16|>=9,<9.1.12|>=9.2,<9.2.4|==11.9999999.9999999.9999999-dev",
"duncanmcclean/guest-entries": "<3.1.2", "duncanmcclean/guest-entries": "<3.1.2",
"dweeves/magmi": "<=0.7.24", "dweeves/magmi": "<=0.7.24",
"ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2", "ec-cube/ec-cube": "<2.4.4|>=2.11,<=2.17.1|>=3,<=3.0.18.0-patch4|>=4,<=4.1.2",
@ -1941,7 +1959,7 @@
"friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "friendsoftypo3/mediace": ">=7.6.2,<7.6.5",
"friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6", "friendsoftypo3/openid": ">=4.5,<4.5.31|>=4.7,<4.7.16|>=6,<6.0.11|>=6.1,<6.1.6",
"froala/wysiwyg-editor": "<3.2.7|>=4.0.1,<=4.1.3", "froala/wysiwyg-editor": "<3.2.7|>=4.0.1,<=4.1.3",
"froxlor/froxlor": "<2.1.9", "froxlor/froxlor": "<=2.2.0.0-RC3",
"frozennode/administrator": "<=5.0.12", "frozennode/administrator": "<=5.0.12",
"fuel/core": "<1.8.1", "fuel/core": "<1.8.1",
"funadmin/funadmin": "<=3.2|>=3.3.2,<=3.3.3", "funadmin/funadmin": "<=3.2|>=3.3.2,<=3.3.3",
@ -1949,7 +1967,7 @@
"genix/cms": "<=1.1.11", "genix/cms": "<=1.1.11",
"getformwork/formwork": "<1.13.1|==2.0.0.0-beta1", "getformwork/formwork": "<1.13.1|==2.0.0.0-beta1",
"getgrav/grav": "<1.7.46", "getgrav/grav": "<1.7.46",
"getkirby/cms": "<4.1.1", "getkirby/cms": "<=3.6.6.5|>=3.7,<=3.7.5.4|>=3.8,<=3.8.4.3|>=3.9,<=3.9.8.1|>=3.10,<=3.10.1|>=4,<=4.3",
"getkirby/kirby": "<=2.5.12", "getkirby/kirby": "<=2.5.12",
"getkirby/panel": "<2.5.14", "getkirby/panel": "<2.5.14",
"getkirby/starterkit": "<=3.7.0.2", "getkirby/starterkit": "<=3.7.0.2",
@ -1996,6 +2014,7 @@
"in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.2.3", "in2code/femanager": "<5.5.3|>=6,<6.3.4|>=7,<7.2.3",
"in2code/ipandlanguageredirect": "<5.1.2", "in2code/ipandlanguageredirect": "<5.1.2",
"in2code/lux": "<17.6.1|>=18,<24.0.2", "in2code/lux": "<17.6.1|>=18,<24.0.2",
"in2code/powermail": "<7.5|>=8,<8.5|>=9,<10.9|>=11,<12.4",
"innologi/typo3-appointments": "<2.0.6", "innologi/typo3-appointments": "<2.0.6",
"intelliants/subrion": "<4.2.2", "intelliants/subrion": "<4.2.2",
"inter-mediator/inter-mediator": "==5.5", "inter-mediator/inter-mediator": "==5.5",
@ -2178,7 +2197,7 @@
"phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5", "phpmyfaq/phpmyfaq": "<3.2.5|==3.2.5",
"phpoffice/common": "<0.2.9", "phpoffice/common": "<0.2.9",
"phpoffice/phpexcel": "<1.8", "phpoffice/phpexcel": "<1.8",
"phpoffice/phpspreadsheet": "<1.16", "phpoffice/phpspreadsheet": "<1.29.1|>=2,<2.2.1",
"phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36", "phpseclib/phpseclib": "<2.0.47|>=3,<3.0.36",
"phpservermon/phpservermon": "<3.6", "phpservermon/phpservermon": "<3.6",
"phpsysinfo/phpsysinfo": "<3.4.3", "phpsysinfo/phpsysinfo": "<3.4.3",
@ -2187,9 +2206,10 @@
"phpxmlrpc/extras": "<0.6.1", "phpxmlrpc/extras": "<0.6.1",
"phpxmlrpc/phpxmlrpc": "<4.9.2", "phpxmlrpc/phpxmlrpc": "<4.9.2",
"pi/pi": "<=2.5", "pi/pi": "<=2.5",
"pimcore/admin-ui-classic-bundle": "<=1.5.1", "pimcore/admin-ui-classic-bundle": "<1.5.4",
"pimcore/customer-management-framework-bundle": "<4.0.6", "pimcore/customer-management-framework-bundle": "<4.0.6",
"pimcore/data-hub": "<1.2.4", "pimcore/data-hub": "<1.2.4",
"pimcore/data-importer": "<1.8.9|>=1.9,<1.9.3",
"pimcore/demo": "<10.3", "pimcore/demo": "<10.3",
"pimcore/ecommerce-framework-bundle": "<1.0.10", "pimcore/ecommerce-framework-bundle": "<1.0.10",
"pimcore/perspective-editor": "<1.5.1", "pimcore/perspective-editor": "<1.5.1",
@ -2534,7 +2554,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-08-19T21:04:39+00:00" "time": "2024-09-04T15:05:11+00:00"
}, },
{ {
"name": "squizlabs/php_codesniffer", "name": "squizlabs/php_codesniffer",

View file

@ -9,7 +9,7 @@
], ],
"extends": "/home/gcch/.dprint.jsonc", "extends": "/home/gcch/.dprint.jsonc",
"exec": { "exec": {
"cacheKey": 1, "cacheKey": "1",
"commands": [ "commands": [
{ {
"command": "prettier --ignore-unknown --write --stdin-filepath {{file_path}}", "command": "prettier --ignore-unknown --write --stdin-filepath {{file_path}}",
@ -23,13 +23,13 @@
"useTabs": false "useTabs": false
}, },
"plugins": [ "plugins": [
"https://plugins.dprint.dev/typescript-0.91.6.wasm", "https://plugins.dprint.dev/typescript-0.91.8.wasm",
"https://plugins.dprint.dev/json-0.19.3.wasm", "https://plugins.dprint.dev/json-0.19.3.wasm",
"https://plugins.dprint.dev/markdown-0.17.5.wasm", "https://plugins.dprint.dev/markdown-0.17.8.wasm",
"https://plugins.dprint.dev/toml-0.6.2.wasm", "https://plugins.dprint.dev/toml-0.6.2.wasm",
"https://plugins.dprint.dev/g-plane/malva-v0.9.1.wasm", "https://plugins.dprint.dev/g-plane/malva-v0.10.1.wasm",
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.12.0.wasm", "https://plugins.dprint.dev/g-plane/markup_fmt-v0.12.0.wasm",
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.4.0.wasm", "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm",
"https://plugins.dprint.dev/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0" "https://plugins.dprint.dev/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0"
] ]
} }

66455
lib/openapi3_1.json Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

66455
lib/wp-json-openapi.json Normal file

File diff suppressed because it is too large Load diff

99225
lib/wp-json-schema.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -10,36 +10,40 @@
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"dependencies": {
"@sentry/browser": "^8.28.0",
"purify-ts": "^2.1.0",
"remeda": "^2.12.0",
"valibot": "^0.41.0",
"wretch": "^2.9.0"
},
"devDependencies": { "devDependencies": {
"@awmottaz/prettier-plugin-void-html": "^1.6.1", "@biomejs/biome": "^1.8.3",
"@biomejs/biome": "1.8.3",
"@prettier/plugin-php": "^0.22.2", "@prettier/plugin-php": "^0.22.2",
"@prettier/plugin-xml": "^3.4.1", "@prettier/plugin-xml": "^3.4.1",
"@sentry/types": "^8.28.0",
"@swc/cli": "^0.4.0", "@swc/cli": "^0.4.0",
"@swc/core": "^1.7.14",
"@zackad/prettier-plugin-twig": "^0.8.0",
"browserslist": "^4.23.3", "browserslist": "^4.23.3",
"fdir": "^6.2.0", "fdir": "^6.3.0",
"picomatch": "^4.0.2", "picomatch": "^4.0.2",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-pkg": "^0.18.1", "prettier-plugin-pkg": "^0.18.1",
"prettier-plugin-sh": "^0.14.0", "prettier-plugin-sh": "^0.14.0",
"sass": "^1.77.8", "sass": "^1.78.0",
"stylelint": "^16.8.2", "stylelint": "^16.9.0",
"stylelint-config-clean-order": "^6.1.0", "stylelint-config-clean-order": "^6.1.0",
"stylelint-config-sass-guidelines": "^12.0.0", "stylelint-config-sass-guidelines": "^12.0.0",
"stylelint-config-standard-scss": "^13.1.0", "stylelint-config-standard-scss": "^13.1.0",
"stylelint-declaration-block-no-ignored-properties": "^2.8.0", "stylelint-declaration-block-no-ignored-properties": "^2.8.0",
"stylelint-plugin-logical-css": "^1.2.1", "stylelint-plugin-logical-css": "^1.2.1",
"typescript": "^5.5.4", "typescript": "^5.5.4",
"vite": "^5.4.2" "vite": "^5.4.3",
"vite-plugin-valibot-env": "^0.6.11",
"wp-types": "^4.66.1"
}, },
"browserslist": [ "browserslist": [
"defaults", "defaults",
"last 3 years", "last 3 years",
"not dead" "not dead"
], ]
"dependencies": {
"purify-ts": "^2.1.0"
}
} }

781
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -37,7 +37,7 @@ export default {
xmlWhitespaceSensitivity: "strict", xmlWhitespaceSensitivity: "strict",
}, },
}, },
// Pour package.json // package.json
{ {
files: ["package.json"], files: ["package.json"],
options: { options: {

View file

@ -1,27 +1,54 @@
{ {
"compilerOptions": { "compilerOptions": {
"allowArbitraryExtensions": true, "allowArbitraryExtensions": true,
"allowImportingTsExtensions": true,
"allowJs": true, "allowJs": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"alwaysStrict": true,
"checkJs": true, "checkJs": true,
"downlevelIteration": false, "downlevelIteration": false,
"esModuleInterop": true,
"exactOptionalPropertyTypes": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true, "isolatedModules": true,
"lib": ["DOM", "ES2022"], "lib": ["DOM", "ES2020", "ES2022"],
"module": "NodeNext", "module": "ES2022",
"moduleDetection": "force", "moduleDetection": "force",
"moduleResolution": "NodeNext", "moduleResolution": "Bundler",
"noEmit": false, "noEmit": true,
"noImplicitAny": false, "noFallthroughCasesInSwitch": true,
"noImplicitReturns": false, "noImplicitAny": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noPropertyAccessFromIndexSignature": true, "noPropertyAccessFromIndexSignature": true,
"noStrictGenericChecks": false,
"noUncheckedIndexedAccess": true, "noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"skipLibCheck": false,
"strict": true, "strict": true,
"strictBindCallApply": true,
"strictFunctionTypes": true, "strictFunctionTypes": true,
"strictNullChecks": true, "strictNullChecks": true,
"strictPropertyInitialization": true, "strictPropertyInitialization": true,
"target": "ES2022" "suppressExcessPropertyErrors": false,
"suppressImplicitAnyIndexErrors": false,
"target": "ES2022",
"types": ["vite/client"],
"useDefineForClassFields": true,
"useUnknownInCatchVariables": true
}, },
"exclude": ["vendor", "web/app/plugins", "web/wp"], "exclude": [
"include": ["*.js", "lib", "web/app/themes/haiku-atelier-2024/src"] "vendor",
"web/app/plugins",
"web/wp"
],
"include": [
"*.js",
"lib",
"web/app/themes/haiku-atelier-2024/src"
]
} }

View file

@ -1,13 +1,23 @@
import { fdir } from "fdir"; import { fdir } from "fdir";
import { resolve } from "node:path"; import { resolve } from "node:path";
import * as v from "valibot";
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import valibot from "vite-plugin-valibot-env";
const SLUG_THEME = "haiku-atelier-2024";
const SRC_TYPESCRIPT_PATHS = new fdir() const SRC_TYPESCRIPT_PATHS = new fdir()
.withBasePath() .withBasePath()
.glob("**/*.ts") .glob("**/*.ts")
.crawl("web/app/themes/haiku-atelier-2024/src/scripts") .filter((path, isDirectory) => !isDirectory && !path.endsWith("lol.ts"))
.crawl(`web/app/themes/${SLUG_THEME}/src/scripts`)
.withPromise(); .withPromise();
/* Voir le fichier vite.env.d.ts */
const SCHEMA_ENVIRONNEMENT = v.object({
VITE_URL: v.pipe(v.string(), v.nonEmpty(), v.url(), v.readonly()),
VITE_GLITCHTIP_NSD: v.pipe(v.string(), v.url(), v.readonly()),
});
export default defineConfig({ export default defineConfig({
base: "", base: "",
build: { build: {
@ -21,16 +31,21 @@ export default defineConfig({
input: await SRC_TYPESCRIPT_PATHS, input: await SRC_TYPESCRIPT_PATHS,
output: { output: {
assetFileNames: "[name][extname]", assetFileNames: "[name][extname]",
chunkFileNames: "[name][extname]", chunkFileNames: "[name].js",
entryFileNames: "[name].js",
compact: true, compact: true,
minifyInternalExports: true, entryFileNames: "[name].js",
validate: true, validate: true,
}, },
treeshake: "recommended", treeshake: "smallest",
}, },
sourcemap: true, sourcemap: true,
reportCompressedSize: true,
target: "es2023", target: "es2023",
watch: { clearScreen: false },
write: true, write: true,
}, },
plugins: [
/* Permet de valider les variables d'environnements définies à partir d'un schéma Valibot */
valibot(SCHEMA_ENVIRONNEMENT),
],
}); });

View file

@ -557,28 +557,43 @@ ul.avec-puce-cercle a {
letter-spacing: -0.5px; letter-spacing: -0.5px;
} }
/**
* Boîte flottante avec les informations Produit, le sélecteur de variation et de quantité pour le
* Panier.
*
* 1. Flotte toujours en bas de la page et s'arrête avant les Produits de la même collection.
* 2. Conteneur existant pour que le flottement sticky fonctionne ; n'a pas de dimensions.
*/
.informations-produit { .informations-produit {
position: sticky; /* Variables */
bottom: 1rem; --boite-couleur-fond: rgb(255 255 255 / 90%);
--boite-position-basse: 1rem;
--boite-longueur: 70ch;
--section-marges-internes: 1rem;
position: sticky; /* 1 */
bottom: var(--boite-position-basse); /* 1 */
overflow: visible; overflow: visible;
width: 0; width: 0; /* 2 */
height: 0; height: 0; /* 2 */
/* Nom du Produit, sélecteur de variation et prix du Produit */ /* Nom du Produit, sélecteur de variation et prix du Produit */
/* Bouton « Ajouter au Panier » */ /* Bouton « Ajouter au Panier » */
} }
.informations-produit__conteneur { .informations-produit__conteneur {
transform: translate(calc(50vw - 35ch), -100%); transform: translate(calc(50vw - var(--boite-longueur) / 2), -100%);
display: grid; display: grid;
width: 70ch; width: var(--boite-longueur);
max-width: 70ch; max-width: var(--boite-longueur);
border: 1px solid var(--couleur-noir); border: 1px solid var(--couleur-noir);
/**
* Définis les apparence et comportement de toutes les sections de la boîte.
*/
} }
.informations-produit__conteneur > section { .informations-produit__conteneur > section {
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
height: 100%; height: 100%;
padding: 1rem; padding: var(--section-marges-internes);
background: rgba(255, 255, 255, 0.9); background: var(--boite-couleur-fond);
} }
.informations-produit__conteneur > section + section { .informations-produit__conteneur > section + section {
border-top: 1px solid var(--couleur-noir); border-top: 1px solid var(--couleur-noir);
@ -586,12 +601,13 @@ ul.avec-puce-cercle a {
.informations-produit .onglets-details-produit { .informations-produit .onglets-details-produit {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-rows: 1fr min-content; grid-template-rows: auto min-content;
padding: initial; padding: initial;
} }
.informations-produit .onglets-details-produit > section { .informations-produit .onglets-details-produit > section {
overflow-y: auto;
grid-column: span 3; grid-column: span 3;
padding: 1rem; padding: var(--section-marges-internes);
font-weight: 350; font-weight: 350;
} }
.informations-produit .onglets-details-produit > section[hidden] { .informations-produit .onglets-details-produit > section[hidden] {
@ -616,24 +632,36 @@ ul.avec-puce-cercle a {
.informations-produit .onglets-details-produit > ul li { .informations-produit .onglets-details-produit > ul li {
width: 100%; width: 100%;
} }
.informations-produit .onglets-details-produit > ul li:first-of-type a {
border-right-color: transparent;
border-left-color: transparent;
}
.informations-produit .onglets-details-produit > ul li:last-of-type a {
border-right-color: transparent;
border-left-color: transparent;
}
.informations-produit .onglets-details-produit > ul li a { .informations-produit .onglets-details-produit > ul li a {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: var(--espace-xs) 0; padding: var(--espace-xs) 0;
border-top: 1px solid var(--couleur-noir); border-top: 1px solid transparent;
border-right: 1px solid var(--couleur-noir); border-right: 1px solid var(--couleur-noir);
border-left: 1px solid var(--couleur-noir); border-left: 1px solid var(--couleur-noir);
} /* Quand l'onglet est sélectionné */
.informations-produit .onglets-details-produit > ul li a:first-of-type {
border-left: initial;
}
.informations-produit .onglets-details-produit > ul li a:last-of-type {
border-left: initial;
} }
.informations-produit .onglets-details-produit > ul li a[aria-selected=true] { .informations-produit .onglets-details-produit > ul li a[aria-selected=true] {
border-top-color: transparent; border-top-color: transparent;
} }
.informations-produit .onglets-details-produit > ul li a:focus, .informations-produit .onglets-details-produit > ul li a:focus-visible {
outline-color: transparent;
}
.informations-produit .onglets-details-produit:has(section:not([hidden])) {
grid-template-rows: 12lh min-content;
}
.informations-produit .onglets-details-produit:has(section:not([hidden])) > ul li a:not([aria-selected=true]) {
border-top-color: var(--couleur-noir);
}
.informations-produit .selecteur-produit { .informations-produit .selecteur-produit {
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../src/sass/base/polices/_lato.scss","../../src/sass/base/polices/_myriad.scss","../../src/sass/abstracts/_variables.scss","../../src/sass/base/_base.scss","../../src/sass/base/_typographie.scss","../../src/sass/base/elements/_boutons.scss","../../src/sass/base/elements/_images.scss","../../src/sass/base/elements/_liens.scss","../../src/sass/base/elements/_listes.scss","../../src/sass/layouts/_en-tete.scss","../../src/sass/layouts/_menu-categories-produits.scss","../../src/sass/layouts/_colonnes-photos.scss","../../src/sass/layouts/_grille-produits.scss","../../src/sass/layouts/_informations-produit.scss","../../src/sass/layouts/_produits-similaires.scss","../../src/sass/layouts/_pied-de-page.scss"],"names":[],"mappings":";AAAA;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AChGJ;EACE;EACA;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA,KACE;;AChBJ;AACE;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;AAEA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;;;AClBF;AAAA;AAAA;AAAA;AAAA;AAKA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;AAAA;AAAA;EAGE;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;AAIA;EACE;EACA;;;AAGF;AAAA;AAAA;AAGA;AAAA;AAAA;AAAA;EAIE;;;AAGF;AAAA;AAAA;AAGA;EACE;;;ACtDF;EACE;EACA;EACA;;;ACHF;AAAA;AAAA;AAGA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;ACfJ;AAAA;AAAA;AAAA;AAAA;AAKA;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;;;ACbF;AAAA;AAAA;AAAA;AAAA;AAKA;AACE;EACA;EAEA;EACA;EACA;EACA;EACA;EACA,YACE,kHAK4B;EAE9B;AAEA;AAMA;AAAA;AAAA;;AALA;EAEE;;AAMF;AACE;EACA;EACA;AAEA;EACA;EAEA;EACA;AAEA;AAMA;AAKA;;AAVA;EAEE;;AAIF;EACE;;AAIF;EACE;IACE;;;AAMJ;EACE;IACE;;;;AClER;AAAA;AAAA;AAAA;AAAA;AAKA;EACE;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAMA;AACE;EACA;AAEA;EACA;AAEA;EACA;;AAEA;EACE;EACA,qLAEkE;;;AC5BxE;AACE;EACA;EACA;AAEA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAWA;;AATA;EACE;;AAEA;AAAA;EAEE;;AAKJ;EACE;EACA;EACA;EACA;EACA;AAUA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AARA;EACE;EACA;EACA;EACA;EACA;;AAYF;AACE;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EAEA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;IACE;;;AAMR;EACE;EACA;EACA;EACA;EACA;AAEA;AAAA;AAAA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;;ACvGR;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA;AACE;EACA;EACA;AAEA;EACA;EACA;EAEA;EACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAOA;EACE;EACA;EACA;AAEA;AAAA;AAAA;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAMA;AAAA;AAAA;AAAA;AAAA;;AAJA;EACE;;AAQF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;;AAGF;EACE;IACE;IACA;;;AAKN;EACE;;AAGF;EACE;;;AC/ER;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;AACE;EACA;AAEA;EACA;EACA;AAAA;AAAA;AAIA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGA;EACE;;AAMF;EACE;;;AC5CR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAEI;IACE;IACA;IACA;;;AAMR;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AC9EV;EACE;EACA;EACA;EACA;EACA;AAoFA;AAyGA;;AA3LA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAQV;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;AAEA;AAMA;AAwCA;AAqBA;;AAlEA;EACE;EACA;;AAIF;EACE;AAEA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEI;IACE;IACA;;;AAKN;EAEI;IACE;IACA;;;AAOR;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;IACE;;;AAKN;EACE;;AAeJ;EACE;;AAKJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;IACE;;;;AAQZ;EACE;IACE;;EAGF;IACE;;;ACnOJ;EACE;EACA;EACA;EACA,qBACE;EAEF;EACA;AAEA;;AACA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAEI;IACE;IACA;IACA;;;AAMR;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AC7FZ;AAAA;AAAA;AAGA;AACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA","file":"main.css"} {"version":3,"sourceRoot":"","sources":["../../src/sass/base/polices/_lato.scss","../../src/sass/base/polices/_myriad.scss","../../src/sass/abstracts/_variables.scss","../../src/sass/base/_base.scss","../../src/sass/base/_typographie.scss","../../src/sass/base/elements/_boutons.scss","../../src/sass/base/elements/_images.scss","../../src/sass/base/elements/_liens.scss","../../src/sass/base/elements/_listes.scss","../../src/sass/layouts/_en-tete.scss","../../src/sass/layouts/_menu-categories-produits.scss","../../src/sass/layouts/_colonnes-photos.scss","../../src/sass/layouts/_grille-produits.scss","../../src/sass/layouts/_informations-produit.scss","../../src/sass/layouts/_produits-similaires.scss","../../src/sass/layouts/_pied-de-page.scss"],"names":[],"mappings":";AAAA;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,KACE;;AChGJ;EACE;EACA;EACA;EACA;EACA;EACA,KACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA,KACE;;AChBJ;AACE;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;AAEA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;;;AClBF;AAAA;AAAA;AAAA;AAAA;AAKA;EACE;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;AAAA;AAAA;EAGE;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;AAAA;AAIA;EACE;EACA;;;AAGF;AAAA;AAAA;AAGA;AAAA;AAAA;AAAA;EAIE;;;AAGF;AAAA;AAAA;AAGA;EACE;;;ACtDF;EACE;EACA;EACA;;;ACHF;AAAA;AAAA;AAGA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;ACfJ;AAAA;AAAA;AAAA;AAAA;AAKA;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;;;ACbF;AAAA;AAAA;AAAA;AAAA;AAKA;AACE;EACA;EAEA;EACA;EACA;EACA;EACA;EACA,YACE,kHAK4B;EAE9B;AAEA;AAMA;AAAA;AAAA;;AALA;EAEE;;AAMF;AACE;EACA;EACA;AAEA;EACA;EAEA;EACA;AAEA;AAMA;AAKA;;AAVA;EAEE;;AAIF;EACE;;AAIF;EACE;IACE;;;AAMJ;EACE;IACE;;;;AClER;AAAA;AAAA;AAAA;AAAA;AAKA;EACE;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAMA;AACE;EACA;AAEA;EACA;AAEA;EACA;;AAEA;EACE;EACA,qLAEkE;;;AC5BxE;AACE;EACA;EACA;AAEA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAWA;;AATA;EACE;;AAEA;AAAA;EAEE;;AAKJ;EACE;EACA;EACA;EACA;EACA;AAUA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AARA;EACE;EACA;EACA;EACA;EACA;;AAYF;AACE;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EAEA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;IACE;;;AAMR;EACE;EACA;EACA;EACA;EACA;AAEA;AAAA;AAAA;;AAGA;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;;ACvGR;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA;AACE;EACA;EACA;AAEA;EACA;EACA;EAEA;EACA;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAOA;EACE;EACA;EACA;AAEA;AAAA;AAAA;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAMA;AAAA;AAAA;AAAA;AAAA;;AAJA;EACE;;AAQF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;;AAGF;EACE;IACE;IACA;;;AAKN;EACE;;AAGF;EACE;;;AC/ER;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;AACE;EACA;AAEA;EACA;EACA;AAAA;AAAA;AAIA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGA;EACE;;AAMF;EACE;;;AC5CR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAEI;IACE;IACA;IACA;;;AAMR;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AC9EV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA;AACE;EAEA;EAGA;EACA;EAGA;EAEA;EACA;EACA;EACA;EACA;AAuGA;AAyGA;;AA9MA;EACE;EACA;EACA;EACA;EACA;AAEA;AAAA;AAAA;;AAGA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;;AACA;EACE;;AAGF;EACE;;AAMR;EACE;;AAEA;EACE;;AAMN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;AAEA;AAMA;AAwCA;AAqBA;;AAlEA;EACE;EACA;;AAIF;EACE;AAEA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEI;IACE;IACA;;;AAKN;EAEI;IACE;IACA;;;AAOR;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;IACE;;;AAKN;EACE;;AAeJ;EACE;;AAKJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGE;EACE;IACE;;;;AAQZ;EACE;IACE;;EAGF;IACE;;;ACxQJ;EACE;EACA;EACA;EACA,qBACE;EAEF;EACA;AAEA;;AACA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EAEI;IACE;IACA;IACA;;;AAMR;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AC7FZ;AAAA;AAAA;AAGA;AACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA","file":"main.css"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,17 +1,97 @@
{ {
"_Either.js": {
"file": "Either.js",
"name": "Either"
},
"web/app/themes/haiku-atelier-2024/src/scripts/gaffe.ts": {
"file": "gaffe.js",
"name": "gaffe",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/gaffe.ts",
"isEntry": true
},
"web/app/themes/haiku-atelier-2024/src/scripts/lib/api.ts": {
"file": "api.js",
"name": "api",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/lib/api.ts",
"isEntry": true,
"imports": [
"web/app/themes/haiku-atelier-2024/src/scripts/lib/constantes.ts",
"_Either.js"
]
},
"web/app/themes/haiku-atelier-2024/src/scripts/lib/constantes.ts": {
"file": "constantes.js",
"name": "constantes",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/lib/constantes.ts",
"isEntry": true
},
"web/app/themes/haiku-atelier-2024/src/scripts/lib/dom.ts": { "web/app/themes/haiku-atelier-2024/src/scripts/lib/dom.ts": {
"file": "dom.js", "file": "dom.js",
"name": "dom", "name": "dom",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/lib/dom.ts", "src": "web/app/themes/haiku-atelier-2024/src/scripts/lib/dom.ts",
"isEntry": true,
"imports": [
"web/app/themes/haiku-atelier-2024/src/scripts/lib/erreurs.ts",
"_Either.js"
]
},
"web/app/themes/haiku-atelier-2024/src/scripts/lib/erreurs.ts": {
"file": "erreurs.js",
"name": "erreurs",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/lib/erreurs.ts",
"isEntry": true "isEntry": true
}, },
"web/app/themes/haiku-atelier-2024/src/scripts/lib/gestion-panier.ts": {
"file": "gestion-panier.js",
"name": "gestion-panier",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/lib/gestion-panier.ts",
"isEntry": true,
"imports": [
"web/app/themes/haiku-atelier-2024/src/scripts/lib/constantes.ts",
"_Either.js"
]
},
"web/app/themes/haiku-atelier-2024/src/scripts/lib/utils.ts": {
"file": "utils.js",
"name": "utils",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/lib/utils.ts",
"isEntry": true,
"imports": [
"web/app/themes/haiku-atelier-2024/src/scripts/lib/dom.ts",
"web/app/themes/haiku-atelier-2024/src/scripts/lib/erreurs.ts",
"_Either.js"
]
},
"web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts": { "web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts": {
"file": "scripts-page-produit.js", "file": "scripts-page-produit.js",
"name": "scripts-page-produit", "name": "scripts-page-produit",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts", "src": "web/app/themes/haiku-atelier-2024/src/scripts/scripts-page-produit.ts",
"isEntry": true, "isEntry": true,
"imports": [ "imports": [
"web/app/themes/haiku-atelier-2024/src/scripts/lib/constantes.ts",
"web/app/themes/haiku-atelier-2024/src/scripts/lib/erreurs.ts",
"web/app/themes/haiku-atelier-2024/src/scripts/lib/utils.ts",
"web/app/themes/haiku-atelier-2024/src/scripts/types/cart-add-item.ts",
"_Either.js",
"web/app/themes/haiku-atelier-2024/src/scripts/lib/dom.ts" "web/app/themes/haiku-atelier-2024/src/scripts/lib/dom.ts"
] ]
},
"web/app/themes/haiku-atelier-2024/src/scripts/types/cart-add-item.ts": {
"file": "cart-add-item.js",
"name": "cart-add-item",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/types/cart-add-item.ts",
"isEntry": true
},
"web/app/themes/haiku-atelier-2024/src/scripts/types/cart.ts": {
"file": "cart.js",
"name": "cart",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/types/cart.ts",
"isEntry": true
},
"web/app/themes/haiku-atelier-2024/src/scripts/vite.env.d.ts": {
"file": "vite.env.d.js",
"name": "vite.env.d",
"src": "web/app/themes/haiku-atelier-2024/src/scripts/vite.env.d.ts",
"isEntry": true
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
import{d as S}from"./constantes.js";import{r as F}from"./Either.js";const H="application/json",w="Content-Type",_=Symbol(),P=Symbol();function O(t={}){var e;return(e=Object.entries(t).find(([o])=>o.toLowerCase()===w.toLowerCase()))===null||e===void 0?void 0:e[1]}function R(t){return/^application\/.*json.*/.test(t)}const f=function(t,e,o=!1){return Object.entries(e).reduce((n,[i,l])=>{const h=t[i];return Array.isArray(h)&&Array.isArray(l)?n[i]=o?[...h,...l]:l:typeof h=="object"&&typeof l=="object"?n[i]=f(h,l,o):n[i]=l,n},{...t})},y={options:{},errorType:"text",polyfills:{},polyfill(t,e=!0,o=!1,...n){const i=this.polyfills[t]||(typeof self<"u"?self[t]:null)||(typeof global<"u"?global[t]:null);if(e&&!i)throw new Error(t+" is not defined");return o&&i?new i(...n):i}};function L(t,e=!1){y.options=e?t:f(y.options,t)}function N(t,e=!1){y.polyfills=e?t:f(y.polyfills,t)}function I(t){y.errorType=t}const J=t=>e=>t.reduceRight((o,n)=>n(o),e)||e;class x extends Error{}const U=t=>{const e=Object.create(null);t=t._addons.reduce((r,s)=>s.beforeRequest&&s.beforeRequest(r,t._options,e)||r,t);const{_url:o,_options:n,_config:i,_catchers:l,_resolvers:h,_middlewares:g,_addons:m}=t,p=new Map(l),D=f(i.options,n);let T=o;const j=J(g)((r,s)=>(T=r,i.polyfill("fetch")(r,s)))(o,D),b=new Error,v=j.catch(r=>{throw{[_]:r}}).then(r=>{if(!r.ok){const s=new x;if(s.cause=b,s.stack=s.stack+`
CAUSE: `+b.stack,s.response=r,s.url=T,r.type==="opaque")throw s;return r.text().then(a=>{var c;if(s.message=a,i.errorType==="json"||((c=r.headers.get("Content-Type"))===null||c===void 0?void 0:c.split(";")[0])==="application/json")try{s.json=JSON.parse(a)}catch{}throw s.text=a,s.status=r.status,s})}return r}),A=r=>r.catch(s=>{const a=s.hasOwnProperty(_),c=a?s[_]:s,E=c?.status&&p.get(c.status)||p.get(c?.name)||a&&p.has(_)&&p.get(_);if(E)return E(c,t);const C=p.get(P);if(C)return C(c,t);throw c}),d=r=>s=>A(r?v.then(a=>a&&a[r]()).then(a=>s?s(a):a):v.then(a=>s?s(a):a)),M={_wretchReq:t,_fetchReq:j,_sharedState:e,res:d(null),json:d("json"),blob:d("blob"),formData:d("formData"),arrayBuffer:d("arrayBuffer"),text:d("text"),error(r,s){return p.set(r,s),this},badRequest(r){return this.error(400,r)},unauthorized(r){return this.error(401,r)},forbidden(r){return this.error(403,r)},notFound(r){return this.error(404,r)},timeout(r){return this.error(408,r)},internalError(r){return this.error(500,r)},fetchError(r){return this.error(_,r)}},q=m.reduce((r,s)=>({...r,...typeof s.resolver=="function"?s.resolver(r):s.resolver}),M);return h.reduce((r,s)=>s(r,t),q)},k={_url:"",_options:{},_config:y,_catchers:new Map,_resolvers:[],_deferred:[],_middlewares:[],_addons:[],addon(t){return{...this,_addons:[...this._addons,t],...t.wretch}},errorType(t){return{...this,_config:{...this._config,errorType:t}}},polyfills(t,e=!1){return{...this,_config:{...this._config,polyfills:e?t:f(this._config.polyfills,t)}}},url(t,e=!1){if(e)return{...this,_url:t};const o=this._url.split("?");return{...this,_url:o.length>1?o[0]+t+"?"+o[1]:this._url+t}},options(t,e=!1){return{...this,_options:e?t:f(this._options,t)}},headers(t){const e=t?Array.isArray(t)?Object.fromEntries(t):"entries"in t?Object.fromEntries(t.entries()):t:{};return{...this,_options:f(this._options,{headers:e})}},accept(t){return this.headers({Accept:t})},content(t){return this.headers({[w]:t})},auth(t){return this.headers({Authorization:t})},catcher(t,e){const o=new Map(this._catchers);return o.set(t,e),{...this,_catchers:o}},catcherFallback(t){return this.catcher(P,t)},resolve(t,e=!1){return{...this,_resolvers:e?[t]:[...this._resolvers,t]}},defer(t,e=!1){return{...this,_deferred:e?[t]:[...this._deferred,t]}},middlewares(t,e=!1){return{...this,_middlewares:e?t:[...this._middlewares,...t]}},fetch(t=this._options.method,e="",o=null){let n=this.url(e).options({method:t});const i=O(n._options.headers),l=this._config.polyfill("FormData",!1),h=typeof o=="object"&&!(l&&o instanceof l)&&(!n._options.headers||!i||R(i));return n=o?h?n.json(o,i):n.body(o):n,U(n._deferred.reduce((g,m)=>m(g,g._url,g._options),n))},get(t=""){return this.fetch("GET",t)},delete(t=""){return this.fetch("DELETE",t)},put(t,e=""){return this.fetch("PUT",e,t)},post(t,e=""){return this.fetch("POST",e,t)},patch(t,e=""){return this.fetch("PATCH",e,t)},head(t=""){return this.fetch("HEAD",t)},opts(t=""){return this.fetch("OPTIONS",t)},body(t){return{...this,_options:{...this._options,body:t}}},json(t,e){const o=O(this._options.headers);return this.content(e||R(o)&&o||H).body(JSON.stringify(t))}};function u(t="",e={}){return{...k,_url:t,_options:e}}u.default=u;u.options=L;u.errorType=I;u.polyfills=N;u.WretchError=x;fetch(S,{credentials:"same-origin",headers:{"Content-Type":"application/json"},method:"GET",mode:"same-origin",signal:AbortSignal.timeout(5e3)}).then(F);u(S).content("application/json").options({credentials:"same-origin",mode:"same-origin",signal:AbortSignal.timeout(5e3)}).get().json().catch();
//# sourceMappingURL=api.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
var y;function g(e){return{lang:e?.lang??y?.lang,message:e?.message,abortEarly:e?.abortEarly??y?.abortEarly,abortPipeEarly:e?.abortPipeEarly??y?.abortPipeEarly}}var E;function d(e){return E?.get(e)}var k;function _(e){return k?.get(e)}var A;function I(e,s){return A?.get(e)?.get(s)}function S(e){const s=typeof e;return s==="string"?`"${e}"`:s==="number"||s==="bigint"||s==="boolean"?`${e}`:s==="object"||s==="function"?(e&&Object.getPrototypeOf(e)?.constructor?.name)??"null":s}function c(e,s,r,n,t){const i=r.value,o=e.expects??null,u=S(i),l={kind:e.kind,type:e.type,input:i,expected:o,received:u,message:`Invalid ${s}: ${o?`Expected ${o} but r`:"R"}eceived ${u}`,requirement:e.requirement,path:t?.path,issues:t?.issues,lang:n.lang,abortEarly:n.abortEarly,abortPipeEarly:n.abortPipeEarly},a=e.kind==="schema",f=e.message??I(e.reference,l.lang)??(a?_(l.lang):null)??n.message??d(l.lang);f&&(l.message=typeof f=="function"?f(l):f),a&&(r.typed=!1),r.issues?r.issues.push(l):r.issues=[l]}var j=class extends Error{issues;constructor(e){super(e[0].message),this.name="ValiError",this.issues=e}};function C(e,s,r){return typeof e.default=="function"?e.default(s,r):e.default}function b(e,s){return{kind:"schema",type:"array",reference:b,expects:"Array",async:!1,item:e,message:s,_run(r,n){const t=r.value;if(Array.isArray(t)){r.typed=!0,r.value=[];for(let i=0;i<t.length;i++){const o=t[i],u=this.item._run({typed:!1,value:o},n);if(u.issues){const l={type:"array",origin:"value",input:t,key:i,value:o};for(const a of u.issues)a.path?a.path.unshift(l):a.path=[l],r.issues?.push(a);if(r.issues||(r.issues=u.issues),n.abortEarly){r.typed=!1;break}}u.typed||(r.typed=!1),r.value.push(u.value)}}else c(this,"type",r,n);return r}}}function v(e){return{kind:"schema",type:"number",reference:v,expects:"number",async:!1,message:e,_run(s,r){return typeof s.value=="number"&&!isNaN(s.value)?s.typed=!0:c(this,"type",s,r),s}}}function m(e,s){return{kind:"schema",type:"object",reference:m,expects:"Object",async:!1,entries:e,message:s,_run(r,n){const t=r.value;if(t&&typeof t=="object"){r.typed=!0,r.value={};for(const i in this.entries){const o=t[i],u=this.entries[i]._run({typed:!1,value:o},n);if(u.issues){const l={type:"object",origin:"value",input:t,key:i,value:o};for(const a of u.issues)a.path?a.path.unshift(l):a.path=[l],r.issues?.push(a);if(r.issues||(r.issues=u.issues),n.abortEarly){r.typed=!1;break}}u.typed||(r.typed=!1),(u.value!==void 0||i in t)&&(r.value[i]=u.value)}}else c(this,"type",r,n);return r}}}function p(e,...s){const r={kind:"schema",type:"optional",reference:p,expects:`(${e.expects} | undefined)`,async:!1,wrapped:e,_run(n,t){return n.value===void 0&&("default"in this&&(n.value=C(this,n,t)),n.value===void 0)?(n.typed=!0,n):this.wrapped._run(n,t)}};return 0 in s&&(r.default=s[0]),r}function h(e){return{kind:"schema",type:"string",reference:h,expects:"string",async:!1,message:e,_run(s,r){return typeof s.value=="string"?s.typed=!0:c(this,"type",s,r),s}}}function $(e,s,r){const n=e._run({typed:!1,value:s},g(r));if(n.issues)throw new j(n.issues);return n.value}const P=m({attribute:h(),value:h()}),x=m({id:p(v()),quantity:p(v()),variation:p(b(P))}),W=e=>$(x,e);export{W};
//# sourceMappingURL=cart-add-item.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
//# sourceMappingURL=cart.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"cart.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}

View file

@ -0,0 +1,2 @@
const t="aria-selected",T="hidden",a=".compte-panier a[rel='cart']",s="#selecteur-variation",_="#bouton-ajout-panier",E="wp-json",c=`/${E}/wc/store/cart`,o=`/${E}/wc/store/cart/add-item`;export{t as A,o as R,a as S,s as a,_ as b,T as c,c as d};
//# sourceMappingURL=constantes.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"constantes.js","sources":["../../src/scripts/lib/constantes.ts"],"sourcesContent":["export const ATTRIBUT_ARIA_SELECTED = \"aria-selected\";\nexport const ATTRIBUT_ARIA_HIDDEN = \"aria-hidden\";\nexport const ATTRIBUT_HIDDEN = \"hidden\";\n\nexport const SELECTEUR_BOUTON_PANIER = \".compte-panier a[rel='cart']\";\nexport const SELECTEUR_SELECTEUR_QUANTITE = \"#selecteur-variation\";\nexport const SELECTEUR_BOUTON_AJOUT_PANIER = \"#bouton-ajout-panier\";\n\nexport const CHEMIN_API_REST = \"wp-json\";\nexport const ROUTE_API_PANIER = `/${CHEMIN_API_REST}/wc/store/cart`;\nexport const ROUTE_API_AJOUTE_ARTICLE_PANIER = `/${CHEMIN_API_REST}/wc/store/cart/add-item`;\n"],"names":["ATTRIBUT_ARIA_SELECTED","ATTRIBUT_HIDDEN","SELECTEUR_BOUTON_PANIER","SELECTEUR_SELECTEUR_QUANTITE","SELECTEUR_BOUTON_AJOUT_PANIER","CHEMIN_API_REST","ROUTE_API_PANIER","ROUTE_API_AJOUTE_ARTICLE_PANIER"],"mappings":"AAAO,MAAMA,EAAyB,gBAEzBC,EAAkB,SAElBC,EAA0B,+BAC1BC,EAA+B,uBAC/BC,EAAgC,uBAEhCC,EAAkB,UAClBC,EAAmB,IAAID,CAAe,iBACtCE,EAAkC,IAAIF,CAAe"}

View file

@ -1,2 +1,2 @@
const l=t=>r=>{const e=Array.from(t.querySelectorAll(r));if(!e||e.length===0)throw new Error(`La requête "${r}" n'a débouché sur aucun Élément.`);return e};export{l as s}; import{c as u,E as c,a as h}from"./erreurs.js";import{E as p,r as y,l as E}from"./Either.js";var x={done:!1,hasNext:!1};function S(e,...r){let t=e,l=r.map(i=>"lazy"in i?A(i):void 0),n=0;for(;n<r.length;){if(l[n]===void 0||!d(t)){let a=r[n];t=a(t),n+=1;continue}let i=[];for(let a=n;a<r.length;a++){let o=l[a];if(o===void 0||(i.push(o),o.isSingle))break}let s=[];for(let a of t)if(g(a,s,i))break;let{isSingle:f}=i.at(-1);t=f?s[0]:s,n+=i.length}return t}function g(e,r,t){if(t.length===0)return r.push(e),!1;let l=e,n=x,i=!1;for(let[s,f]of t.entries()){let{index:a,items:o}=f;if(o.push(l),n=f(l,a,o),f.index+=1,n.hasNext){if(n.hasMany??!1){for(let m of n.next)if(g(m,r,t.slice(s+1)))return!0;return i}l=n.next}if(!n.hasNext)break;n.done&&(i=!0)}return n.hasNext&&r.push(l),i}function A(e){let{lazy:r,lazyArgs:t}=e,l=r(...t);return Object.assign(l,{isSingle:r.single??!1,index:0,items:[]})}function d(e){return typeof e=="string"||typeof e=="object"&&e!==null&&Symbol.iterator in e}function N(e){return e!==null}function b(e){return e===void 0?!0:typeof e=="string"||Array.isArray(e)?e.length===0:Object.keys(e).length===0}const _=e=>r=>p.encase(()=>e.querySelector(r)).mapLeft(t=>u(c(r))).chain(t=>N(t)?y(t):E(u(h(r)))),k=e=>r=>p.encase(()=>S(e.querySelectorAll(r),Array.from)).mapLeft(t=>u(c(r))).chain(t=>b(t)?E(u(h(r))):y(t));export{k as a,_ as r};
//# sourceMappingURL=dom.js.map //# sourceMappingURL=dom.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
const r=e=>`Le selecteur "${e}" est invalide`,E=e=>`La requête "${e}" n'a retourné aucun Élément.`,t=e=>new SyntaxError(e),n=e=>{throw e};export{r as E,E as a,t as c,n as l};
//# sourceMappingURL=erreurs.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"erreurs.js","sources":["../../src/scripts/lib/erreurs.ts"],"sourcesContent":["/**\n * Fonctions pour la création d'Erreurs.\n */\n\n/* Messages d'erreur */\nexport const ERREUR_SYNTAXE_INVALIDE: (selecteur: string) => string = (selecteur) =>\n `Le selecteur \"${selecteur}\" est invalide`;\nexport const ERREUR_SELECTEUR_INEXISTANT: (selecteur: string) => string = (selecteur) =>\n `La requête \"${selecteur}\" n'a retourné aucun Élément.`;\n\n/* Création d'erreurs */\nexport const creeSyntaxError: (message: string) => SyntaxError = (message) => new SyntaxError(message);\n\nexport const leveErreur: (erreur: Error) => never = (erreur) => {\n throw erreur;\n};\n"],"names":["ERREUR_SYNTAXE_INVALIDE","selecteur","ERREUR_SELECTEUR_INEXISTANT","creeSyntaxError","message","leveErreur","erreur"],"mappings":"AAKO,MAAMA,EAA0DC,GACrE,iBAAiBA,CAAS,iBACfC,EAA8DD,GACzE,eAAeA,CAAS,gCAGbE,EAAqDC,GAAY,IAAI,YAAYA,CAAO,EAExFC,EAAwCC,GAAW,CACxD,MAAAA,CACR"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
import{d as g}from"./constantes.js";import{E as h,r as o,l as f,M,n as u,j as c}from"./Either.js";var l;const y={liftEither(i){if(i.isRight())return Promise.resolve(i.extract());throw i.extract()},fromPromise(i){return i.then(y.liftEither)},throwE(i){throw i}};class m{constructor(t){this.runPromise=t,this[l]="EitherAsync",this["fantasy-land/chain"]=this.chain,this["fantasy-land/alt"]=this.alt,this.then=(r,e)=>this.run().then(r,e)}leftOrDefault(t){return this.run().then(r=>r.leftOrDefault(t))}orDefault(t){return this.run().then(r=>r.orDefault(t))}join(){return n(async t=>{const r=await this;if(r.isRight()){const e=await r.extract();return t.liftEither(e)}return t.liftEither(r)})}ap(t){return n(async r=>{const e=await t;if(e.isRight()){const a=await this.run();return a.isRight()?e.extract()(a.extract()):r.liftEither(a)}return r.liftEither(e)})}alt(t){return n(async r=>{const e=await this.run();if(e.isRight())return e.extract();{const a=await t;return r.liftEither(a)}})}extend(t){return n(async r=>{const e=await this.run();if(e.isRight()){const a=n.liftEither(e);return r.liftEither(o(t(a)))}return r.liftEither(e)})}async run(){try{return o(await this.runPromise(y))}catch(t){return f(t)}}bimap(t,r){return n(async e=>{const a=await this.run();try{return await e.liftEither(a.bimap(t,r))}catch(P){throw await P}})}map(t){return n(r=>this.runPromise(r).then(t))}mapLeft(t){return n(async r=>{try{return await this.runPromise(r)}catch(e){throw await t(e)}})}chain(t){return n(async r=>{const e=await this.runPromise(r);return r.fromPromise(t(e))})}chainLeft(t){return n(async r=>{try{return await this.runPromise(r)}catch(e){return r.fromPromise(t(e))}})}toMaybeAsync(){return s(async({liftMaybe:t})=>{const r=await this.run();return t(r.toMaybe())})}swap(){return n(async t=>{const r=await this.run();return r.isRight()&&t.throwE(r.extract()),t.liftEither(o(r.extract()))})}ifLeft(t){return n(async r=>{const e=await this.run();return e.ifLeft(t),r.liftEither(e)})}ifRight(t){return n(async r=>{const e=await this.run();return e.ifRight(t),r.liftEither(e)})}void(){return this.map(t=>{})}caseOf(t){return this.run().then(r=>r.caseOf(t))}finally(t){return n(({fromPromise:r})=>r(this.run().finally(t)))}}l=Symbol.toStringTag;const n=Object.assign(i=>new m(i),{fromPromise:i=>n(({fromPromise:t})=>t(i())),liftEither:i=>n(({liftEither:t})=>t(i)),lefts:i=>Promise.all(i.map(t=>t.run())).then(h.lefts),rights:i=>Promise.all(i.map(t=>t.run())).then(h.rights),sequence:i=>n(async t=>{let r=[];for await(const e of i){if(e.isLeft())return t.liftEither(e);r.push(e.extract())}return t.liftEither(o(r))}),all:i=>n.fromPromise(async()=>Promise.all(i).then(h.sequence))});m.prototype.constructor=n;var w;const b={liftMaybe(i){if(i.isJust())return Promise.resolve(i.extract());throw u},fromPromise(i){return i.then(b.liftMaybe)}};class E{constructor(t){this.runPromise=t,this[w]="MaybeAsync",this["fantasy-land/chain"]=this.chain,this["fantasy-land/filter"]=this.filter,this["fantasy-land/alt"]=this.alt}orDefault(t){return this.run().then(r=>r.orDefault(t))}join(){return s(async t=>{const r=await this.run();if(r.isJust()){const e=await r.extract();return t.liftMaybe(e)}return t.liftMaybe(u)})}ap(t){return s(async r=>{const e=await t;if(e.isJust()){const a=await this.run();return a.isJust()?e.extract()(a.extract()):r.liftMaybe(u)}return r.liftMaybe(u)})}alt(t){return s(async r=>{const e=await this.run();if(e.isJust())return e.extract();{const a=await t;return r.liftMaybe(a)}})}extend(t){return s(async r=>{const e=await this.run();if(e.isJust()){const a=s.liftMaybe(e);return r.liftMaybe(c(t(a)))}return r.liftMaybe(u)})}filter(t){return s(async r=>{const e=await this.run();return r.liftMaybe(e.filter(t))})}async run(){try{return c(await this.runPromise(b))}catch{return u}}map(t){return s(r=>this.runPromise(r).then(t))}chain(t){return s(async r=>{const e=await this.runPromise(r);return r.fromPromise(t(e))})}toEitherAsync(t){return n(async({liftEither:r})=>{const e=await this.run();return r(e.toEither(t))})}ifJust(t){return s(async r=>{const e=await this.run();return e.ifJust(t),r.liftMaybe(e)})}ifNothing(t){return s(async r=>{const e=await this.run();return e.ifNothing(t),r.liftMaybe(e)})}void(){return this.map(t=>{})}caseOf(t){return this.run().then(r=>r.caseOf(t))}finally(t){return s(({fromPromise:r})=>r(this.run().finally(t)))}then(t,r){return this.run().then(t,r)}}w=Symbol.toStringTag;const s=Object.assign(i=>new E(i),{catMaybes:i=>Promise.all(i).then(M.catMaybes),fromPromise:i=>s(({fromPromise:t})=>t(i())),liftMaybe:i=>s(({liftMaybe:t})=>t(i))});E.prototype.constructor=s;const p=_etats,d=()=>fetch(g,{credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json","X-WC-Store-API-Nonce":p.nonce},method:"GET",mode:"same-origin",signal:AbortSignal.timeout(5e3)}).then(o).catch(f);n.fromPromise(d).map(i=>i.json()).ifLeft(i=>console.error(i));
//# sourceMappingURL=gestion-panier.js.map

File diff suppressed because one or more lines are too long

View file

@ -1,2 +1,2 @@
import{s as a}from"./dom.js";const c=o=>{o.forEach(t=>{t[0].setAttribute("aria-selected","false"),t[1].setAttribute("hidden","true")})},d=()=>{const o=a(document)("a[role='tab']"),t=a(document)("section[role='tabpanel']"),r=new Map;o.forEach((e,s)=>{const n=e.getAttribute("id"),i=t[s];if(!n)throw new Error("Le lien ne dispose pas d'ID !");if(!i)throw new Error("Le lien ne dispose pas de section correspondante !");r.set(n,[e,i])}),Array.from(r.values()).forEach(e=>{e[0].addEventListener("click",s=>{s.preventDefault();const n=e[0].getAttribute("aria-selected")==="true";c(Array.from(r.values())),!n&&(e[0].setAttribute("aria-selected","true"),e[1].removeAttribute("hidden"))})}),console.debug(r)};document.addEventListener("DOMContentLoaded",()=>{d()}); import{S as p,a as T,b as g,A as E,c as m,R as L}from"./constantes.js";import{l as a}from"./erreurs.js";import{r as d,a as f}from"./utils.js";import{W as O}from"./cart-add-item.js";import{E as h}from"./Either.js";import"./dom.js";const c=n=>n,A=_etats,R=n=>{n.forEach(t=>{t[0].setAttribute(E,"false"),t[1].setAttribute(m,"true")})},I=d(p),_=()=>{const n=f("a[role='tab']").caseOf({Left:a,Right:c}),t=f("section[role='tabpanel']").caseOf({Left:a,Right:c}),o=d(T).caseOf({Left:a,Right:c}),r=d(g).caseOf({Left:a,Right:c}),s=new Map;n.forEach((e,u)=>{const i=e.getAttribute("id"),l=t[u];if(!i)throw new Error("Le lien ne dispose pas d'ID !");if(!l)throw new Error("Le lien ne dispose pas de section correspondante !");s.set(i,[e,l])}),Array.from(s.values()).forEach(e=>{e[0].addEventListener("click",u=>{u.preventDefault();const i=e[0].getAttribute(E)==="true";R(Array.from(s.values())),!i&&(e[0].setAttribute(E,"true"),e[1].removeAttribute(m))})}),o.addEventListener("change",e=>{console.debug(o.value,e)}),r.addEventListener("click",e=>{e.preventDefault(),b()}),console.debug(s)},b=()=>{const n={quantity:1,id:A.idProduit};h.encase(()=>O(n)).ifLeft(t=>console.error(t)).map(t=>{fetch(L,{body:JSON.stringify(t),credentials:"same-origin",headers:{Accept:"application/json","Content-Type":"application/json","X-WC-Store-API-Nonce":A.nonce},method:"POST",mode:"same-origin",signal:AbortSignal.timeout(5e3)}).then(o=>o.json()).then(o=>{I.caseOf({Left:r=>console.error("Le bouton du Panier n'existe pas !",r),Right:r=>{console.debug(r),r.textContent=`cart ${o.items.length}`}})}).catch(o=>console.error(o))})};document.addEventListener("DOMContentLoaded",()=>{_()});
//# sourceMappingURL=scripts-page-produit.js.map //# sourceMappingURL=scripts-page-produit.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
import{r as e,a as r}from"./dom.js";import"./erreurs.js";import"./Either.js";const o=r(document),n=e(document);export{o as a,n as r};
//# sourceMappingURL=utils.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"utils.js","sources":["../../src/scripts/lib/utils.ts"],"sourcesContent":["import type { Either } from \"purify-ts\";\nimport { recupereElementAvecSelecteur, recupereElementsAvecSelecteur } from \"./dom\";\n\nexport const recupereElementsDocument: (selecteur: string) => Either<SyntaxError, Element[]> =\n recupereElementsAvecSelecteur(document);\n\nexport const recupereElementDocument: <E extends Element = Element>(selecteur: string) => Either<SyntaxError, E> =\n recupereElementAvecSelecteur(document);\n"],"names":["recupereElementAvecSelecteur","recupereElementsAvecSelecteur","recupereElementsDocument","recupereElementDocument"],"mappings":"AAGa,OAAA,KAAAA,EAAA,KAAAC,MAAA,WAAA,MAAA,eAAA,MAAA,cAAA,MAAAC,EACXD,EAA8B,QAAQ,EAE3BE,EACXH,EAA6B,QAAQ"}

View file

@ -0,0 +1 @@
//# sourceMappingURL=vite.env.d.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"vite.env.d.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}

View file

@ -19,23 +19,21 @@ $produits_collection = array_map(
"recupere_informations_produit_page_produit", "recupere_informations_produit_page_produit",
recupere_produits_meme_collection($produit["collection"])($produit["id"]), recupere_produits_meme_collection($produit["collection"])($produit["id"]),
); );
$nonce = wp_create_nonce("wc_store_api");
$contexte["produit"] = $produit; $contexte["produit"] = $produit;
$contexte["produits_collection"] = $produits_collection; $contexte["produits_collection"] = $produits_collection;
$contexte["nonce"] = $nonce;
/* echo "<pre>";
print_r($produit);
echo "</pre>"; */
/** /**
* Charge les Scripts nécessaires pour la page Produit. * Charge les Scripts nécessaires pour la page Produit.
*/ */
function charge_scripts_page_produit(): void { function charge_scripts_page_produit(): void {
wp_enqueue_script_module( wp_enqueue_script_module(
$id = "haiku-atelier-2024-scripts-page-produit", id: "haiku-atelier-2024-scripts-page-produit",
$src = get_template_directory_uri() . "/assets/js/scripts-page-produit.js", src: get_template_directory_uri() . "/assets/js/scripts-page-produit.js",
$deps = [], deps: [],
$ver = filemtime(get_template_directory() . "/assets/js/scripts-page-produit.js"), version: filemtime(get_template_directory() . "/assets/js/scripts-page-produit.js"),
); );
} }
add_action("wp_enqueue_scripts", "charge_scripts_page_produit"); add_action("wp_enqueue_scripts", "charge_scripts_page_produit");

View file

@ -1,25 +1,46 @@
// Styles pour la boîte flottante des Informations sur le Produit // Styles pour la boîte flottante des Informations sur le Produit
/**
* Boîte flottante avec les informations Produit, le sélecteur de variation et de quantité pour le
* Panier.
*
* 1. Flotte toujours en bas de la page et s'arrête avant les Produits de la même collection.
* 2. Conteneur existant pour que le flottement sticky fonctionne ; n'a pas de dimensions.
*/
.informations-produit { .informations-produit {
position: sticky; /* Variables */
bottom: 1rem; // Couleurs
--boite-couleur-fond: rgb(255 255 255 / 90%);
// Dimensions et positions
--boite-position-basse: 1rem;
--boite-longueur: 70ch;
// Marges
--section-marges-internes: 1rem;
position: sticky; /* 1 */
bottom: var(--boite-position-basse); /* 1 */
overflow: visible; overflow: visible;
width: 0; width: 0; /* 2 */
height: 0; height: 0; /* 2 */
&__conteneur { &__conteneur {
transform: translate(calc(50vw - 35ch), -100%); transform: translate(calc(50vw - var(--boite-longueur) / 2), -100%);
display: grid; display: grid;
width: 70ch; width: var(--boite-longueur);
max-width: 70ch; max-width: var(--boite-longueur);
border: 1px solid var(--couleur-noir); border: 1px solid var(--couleur-noir);
/**
* Définis les apparence et comportement de toutes les sections de la boîte.
*/
> section { > section {
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
height: 100%; height: 100%;
padding: 1rem; padding: var(--section-marges-internes);
background: rgb(255 255 255 / 90%); background: var(--boite-couleur-fond);
+ section { + section {
border-top: 1px solid var(--couleur-noir); border-top: 1px solid var(--couleur-noir);
@ -30,12 +51,13 @@
.onglets-details-produit { .onglets-details-produit {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-rows: 1fr min-content; grid-template-rows: auto min-content;
padding: initial; padding: initial;
> section { > section {
overflow-y: auto;
grid-column: span 3; grid-column: span 3;
padding: 1rem; padding: var(--section-marges-internes);
font-weight: 350; font-weight: 350;
&[hidden] { &[hidden] {
@ -64,31 +86,46 @@
li { li {
width: 100%; width: 100%;
&:first-of-type a {
border-right-color: transparent;
border-left-color: transparent;
}
&:last-of-type a {
border-right-color: transparent;
border-left-color: transparent;
}
a { a {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: var(--espace-xs) 0; padding: var(--espace-xs) 0;
border-top: 1px solid var(--couleur-noir); border-top: 1px solid transparent;
border-right: 1px solid var(--couleur-noir); border-right: 1px solid var(--couleur-noir);
border-left: 1px solid var(--couleur-noir); border-left: 1px solid var(--couleur-noir);
&:first-of-type { /* Quand l'onglet est sélectionné */
border-left: initial;
}
&:last-of-type {
border-left: initial;
}
&[aria-selected="true"] { &[aria-selected="true"] {
border-top-color: transparent; border-top-color: transparent;
} }
&:focus, &:focus-visible {
outline-color: transparent;
} }
} }
} }
} }
&:has(section:not([hidden])) {
grid-template-rows: 12lh min-content;
> ul li a:not([aria-selected="true"]) {
border-top-color: var(--couleur-noir);
}
}
}
/* Nom du Produit, sélecteur de variation et prix du Produit */ /* Nom du Produit, sélecteur de variation et prix du Produit */
.selecteur-produit { .selecteur-produit {
display: flex; display: flex;

View file

@ -0,0 +1,67 @@
/**
* Initialise GlitchTip.
*/
import {
breadcrumbsIntegration,
BrowserClient,
createTransport,
dedupeIntegration,
defaultStackParser,
functionToStringIntegration,
getCurrentScope,
globalHandlersIntegration,
httpContextIntegration,
linkedErrorsIntegration,
} from "@sentry/browser";
import type { Transport } from "@sentry/types";
const makeFetchTransport = (options): Transport => {
const makeRequest = async (request) => {
const requestOptions: RequestInit = {
body: request.body,
headers: options.headers,
method: "POST",
mode: "no-cors",
referrerPolicy: "no-referrer",
...options.fetchOptions,
};
const response = await fetch(options.url, requestOptions);
return ({
headers: {
"x-sentry-rate-limits": response.headers.get("X-Sentry-Rate-Limits"),
"retry-after": response.headers.get("Retry-After"),
},
statusCode: response.status,
});
};
return createTransport(options, makeRequest);
};
/* Créé la configuration Sentry */
const client = new BrowserClient({
dsn: import.meta.env.VITE_GLITCHTIP_NSD,
environment: "local",
integrations: [
breadcrumbsIntegration(),
dedupeIntegration(),
functionToStringIntegration(),
globalHandlersIntegration(),
httpContextIntegration(),
linkedErrorsIntegration(),
],
stackParser: defaultStackParser,
tracesSampleRate: 0.01,
transport: makeFetchTransport,
});
/* Initialise la configuration */
getCurrentScope().setClient(client);
client.init();
/* TODO: Retirer une fois les tests réalisés */
setTimeout(() => {
throw new Error("Test GlitchTip !");
}, 3000);

View file

@ -0,0 +1,65 @@
import { Right } from "purify-ts";
import wretch from "wretch";
import { ROUTE_API_PANIER } from "./constantes";
export const get = async (
url: string,
input: Record<string, string>,
) => {
return fetch(
`${url}?${new URLSearchParams(input).toString()}`,
);
};
export const post = async (
url: string,
input: Record<string, string>,
) => {
return fetch(url, {
method: "POST",
body: JSON.stringify(input),
});
};
type CreateAPIMethod = <
Input extends Record<string, string>,
Output,
>(opts: {
url: string;
method: "GET" | "POST";
}) => (input: Input) => Promise<Output>;
const createAPIMethod: CreateAPIMethod = (opts) => (input) => {
const method = opts.method === "GET" ? get : post;
return (
method(opts.url, input)
// Imagine error handling here...
.then((res) => res.json())
);
};
const requeteRecuperationPanierUtilisateur = fetch(
ROUTE_API_PANIER,
{
credentials: "same-origin",
headers: { "Content-Type": "application/json" },
method: "GET",
mode: "same-origin",
signal: AbortSignal.timeout(5000),
},
).then(Right);
const recuperePanierUtilisateur = wretch(ROUTE_API_PANIER)
.content("application/json")
.options({
credentials: "same-origin",
mode: "same-origin",
signal: AbortSignal.timeout(5000),
})
.get()
.json()
.catch();
/**
* Récupération du Panier. Pas de paramètres, juste une requête GET.
*/

View file

@ -0,0 +1,11 @@
export const ATTRIBUT_ARIA_SELECTED = "aria-selected";
export const ATTRIBUT_ARIA_HIDDEN = "aria-hidden";
export const ATTRIBUT_HIDDEN = "hidden";
export const SELECTEUR_BOUTON_PANIER = ".compte-panier a[rel='cart']";
export const SELECTEUR_SELECTEUR_QUANTITE = "#selecteur-variation";
export const SELECTEUR_BOUTON_AJOUT_PANIER = "#bouton-ajout-panier";
export const CHEMIN_API_REST = "wp-json";
export const ROUTE_API_PANIER = `/${CHEMIN_API_REST}/wc/store/cart`;
export const ROUTE_API_AJOUTE_ARTICLE_PANIER = `/${CHEMIN_API_REST}/wc/store/cart/add-item`;

View file

@ -1,16 +1,69 @@
export const safeQuerySelector = (parent: Document | Element) => (query: string) => { import { Either, Left, Right } from "purify-ts";
const element: Element | null = parent.querySelector(query); import { isEmpty, isNonNull, isNullish, pipe } from "remeda";
if (!element) throw new Error(`La requête "${query}" n'a débouché sur aucun Élément.`); import { creeSyntaxError, ERREUR_SELECTEUR_INEXISTANT, ERREUR_SYNTAXE_INVALIDE } from "./erreurs";
/** Type union des parents possible pour un `querySelector`. */
type ElementParent = Document | Element;
/**
* @param parent L'Élément parent dans lequel l'Élément souhaité sera recherché.
* @param selecteur Le sélecteur de l'Élément recherché.
*
* @throws Une Erreur si aucun Élément n'a é trouvé.
* @returns Un Élément.
*/
export const safeQuerySelector: (parent: ElementParent) => (selecteur: string) => Element = (parent) => (selecteur) => {
const element: Element | null = parent.querySelector(selecteur);
if (!element) throw new DOMException(ERREUR_SELECTEUR_INEXISTANT(selecteur));
return element; return element;
}; };
export const safeQuerySelectorAll = (parent: Document | Element) => (query: string) => { /**
const elements: Element[] = Array.from(parent.querySelectorAll(query)); * @param parent L'Élément parent dans lequel l'Élément souhaité sera recherché.
if (!elements || elements.length === 0) throw new Error(`La requête "${query}" n'a débouché sur aucun Élément.`); * @param selecteur Le sélecteur de l'Élément recherché.
*
* @returns Un tableau pouvant être vide d'Éléments.
*/
export const safeQuerySelectorAll: (parent: ElementParent) => (selecteur: string) => Element[] =
(parent) => (selecteur) => {
const elements: Element[] = Array.from(parent.querySelectorAll(selecteur));
if (isEmpty(elements)) {
throw new DOMException(ERREUR_SELECTEUR_INEXISTANT(selecteur));
}
return elements; return elements;
}; };
export const estDansLaVue = (element: Element) => { // export const recupereElementAvecSelecteur: (
// parent: ElementParent,
// ) => <T extends Element = Element>(selecteur: string) => Either<SyntaxError, T> = (parent) => (selecteur) =>
export const recupereElementAvecSelecteur =
(parent: ElementParent) => <E extends Element = Element>(selecteur: string): Either<SyntaxError, E> =>
Either
// Retourne une SyntaxError dans un Left si le sélecteur est invalide
.encase(() => parent.querySelector<E>(selecteur))
// 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) => isNonNull(e) ? Right(e) : Left(creeSyntaxError(ERREUR_SELECTEUR_INEXISTANT(selecteur))));
export const recupereElementsAvecSelecteur: (
parent: ElementParent,
) => (selecteur: string) => Either<SyntaxError, Element[]> = (parent) => (selecteur) =>
Either
// Retourne une SyntaxError dans un Left si le sélecteur est invalide
.encase(() => pipe(parent.querySelectorAll(selecteur), Array.from<Element>))
// Transforme le Left en une erreur plus sympathique
.mapLeft(_ => creeSyntaxError(ERREUR_SYNTAXE_INVALIDE(selecteur)))
// Retourne une SyntaxError si le tableau est vide
.chain(e => isEmpty(e) ? Left(creeSyntaxError(ERREUR_SELECTEUR_INEXISTANT(selecteur))) : Right(e));
/**
* @param element
* @returns Un booléen
*/
export const estDansLaVue: (element: Element) => boolean = (element) => {
const rect: DOMRect = element.getBoundingClientRect(); const rect: DOMRect = element.getBoundingClientRect();
return ( return (

View file

@ -0,0 +1,16 @@
/**
* Fonctions pour la création d'Erreurs.
*/
/* Messages d'erreur */
export const ERREUR_SYNTAXE_INVALIDE: (selecteur: string) => string = (selecteur) =>
`Le selecteur "${selecteur}" est invalide`;
export const ERREUR_SELECTEUR_INEXISTANT: (selecteur: string) => string = (selecteur) =>
`La requête "${selecteur}" n'a retourné aucun Élément.`;
/* Création d'erreurs */
export const creeSyntaxError: (message: string) => SyntaxError = (message) => new SyntaxError(message);
export const leveErreur: (erreur: Error) => never = (erreur) => {
throw erreur;
};

View file

@ -0,0 +1,38 @@
/**
* Fonctions liées à la gestion du Panier.
*/
import { Either, EitherAsync, identity, Left, Right } from "purify-ts";
import { ROUTE_API_PANIER } from "./constantes";
/** États utiles pour les scripts. */
type Etats = {
/** Un nonce pour l'authentification de requêtes API */
nonce: string;
};
// @ts-expect-error - États injectés par le modèle PHP
const ETATS: Etats = _etats;
const requeteRecuperePanier = (): Promise<Either<Error, Response>> =>
fetch(
ROUTE_API_PANIER,
{
credentials: "same-origin",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"X-WC-Store-API-Nonce": ETATS.nonce,
},
method: "GET",
mode: "same-origin",
signal: AbortSignal.timeout(5000),
},
)
.then(Right)
.catch(Left);
export const recuperePanier = EitherAsync
.fromPromise(requeteRecuperePanier)
.map(a => a.json())
.ifLeft(e => console.error(e));

View file

@ -0,0 +1,109 @@
import { Either, Left, Right } from "fp-ts/lib/Either";
import { Errors, Type } from "io-ts";
import { reporter } from "io-ts-reporters";
export async function fetchJson<T, O, I>(
url: string,
validator: Type<T, O, I>,
init?: RequestInit,
): Promise<Either<Error, T>> {
try {
const response = await fetch(url, init);
const json: I = await response.json();
const result = validator.decode(json);
return result.fold<Either<Error, T>>(
(errors: Errors) => {
const messages = reporter(result);
return new Left<Error, T>(new Error(messages.join("\n")));
},
(value: T) => {
return new Right<Error, T>(value);
},
);
} catch (err) {
return Promise.resolve(new Left<Error, T>(err));
}
}
// the promise is never rejected
// we handle the error with Either
const readMailFromCli = async (): Promise<
Either<{ errorReason: string }, string>
> => {
const rl = readline.createInterface({ input, output });
return rl
.question("Please enter your email \n")
.then(Right)
.catch((error) => Left({ errorReason: error.message }))
.finally(() => {
rl.close();
});
};
const validateEmail = (
email: string,
): Either<{ errorReason: string }, Email> => {
if (!email.includes("@")) {
return Left({ errorReason: "The email does not contain an @ character" });
}
if (email.length < 3) {
return Left({ errorReason: "The email be at least 3 characters long" });
}
return Right(email);
};
const getSessionIdFromEmail = (
email: Email,
): Promise<Either<{ errorReason: string }, SessionId>> => {
// lets simulate an API call
return Promise.resolve(Right("12345"));
};
const printInformations = ({
email,
sessionId,
}: {
email: Email;
sessionId: SessionId;
}): void => {
console.log(`Email: ${email}; SessionId: ${sessionId}`);
};
const main = async (): Promise<void> => {
const eitherEmailFromInput = await readMailFromCli(); // Returns an Either type
const eitherEmail = eitherEmailFromInput.map(validateEmail); // map over the either to validate the email
eitherEmail.caseOf({
Left: (error) => console.log(error.errorReason), // email is invalid
Right: async (email) => {
const eitherSessionId = await getSessionIdFromEmail(email);
eitherSessionId.caseOf({
Left: (error) => console.log(error.errorReason), // getting the sessionId failed
Right: (sessionId) => {
printInformations({ sessionId, email });
},
});
},
});
};
const flow = EitherAsync<{ errorReason: string }, void>(
async ({ liftEither, fromPromise }) => {
const emailFromInput = await fromPromise(readMailFromCli());
const email = await liftEither(validateEmail(emailFromInput));
const sessionId = await fromPromise(getSessionIdFromEmail(email));
printInformations({ sessionId, email });
},
);
const main2 = async () => {
const result = await flow.run();
result.ifLeft((error) => {
console.log(error.errorReason);
});
};
main();

View file

@ -0,0 +1,8 @@
import type { Either } from "purify-ts";
import { recupereElementAvecSelecteur, recupereElementsAvecSelecteur } from "./dom";
export const recupereElementsDocument: (selecteur: string) => Either<SyntaxError, Element[]> =
recupereElementsAvecSelecteur(document);
export const recupereElementDocument: <E extends Element = Element>(selecteur: string) => Either<SyntaxError, E> =
recupereElementAvecSelecteur(document);

View file

@ -1,19 +1,68 @@
// Scripts pour la Page Produit // Scripts pour la Page Produit
import { safeQuerySelectorAll } from "./lib/dom.js"; import { Either, identity } from "purify-ts";
import {
ATTRIBUT_ARIA_SELECTED,
ATTRIBUT_HIDDEN,
ROUTE_API_AJOUTE_ARTICLE_PANIER,
SELECTEUR_BOUTON_AJOUT_PANIER,
SELECTEUR_BOUTON_PANIER,
SELECTEUR_SELECTEUR_QUANTITE,
} from "./lib/constantes";
import { leveErreur } from "./lib/erreurs";
import { recupereElementDocument, recupereElementsDocument } from "./lib/utils";
import { WCStoreCartAddItemArgs } from "./types/cart-add-item";
/** États utiles pour les scripts de la page. */
type Etats = {
/** L'ID en base de données du Produit. */
idProduit: number;
/** Un nonce pour l'authentification de requêtes API */
nonce: string;
};
type EnsembleLienContenu = [Element, Element]; type EnsembleLienContenu = [Element, Element];
const fermeToutesSections = (ensembleLiensContenus: EnsembleLienContenu[]) => { // @ts-expect-error - États injectés par le modèle PHP
ensembleLiensContenus.forEach((ensemble) => { const ETATS: Etats = _etats;
ensemble[0].setAttribute("aria-selected", "false");
ensemble[1].setAttribute("hidden", "true"); /**
* Déplie toutes les sections de la Boîte des Informations Produit en ajustant les attributs
* correspondants.
*/
const deplieToutesSections: (ensembleLiensContenus: EnsembleLienContenu[]) => void = (ensembleLiensContenus) => {
ensembleLiensContenus.forEach(ensemble => {
ensemble[0].setAttribute(ATTRIBUT_ARIA_SELECTED, "false");
ensemble[1].setAttribute(ATTRIBUT_HIDDEN, "true");
}); });
}; };
// Éléments d'intérêt
/** Le « Bouton » vers le Panier dont le texte est un indicateur du nombre de Produits dedans. */
const BOUTON_PANIER: Either<SyntaxError, HTMLAnchorElement> = recupereElementDocument<HTMLAnchorElement>(
SELECTEUR_BOUTON_PANIER,
);
const gereBoiteInformationsProduit = () => { const gereBoiteInformationsProduit = () => {
const liensOnglets: Element[] = safeQuerySelectorAll(document)("a[role='tab']"); /* Récupère les Éléments intéressants */
const sectionsContenus: Element[] = safeQuerySelectorAll(document)("section[role='tabpanel']"); const liensOnglets: Element[] = recupereElementsDocument("a[role='tab']").caseOf({
Left: leveErreur,
Right: identity,
});
const sectionsContenus: Element[] = recupereElementsDocument("section[role='tabpanel']").caseOf({
Left: leveErreur,
Right: identity,
});
const selecteurQuantite: HTMLSelectElement = recupereElementDocument<HTMLSelectElement>(SELECTEUR_SELECTEUR_QUANTITE)
.caseOf({
Left: leveErreur,
Right: identity,
});
const boutonAjoutPanier: HTMLButtonElement = recupereElementDocument<HTMLButtonElement>(SELECTEUR_BOUTON_AJOUT_PANIER)
.caseOf({
Left: leveErreur,
Right: identity,
});
const onglets = new Map<string, EnsembleLienContenu>(); const onglets = new Map<string, EnsembleLienContenu>();
/* Créé la Map avec les ensembles Lien-Contenu */ /* Créé la Map avec les ensembles Lien-Contenu */
@ -35,21 +84,74 @@ const gereBoiteInformationsProduit = () => {
e.preventDefault(); e.preventDefault();
/* Sauvegarde l'état d'ouverture de la section avant de toutes les fermer */ /* Sauvegarde l'état d'ouverture de la section avant de toutes les fermer */
const estAncienOngletCourant: boolean = v[0].getAttribute("aria-selected") === "true"; const estAncienOngletCourant: boolean = v[0].getAttribute(ATTRIBUT_ARIA_SELECTED) === "true";
fermeToutesSections(Array.from(onglets.values())); deplieToutesSections(Array.from(onglets.values()));
/* Ne fais rien de plus si l'onglet sélectionné était le courant */ /* Ne fais rien de plus si l'onglet sélectionné était le courant */
if (estAncienOngletCourant) return; if (estAncienOngletCourant) return;
/* Ouvre le nouvel onglet sélectionné */ /* Ouvre le nouvel onglet sélectionné */
v[0].setAttribute("aria-selected", "true"); v[0].setAttribute(ATTRIBUT_ARIA_SELECTED, "true");
v[1].removeAttribute("hidden"); v[1].removeAttribute(ATTRIBUT_HIDDEN);
}); });
}); });
/* Ajout des Écouteurs d'Événements */
selecteurQuantite.addEventListener("change", (e) => {
/* e peut être null ou "--" ou autre chose */
console.debug(selecteurQuantite.value, e);
});
boutonAjoutPanier.addEventListener("click", (e) => {
e.preventDefault();
ajouteProduitAuPanier();
});
console.debug(onglets); console.debug(onglets);
}; };
// TODO: Traiter le cas des Produits avec variations
const ajouteProduitAuPanier = () => {
const requeteInvalide: WCStoreCartAddItemArgs = {
quantity: 1,
id: ETATS.idProduit,
};
// TODO: Traiter les cas d'erreurs avec ts-pattern
/* Exécution de la requête */
Either
.encase<Error, WCStoreCartAddItemArgs>(() => WCStoreCartAddItemArgs(requeteInvalide))
.ifLeft(e => console.error(e))
.map(r => {
fetch(
ROUTE_API_AJOUTE_ARTICLE_PANIER,
{
body: JSON.stringify(r),
credentials: "same-origin",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"X-WC-Store-API-Nonce": ETATS.nonce,
},
method: "POST",
mode: "same-origin",
signal: AbortSignal.timeout(5000),
},
)
.then(a => a.json())
.then(a => {
BOUTON_PANIER
.caseOf({
Left: (e) => console.error("Le bouton du Panier n'existe pas !", e),
Right: (r) => {
console.debug(r);
r.textContent = `cart ${a.items.length}`;
},
});
})
.catch(b => console.error(b));
});
};
/* Fonctions utilitaires */ /* Fonctions utilitaires */
// const creeObservateurIntersection = (element: Element, options: IntersectionObserverInit, classe = "anime") => { // const creeObservateurIntersection = (element: Element, options: IntersectionObserverInit, classe = "anime") => {
@ -76,4 +178,5 @@ const gereBoiteInformationsProduit = () => {
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
gereBoiteInformationsProduit(); gereBoiteInformationsProduit();
// recuperePanierUtilisateur();
}); });

View file

@ -0,0 +1,23 @@
import * as v from "valibot";
const WCStoreCartAddItemArgsItemsSchema = v.object({
/** Variation attribute name. */
attribute: v.string(),
/** Variation attribute value. */
value: v.string(),
});
const WCStoreCartAddItemArgsSchema = v.object({
/** The basket item product or variation ID. */
id: v.optional(v.number()),
/** Quantity of this item to add to the basket. */
quantity: v.optional(v.number()),
/** Chosen attributes (for variations). */
variation: v.optional(v.array(WCStoreCartAddItemArgsItemsSchema)),
});
export type WCStoreCartAddItemArgsItems = v.InferOutput<typeof WCStoreCartAddItemArgsItemsSchema>;
export type WCStoreCartAddItemArgs = v.InferOutput<typeof WCStoreCartAddItemArgsSchema>;
export const WCStoreCartAddItemArgs: (args: unknown) => WCStoreCartAddItemArgs = (args) =>
v.parse(WCStoreCartAddItemArgsSchema, args);

View file

@ -0,0 +1,12 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
/** URL du site. */
readonly VITE_URL: string;
/** URL du endpoint pour le report d'Erreurs au service GlitchTip. */
readonly VITE_GLITCHTIP_NSD: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View file

@ -69,7 +69,7 @@
<section class="actions-produit"> <section class="actions-produit">
{# TODO: Ajouter au Panier sans rafraîchir la Page #} {# TODO: Ajouter au Panier sans rafraîchir la Page #}
<button class="bouton-case-pleine desactive" type="button">Add to cart</button> <button class="bouton-case-pleine desactive" id="bouton-ajout-panier" type="button">Add to cart</button>
</section> </section>
</div> </div>
</aside> </aside>

View file

@ -1,5 +1,23 @@
{% extends "base.twig" %} {% extends "base.twig" %}
{% block head %}
<script>
// Injection d'états pour les Scripts de la page.
/**
* @typedef {Object} Etats - États utiles pour les scripts de la page.
* @property {number} idProduit - L'ID en base de données du Produit.
* @property {string} nonce - Un nonce pour l'authentification de requêtes API.
*/
/** @type {Etats} */
const _etats = {
idProduit: {{ produit.id }},
nonce: "{{ nonce }}",
};
</script>
{% endblock head %}
{% block contenu %} {% block contenu %}
{# Menu des catégories de Produits #} {# Menu des catégories de Produits #}
{% include "parts/menu-categories-produits.twig" %} {% include "parts/menu-categories-produits.twig" %}