diff --git a/.eslintrc b/.eslintrc
index e3c8e4c..1ba96a6 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -6,6 +6,9 @@
"plugins": [
"prettier"
],
+ "env": {
+ "browser": true
+ },
"globals": {
"jQuery": true,
"wp": true
diff --git a/components/parts/common/swiper-controls.php b/components/parts/common/swiper-controls.php
new file mode 100644
index 0000000..5c2515a
--- /dev/null
+++ b/components/parts/common/swiper-controls.php
@@ -0,0 +1,17 @@
+
+
+
diff --git a/inc/Services/Assets.php b/inc/Services/Assets.php
index db49859..1403f29 100644
--- a/inc/Services/Assets.php
+++ b/inc/Services/Assets.php
@@ -88,7 +88,7 @@ public function register_assets(): void {
$this->assets_tools->register_script(
'scripts',
$asset['file'],
- array_merge( [ 'jquery' ], $asset['dependencies'] ), // ensure jQuery dependency is set even if not declared explicitly in the JS
+ array_merge( [ 'jquery', 'wp-i18n' ], $asset['dependencies'] ), // ensure jQuery dependency is set even if not declared explicitly in the JS
$asset['version'],
[ 'strategy' => 'defer' ]
);
@@ -127,6 +127,12 @@ public function register_assets(): void {
public function enqueue_scripts(): void {
// JS
$this->assets_tools->enqueue_script( 'scripts' );
+
+ wp_set_script_translations(
+ 'scripts',
+ 'beapi-blocks-theme',
+ \get_theme_file_path( 'languages' )
+ );
}
/**
diff --git a/languages/beapi-blocks-theme-fr_FR-a442c66c97675e8bda3e3b62b3a8c8ec.json b/languages/beapi-blocks-theme-fr_FR-a442c66c97675e8bda3e3b62b3a8c8ec.json
new file mode 100644
index 0000000..b02897e
--- /dev/null
+++ b/languages/beapi-blocks-theme-fr_FR-a442c66c97675e8bda3e3b62b3a8c8ec.json
@@ -0,0 +1 @@
+{"translation-revision-date":"2026-04-10 16:29+0200","generator":"WP-CLI\/2.12.0","source":"dist\/app.js","domain":"messages","locale_data":{"messages":{"":{"domain":"messages","lang":"fr_FR","plural-forms":"nplurals=2; plural=(n > 1);"},"Previous slide":["Diapositive pr\u00e9c\u00e9dente"],"Next slide":["Diapositive suivante"],"This is the first slide":["C'est la premi\u00e8re diapositive"],"This is the last slide":["C'est la derni\u00e8re diapositive"],"Go to slide {{index}}":["Aller \u00e0 la diapositive {{index}}"],"{{index}} of {{slidesLength}}":["{{index}} sur {{slidesLength}}"]}}}
\ No newline at end of file
diff --git a/languages/beapi-blocks-theme-fr_FR.mo b/languages/beapi-blocks-theme-fr_FR.mo
index 9f0fed1..567779f 100644
Binary files a/languages/beapi-blocks-theme-fr_FR.mo and b/languages/beapi-blocks-theme-fr_FR.mo differ
diff --git a/languages/beapi-blocks-theme-fr_FR.po b/languages/beapi-blocks-theme-fr_FR.po
index d79f5d8..e4eb790 100644
--- a/languages/beapi-blocks-theme-fr_FR.po
+++ b/languages/beapi-blocks-theme-fr_FR.po
@@ -381,3 +381,29 @@ msgstr ""
#~ msgid "Add to my calendar"
#~ msgstr "Ajouter à mon calendrier"
+
+#: components/parts/common/swiper-controls.php:14
+#: dist/app.js
+msgid "Previous slide"
+msgstr "Diapositive précédente"
+
+#: components/parts/common/swiper-controls.php:15
+#: dist/app.js
+msgid "Next slide"
+msgstr "Diapositive suivante"
+
+#: dist/app.js
+msgid "This is the first slide"
+msgstr "C'est la première diapositive"
+
+#: dist/app.js
+msgid "This is the last slide"
+msgstr "C'est la dernière diapositive"
+
+#: dist/app.js
+msgid "Go to slide {{index}}"
+msgstr "Aller à la diapositive {{index}}"
+
+#: dist/app.js
+msgid "{{index}} of {{slidesLength}}"
+msgstr "{{index}} sur {{slidesLength}}"
diff --git a/languages/beapi-blocks-theme.pot b/languages/beapi-blocks-theme.pot
index d458cc4..e3fc51e 100644
--- a/languages/beapi-blocks-theme.pot
+++ b/languages/beapi-blocks-theme.pot
@@ -336,3 +336,29 @@ msgstr ""
msgctxt "Template part name"
msgid "Footer"
msgstr ""
+
+#: components/parts/common/swiper-controls.php:14
+#: dist/app.js
+msgid "Previous slide"
+msgstr ""
+
+#: components/parts/common/swiper-controls.php:15
+#: dist/app.js
+msgid "Next slide"
+msgstr ""
+
+#: dist/app.js
+msgid "This is the first slide"
+msgstr ""
+
+#: dist/app.js
+msgid "This is the last slide"
+msgstr ""
+
+#: dist/app.js
+msgid "Go to slide {{index}}"
+msgstr ""
+
+#: dist/app.js
+msgid "{{index}} of {{slidesLength}}"
+msgstr ""
diff --git a/package-lock.json b/package-lock.json
index 5aec151..860a27e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,8 @@
"license": "GPL-2.0",
"dependencies": {
"@beapi/be-a11y": "^2.0.1",
- "oneloop.js": "^5.3.1"
+ "oneloop.js": "^5.3.1",
+ "swiper": "^14.3.0"
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^4.0.1",
@@ -22,6 +23,7 @@
"@wordpress/env": "^11.13.0",
"@wordpress/eslint-plugin": "^22.22.0",
"@wordpress/hooks": "^4.53.0",
+ "@wordpress/i18n": "^4.58.0",
"@wordpress/scripts": "^34.1.0",
"@wordpress/stylelint-config": "^24.2.0",
"chalk": "^4.1.2",
@@ -2067,6 +2069,16 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.29.10",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz",
+ "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
@@ -8423,9 +8435,9 @@
"license": "MIT"
},
"node_modules/@tannin/sprintf": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.3.tgz",
- "integrity": "sha512-RwARl+hFwhzy0tg9atWcchLFvoQiOh4rrP7uG2N5E4W80BPCUX0ElcUR9St43fxB9EfjsW2df9Qp+UsTbvQDjA==",
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.4.tgz",
+ "integrity": "sha512-Kypdet5L0Hhkfbdkn2QREczNTjCXhiAnouRINyAePy1lZKHD/zcwiqN93HC7i/vVCAuzNAlp+wuT4yOZ5icOSQ==",
"dev": true,
"license": "MIT"
},
@@ -9786,6 +9798,25 @@
"npm": ">=8.19.2"
}
},
+ "node_modules/@wordpress/a11y/node_modules/@wordpress/i18n": {
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.30.0.tgz",
+ "integrity": "sha512-Q8ivagc2aG+Qe5dTzmiohR+pDo4xDcD746fGI2dq+JIbTibYRnkFoN1VyGFZX/Q+g98I80ZJLJ5Vk3svSXb9ag==",
+ "dev": true,
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.57.0",
+ "gettext-parser": "^1.3.1",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
"node_modules/@wordpress/autop": {
"version": "4.53.0",
"resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-4.53.0.tgz",
@@ -9915,6 +9946,25 @@
}
}
},
+ "node_modules/@wordpress/blocks/node_modules/@wordpress/i18n": {
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.30.0.tgz",
+ "integrity": "sha512-Q8ivagc2aG+Qe5dTzmiohR+pDo4xDcD746fGI2dq+JIbTibYRnkFoN1VyGFZX/Q+g98I80ZJLJ5Vk3svSXb9ag==",
+ "dev": true,
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.57.0",
+ "gettext-parser": "^1.3.1",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
"node_modules/@wordpress/browserslist-config": {
"version": "6.53.0",
"resolved": "https://registry.npmjs.org/@wordpress/browserslist-config/-/browserslist-config-6.53.0.tgz",
@@ -10224,14 +10274,13 @@
}
},
"node_modules/@wordpress/hooks": {
- "version": "4.53.0",
- "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.53.0.tgz",
- "integrity": "sha512-YU30rweJCTu5g7X8bc+w6QyoxJBYLEHH++Wwo/IbbROHUC/4I7e+ZtxnPWls8BTtMP4d28JJ74s+UoKpzd2J7w==",
+ "version": "4.57.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.57.0.tgz",
+ "integrity": "sha512-vRJxRhOj+a19RvdW6u0ZBmiQc/t/ueVGsIP3IbS9zuvfLJILtvMSWXU8Fe3m/W+mBK5ZEOfm72LYBhWGPse0DA==",
"dev": true,
"license": "GPL-2.0-or-later",
"engines": {
- "node": ">=18.12.0",
- "npm": ">=8.19.2"
+ "node": ">=18.12.0"
}
},
"node_modules/@wordpress/html-entities": {
@@ -10246,25 +10295,46 @@
}
},
"node_modules/@wordpress/i18n": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.26.0.tgz",
- "integrity": "sha512-v/LzeB7T5YKIfBgmLM0jYG8zOuu81biZ90j8RY9pHIVu+jdRE+DSmt55S3FvtXRo5J2X9FoigFRzYPKXLbitWA==",
+ "version": "4.58.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-4.58.0.tgz",
+ "integrity": "sha512-VfvS3BWv/RDjRKD6PscIcvYfWKnGJcI/DEqyDgUMhxCM6NRwoL478CsUKTiGJIymeyRodNRfprdcF086DpGKYw==",
"dev": true,
"license": "GPL-2.0-or-later",
"dependencies": {
- "@tannin/sprintf": "^1.3.2",
- "@wordpress/hooks": "^4.53.0",
+ "@babel/runtime": "^7.16.0",
+ "@wordpress/hooks": "^3.58.0",
"gettext-parser": "^1.3.1",
+ "memize": "^2.1.0",
+ "sprintf-js": "^1.1.1",
"tannin": "^1.2.0"
},
"bin": {
"pot-to-php": "tools/pot-to-php.js"
},
"engines": {
- "node": ">=18.12.0",
- "npm": ">=8.19.2"
+ "node": ">=12"
+ }
+ },
+ "node_modules/@wordpress/i18n/node_modules/@wordpress/hooks": {
+ "version": "3.58.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.58.0.tgz",
+ "integrity": "sha512-9LB0ZHnZRQlORttux9t/xbAskF+dk2ujqzPGsVzc92mSKpQP3K2a5Wy74fUnInguB1vLUNHT6nrNdkVom5qX1Q==",
+ "dev": true,
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@babel/runtime": "^7.16.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
+ "node_modules/@wordpress/i18n/node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/@wordpress/is-shallow-equal": {
"version": "5.53.0",
"resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.53.0.tgz",
@@ -10336,6 +10406,25 @@
}
}
},
+ "node_modules/@wordpress/keycodes/node_modules/@wordpress/i18n": {
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.30.0.tgz",
+ "integrity": "sha512-Q8ivagc2aG+Qe5dTzmiohR+pDo4xDcD746fGI2dq+JIbTibYRnkFoN1VyGFZX/Q+g98I80ZJLJ5Vk3svSXb9ag==",
+ "dev": true,
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.57.0",
+ "gettext-parser": "^1.3.1",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
"node_modules/@wordpress/npm-package-json-lint-config": {
"version": "5.53.0",
"resolved": "https://registry.npmjs.org/@wordpress/npm-package-json-lint-config/-/npm-package-json-lint-config-5.53.0.tgz",
@@ -10461,6 +10550,25 @@
}
}
},
+ "node_modules/@wordpress/rich-text/node_modules/@wordpress/i18n": {
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.30.0.tgz",
+ "integrity": "sha512-Q8ivagc2aG+Qe5dTzmiohR+pDo4xDcD746fGI2dq+JIbTibYRnkFoN1VyGFZX/Q+g98I80ZJLJ5Vk3svSXb9ag==",
+ "dev": true,
+ "license": "GPL-2.0-or-later",
+ "dependencies": {
+ "@tannin/sprintf": "^1.3.2",
+ "@wordpress/hooks": "^4.57.0",
+ "gettext-parser": "^1.3.1",
+ "tannin": "^1.2.0"
+ },
+ "bin": {
+ "pot-to-php": "tools/pot-to-php.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
"node_modules/@wordpress/scripts": {
"version": "34.1.0",
"resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-34.1.0.tgz",
@@ -10561,85 +10669,15 @@
"node": ">=18"
}
},
- "node_modules/@wordpress/scripts/node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.0.tgz",
- "integrity": "sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@eslint-community/regexpp": "^4.12.2",
- "@typescript-eslint/scope-manager": "8.72.0",
- "@typescript-eslint/type-utils": "8.72.0",
- "@typescript-eslint/utils": "8.72.0",
- "@typescript-eslint/visitor-keys": "8.72.0",
- "ignore": "^7.0.5",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.72.0",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@wordpress/scripts/node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
- "version": "7.0.13",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.13.tgz",
- "integrity": "sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/@wordpress/scripts/node_modules/@typescript-eslint/parser": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.0.tgz",
- "integrity": "sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.72.0",
- "@typescript-eslint/types": "8.72.0",
- "@typescript-eslint/typescript-estree": "8.72.0",
- "@typescript-eslint/visitor-keys": "8.72.0",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@wordpress/scripts/node_modules/@typescript-eslint/project-service": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.0.tgz",
- "integrity": "sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==",
+ "version": "8.72.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.1.tgz",
+ "integrity": "sha512-fAuVMkwdO/huI8nyo/WKnNKf6Gag93S56Rh3Ts5mGcavwSdaN8tRg1gCeIGX8PF+nzp1BjGfO4gHhGzW1J6jTg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.72.0",
- "@typescript-eslint/types": "^8.72.0",
+ "@typescript-eslint/tsconfig-utils": "^8.72.1",
+ "@typescript-eslint/types": "^8.72.1",
"debug": "^4.4.3"
},
"engines": {
@@ -10654,14 +10692,14 @@
}
},
"node_modules/@wordpress/scripts/node_modules/@typescript-eslint/scope-manager": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.0.tgz",
- "integrity": "sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==",
+ "version": "8.72.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.1.tgz",
+ "integrity": "sha512-lC/t6eMCZZQAAKZEYGL6RDVSuFehpdmMT6h+nJIjIa6oylN/PwDgDVtEIbB7O9xFfZYtZAYLoLlDNWKJhtF+uQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.72.0",
- "@typescript-eslint/visitor-keys": "8.72.0"
+ "@typescript-eslint/types": "8.72.1",
+ "@typescript-eslint/visitor-keys": "8.72.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10672,9 +10710,9 @@
}
},
"node_modules/@wordpress/scripts/node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.0.tgz",
- "integrity": "sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==",
+ "version": "8.72.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.1.tgz",
+ "integrity": "sha512-e4D5R5vpeAEPBxT2BAzsFQL3sbwyeeeo5+aczgNSRK1V734jE7pTqaGFU+fXapWEQwnZbpFaTn32+Z8DwS6OTg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10688,37 +10726,10 @@
"typescript": ">=4.8.4 <6.1.0"
}
},
- "node_modules/@wordpress/scripts/node_modules/@typescript-eslint/type-utils": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.0.tgz",
- "integrity": "sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@typescript-eslint/types": "8.72.0",
- "@typescript-eslint/typescript-estree": "8.72.0",
- "@typescript-eslint/utils": "8.72.0",
- "debug": "^4.4.3",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@wordpress/scripts/node_modules/@typescript-eslint/types": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.0.tgz",
- "integrity": "sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==",
+ "version": "8.72.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.1.tgz",
+ "integrity": "sha512-LmE5IhQet1BWOWEXqBrVtBPsMAOQ6Nzk35d+WLh3mk4EUF33sqlfxjio1MdYeSvKSG5skactKKuixBIKXhL4Rw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10730,16 +10741,16 @@
}
},
"node_modules/@wordpress/scripts/node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.0.tgz",
- "integrity": "sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==",
+ "version": "8.72.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.1.tgz",
+ "integrity": "sha512-DAlUoUV93hIvrtIQLwCbmZQYVZwOMhiHJrOVUUIWicFZaIYjTRqlY3I/NZevfkLmSqkNHRu2qYDcq9lrn16oOQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.72.0",
- "@typescript-eslint/tsconfig-utils": "8.72.0",
- "@typescript-eslint/types": "8.72.0",
- "@typescript-eslint/visitor-keys": "8.72.0",
+ "@typescript-eslint/project-service": "8.72.1",
+ "@typescript-eslint/tsconfig-utils": "8.72.1",
+ "@typescript-eslint/types": "8.72.1",
+ "@typescript-eslint/visitor-keys": "8.72.1",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
@@ -10758,16 +10769,16 @@
}
},
"node_modules/@wordpress/scripts/node_modules/@typescript-eslint/utils": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.0.tgz",
- "integrity": "sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==",
+ "version": "8.72.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.1.tgz",
+ "integrity": "sha512-5v5liypurJBFHCBNqh/RjsymAD+VWcE0djuGY/BUqp5fC3Qn+luWDEksjuTtWjPn6i5zQlUtkZCRG/CYG0yCkA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.72.0",
- "@typescript-eslint/types": "8.72.0",
- "@typescript-eslint/typescript-estree": "8.72.0"
+ "@typescript-eslint/scope-manager": "8.72.1",
+ "@typescript-eslint/types": "8.72.1",
+ "@typescript-eslint/typescript-estree": "8.72.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -10782,13 +10793,13 @@
}
},
"node_modules/@wordpress/scripts/node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.72.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.0.tgz",
- "integrity": "sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==",
+ "version": "8.72.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.1.tgz",
+ "integrity": "sha512-SB8xqipSzZHUePCCLNeYxqnxq83X61/qzNhjQGbTfjxGZIeOEP7tBgWYX05Tcd6CgE02WdTJnh5b8kMZJ/+bnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.72.0",
+ "@typescript-eslint/types": "8.72.1",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
@@ -34167,6 +34178,25 @@
"url": "https://github.com/sponsors/fb55"
}
},
+ "node_modules/swiper": {
+ "version": "14.3.0",
+ "resolved": "https://registry.npmjs.org/swiper/-/swiper-14.3.0.tgz",
+ "integrity": "sha512-qw0Xv1+sylM8KCQxWxV2iYAK2G6XjFGPZ/8F1baEsY8DK4A3sTnLx6jUJ0G71rDbVQ05JHMWkcWmJ5NvlxZO0w==",
+ "funding": [
+ {
+ "type": "custom",
+ "url": "https://sponsors.nolimits4web.com"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nolimits4web"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.7.0"
+ }
+ },
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
diff --git a/package.json b/package.json
index ed0874b..71a78cb 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,8 @@
"license": "GPL-2.0",
"dependencies": {
"@beapi/be-a11y": "^2.0.1",
- "oneloop.js": "^5.3.1"
+ "oneloop.js": "^5.3.1",
+ "swiper": "^14.3.0"
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^4.0.1",
@@ -32,6 +33,7 @@
"@wordpress/env": "^11.13.0",
"@wordpress/eslint-plugin": "^22.22.0",
"@wordpress/hooks": "^4.53.0",
+ "@wordpress/i18n": "^4.58.0",
"@wordpress/scripts": "^34.1.0",
"@wordpress/stylelint-config": "^24.2.0",
"chalk": "^4.1.2",
diff --git a/src/js/common/classes/Slider.js b/src/js/common/classes/Slider.js
new file mode 100644
index 0000000..05ac7b4
--- /dev/null
+++ b/src/js/common/classes/Slider.js
@@ -0,0 +1,156 @@
+import AbstractDomElement from './AbstractDomElement';
+import Swiper from 'swiper';
+import { A11y, Navigation } from 'swiper/modules';
+import { __ } from '@wordpress/i18n';
+import noop from '../utils/noop';
+
+/**
+ * Thin wrapper around Swiper with project defaults (A11y + Navigation).
+ *
+ * @augments AbstractDomElement
+ */
+export default class Slider extends AbstractDomElement {
+ /**
+ * @param {HTMLElement} element - Root `.swiper` element
+ * @param {SliderOptions} [options]
+ */
+ constructor(element, options) {
+ const instance = super(element, options);
+
+ // avoid double init :
+ if (!instance.isNewInstance()) {
+ return instance;
+ }
+
+ this.init();
+ }
+
+ init() {
+ const el = this._element;
+ const {
+ options,
+ onChange,
+ hideNavigationFromA11y,
+ fixLastSlideActiveOnEnd,
+ } = this._settings;
+
+ const defaultModules = Slider.defaults.options.modules;
+ const userModules = options.modules || [];
+ const allModules = [
+ ...defaultModules,
+ ...userModules.filter((mod) => !defaultModules.includes(mod)),
+ ];
+
+ const slidesAreListItems =
+ el.querySelector('.swiper-slide')?.tagName === 'LI';
+
+ const hideNavigationButtons = (swiper) => {
+ if (!hideNavigationFromA11y) {
+ return;
+ }
+
+ swiper.navigation.prevEl?.setAttribute('tabindex', '-1');
+ swiper.navigation.prevEl?.setAttribute('aria-hidden', 'true');
+ swiper.navigation.nextEl?.setAttribute('tabindex', '-1');
+ swiper.navigation.nextEl?.setAttribute('aria-hidden', 'true');
+ };
+
+ const applyLastSlideActiveFix = (swiper) => {
+ if (!swiper.isEnd || swiper.isBeginning) {
+ return;
+ }
+
+ swiper.activeIndex = swiper.slides.length - 1;
+ swiper.updateSlidesClasses();
+ };
+
+ const mergedOptions = {
+ ...options,
+ modules: allModules,
+ a11y: {
+ ...(slidesAreListItems ? { slideRole: '' } : {}),
+ ...options.a11y,
+ },
+ on: {
+ ...options.on,
+ afterInit(swiper) {
+ hideNavigationButtons(swiper);
+
+ if (hideNavigationFromA11y) {
+ swiper.el
+ .querySelector('.swiper-notification')
+ ?.remove();
+ }
+
+ options.on?.afterInit?.call(this, swiper);
+ },
+ transitionEnd(swiper) {
+ hideNavigationButtons(swiper);
+ options.on?.transitionEnd?.call(this, swiper);
+ },
+ progress(swiper, progress) {
+ if (fixLastSlideActiveOnEnd) {
+ applyLastSlideActiveFix(swiper);
+ }
+
+ options.on?.progress?.call(this, swiper, progress);
+ },
+ slideChange(swiper) {
+ if (fixLastSlideActiveOnEnd) {
+ applyLastSlideActiveFix(swiper);
+ }
+
+ options.on?.slideChange?.call(this, swiper);
+ },
+ },
+ };
+
+ this.swiper = new Swiper(el, mergedOptions);
+
+ this.swiper.on('slideChange', onChange.bind(this));
+ }
+
+ /**
+ * @return {import('swiper').Swiper} Native Swiper instance.
+ */
+ getInstance() {
+ return this.swiper;
+ }
+}
+
+/**
+ * @typedef {Object} SliderOptions
+ * @property {Function} [onChange] - Called on slideChange (`this` = Slider instance)
+ * @property {boolean} [hideNavigationFromA11y=false] - Hide prev/next from AT when slides are already focusable; also removes `.swiper-notification`
+ * @property {boolean} [fixLastSlideActiveOnEnd=false] - Fix `.swiper-slide-active` at the end with `slidesPerView: 'auto'` or fractional values other than halves (e.g. 2.75)
+ * @property {import('swiper').SwiperOptions} [options] - Native Swiper options (merged with defaults)
+ */
+
+Slider.defaults = {
+ onChange: noop,
+ hideNavigationFromA11y: false,
+ fixLastSlideActiveOnEnd: false,
+ options: {
+ modules: [A11y, Navigation],
+ a11y: {
+ prevSlideMessage: __('Previous slide', 'beapi-blocks-theme'),
+ nextSlideMessage: __('Next slide', 'beapi-blocks-theme'),
+ firstSlideMessage: __(
+ 'This is the first slide',
+ 'beapi-blocks-theme'
+ ),
+ lastSlideMessage: __(
+ 'This is the last slide',
+ 'beapi-blocks-theme'
+ ),
+ paginationBulletMessage: __(
+ 'Go to slide {{index}}',
+ 'beapi-blocks-theme'
+ ),
+ slideLabelMessage: __(
+ '{{index}} of {{slidesLength}}',
+ 'beapi-blocks-theme'
+ ),
+ },
+ },
+};
diff --git a/src/js/common/utils/beapi.js b/src/js/common/utils/beapi.js
index 01b46f7..25f7b51 100644
--- a/src/js/common/utils/beapi.js
+++ b/src/js/common/utils/beapi.js
@@ -1,4 +1,5 @@
import AbstractDomElement from '../classes/AbstractDomElement';
+import Slider from '../classes/Slider';
import scrollDirection from '../classes/ScrollDirection';
import * as oneloop from 'oneloop.js';
import extend from './extend';
@@ -15,6 +16,7 @@ window.beapi = window.beapi || {};
extend(true, window.beapi, {
classes: {
AbstractDomElement,
+ Slider,
},
libraries: {
oneloop,
diff --git a/src/scss/common/11-vendor/_swiper.scss b/src/scss/common/11-vendor/_swiper.scss
new file mode 100644
index 0000000..371676e
--- /dev/null
+++ b/src/scss/common/11-vendor/_swiper.scss
@@ -0,0 +1,56 @@
+@use "../00-variables/theme-json" as *;
+@use "swiper/css";
+@use "swiper/css/a11y";
+
+.swiper {
+ --swiper-theme-color: #{$settings-color-gray-100};
+
+ &__controls {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &:has(.swiper-button-lock) {
+ display: none;
+ }
+ }
+
+ &__buttons {
+ display: flex;
+ gap: $settings-spacing-xs;
+
+ button,
+ [role="button"] {
+ &.swiper-button-prev {
+ transform: scaleX(-1);
+ }
+
+ &.swiper-button-disabled {
+ cursor: not-allowed;
+ opacity: 0.3;
+ }
+ }
+ }
+
+ &__button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 44px;
+ height: 44px;
+ cursor: pointer;
+ background-color: var(--swiper-theme-color);
+ border-radius: 100%;
+ }
+
+ &-overflow-visible {
+ overflow: visible;
+ }
+
+ .swiper-wrapper {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ }
+}
diff --git a/src/scss/common/editor.scss b/src/scss/common/editor.scss
index 6eb6916..b509a38 100644
--- a/src/scss/common/editor.scss
+++ b/src/scss/common/editor.scss
@@ -13,3 +13,5 @@ $entry-file-name: editor;
@use "06-core-blocks/heading";
@use "09-template-parts/header";
+
+@use "11-vendor/swiper";
diff --git a/src/scss/common/style.scss b/src/scss/common/style.scss
index 2083bf7..b487c04 100644
--- a/src/scss/common/style.scss
+++ b/src/scss/common/style.scss
@@ -17,3 +17,4 @@ $entry-file-name: style;
@use "09-template-parts/header";
@use "11-vendor/toggle";
+@use "11-vendor/swiper";