feat: Added an option to have titles displayed under cards

This commit is contained in:
MrRaph_
2025-05-25 10:03:31 +00:00
parent 54be7b7455
commit fd5275c489
4 changed files with 137 additions and 79 deletions

View File

@@ -3,7 +3,7 @@
* Plugin Name: Category Grid Widget for Elementor * Plugin Name: Category Grid Widget for Elementor
* Plugin URI: https://git.mrraph.fr/WordPress/elementor-category-grid-widget-for-elementor * Plugin URI: https://git.mrraph.fr/WordPress/elementor-category-grid-widget-for-elementor
* Description: Responsive article category grid with image for Elementor. * Description: Responsive article category grid with image for Elementor.
* Version: 1.3.1 * Version: 1.4.0
* Author: MrRaph_ * Author: MrRaph_
* Author URI: https://mrraph.photo * Author URI: https://mrraph.photo
* Requires at least: 5.8 * Requires at least: 5.8
@@ -45,7 +45,7 @@ function ccgw_enqueue_media_uploader($hook_suffix)
'ccgw-category-image', 'ccgw-category-image',
plugin_dir_url(__FILE__) . 'admin/js/category-image.js', plugin_dir_url(__FILE__) . 'admin/js/category-image.js',
['jquery'], ['jquery'],
'1.3.1', '1.4.0',
true true
); );
@@ -223,7 +223,7 @@ function ccgw_enqueue_front_styles()
'ccgw-category-grid-style', 'ccgw-category-grid-style',
plugin_dir_url(__FILE__) . 'css/style.css', plugin_dir_url(__FILE__) . 'css/style.css',
[], [],
'1.3.1' '1.4.0'
); );
} }
} }

View File

@@ -7,16 +7,30 @@
} }
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
/* 0) Cartes, overlay et images */ /* 1) Structure de la carte */
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
.elementor-category-grid .category-card { .elementor-category-grid .category-card {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
/* ratio fixe 5:2 */ /* ratio fixe 5:2 pour limage uniquement */
height: 0; }
.elementor-category-grid .category-card.title-below {
/* autorise la hauteur auto quand titre en dessous */
height: auto !important;
padding-bottom: 0 !important;
}
.elementor-category-grid .category-card-image {
/* reprend lancien padding-bottom */
position: relative;
width: 100%;
padding-bottom: 40%; /* ratio 5:2 */
}
.elementor-category-grid .category-card.title-below .category-card-image {
/* si titre en dessous, on garde juste limage */
padding-bottom: 40%; padding-bottom: 40%;
} }
/* Overlay via pseudo */
.elementor-category-grid .category-card::before { .elementor-category-grid .category-card::before {
content: ""; content: "";
position: absolute; position: absolute;
@@ -26,20 +40,22 @@
pointer-events: none; pointer-events: none;
} }
/* Image plein cadre */
.elementor-category-grid .category-card img { .elementor-category-grid .category-card img {
position: absolute; position: absolute;
inset: 0; inset: 0;
width: 100%; width: 100%; height: 100%;
height: 100%;
object-fit: cover; object-fit: cover;
transition: transform .4s ease; transition: transform .4s ease;
} }
.elementor-category-grid .category-card:hover img { .elementor-category-grid .category-card:hover img {
transform: scale(1.05); transform: scale(1.05);
} }
.elementor-category-grid .category-card-name { /* —————————————————————————————————————————— */
/* 2) Titre overlay */
/* —————————————————————————————————————————— */
.elementor-category-grid .category-card-name.overlay {
position: absolute; position: absolute;
top: 50%; left: 50%; top: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
@@ -54,42 +70,66 @@
} }
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
/* 1) Grille responsive par défaut (desktop) */ /* 3) Titre below */
/* —————————————————————————————————————————— */
.elementor-category-grid .category-card-name-below {
padding: .5em;
text-align: center;
font-size: 1.2rem;
}
.elementor-category-grid .category-card-name-below a {
color: inherit;
text-decoration: none;
transition: text-decoration .3s ease;
}
.elementor-category-grid .category-card-name-below a:hover {
text-decoration: underline;
}
/* —————————————————————————————————————————— */
/* 4) Grille responsive par défaut (desktop) */
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
.elementor-category-grid { .elementor-category-grid {
display: grid; display: grid;
gap: 1rem; gap: 1rem;
/* auto-fit avec min 150px : plusieurs tuiles même sur petits écrans */
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
} }
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
/* 2) Mobile (≤ 480px) → 1 colonne */ /* 5) Mobile (≤ 480px) → 1 colonne */
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
@media screen and (max-width: 480px) { @media screen and (max-width: 480px) {
.elementor-category-grid { .elementor-category-grid {
grid-template-columns: repeat(1, 1fr) !important; grid-template-columns: 1fr !important;
} }
.elementor-category-grid .category-card-name { .elementor-category-grid .category-card-name.overlay {
font-size: 1rem; font-size: 1rem;
padding: .4em .8em; padding: .4em .8em;
} }
.elementor-category-grid .category-card-name-below {
font-size: 1rem;
padding: .4em;
}
} }
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
/* 3) Tablette (481px768px) → 2 colonnes fixes */ /* 6) Tablette (481px768px) → 2 colonnes fixes */
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
@media screen and (min-width: 481px) and (max-width: 768px) { @media screen and (min-width: 481px) and (max-width: 768px) {
.elementor-category-grid { .elementor-category-grid {
grid-template-columns: repeat(2, 1fr) !important; grid-template-columns: repeat(2, 1fr) !important;
} }
.elementor-category-grid .category-card-name { .elementor-category-grid .category-card-name.overlay {
font-size: 1.2rem; font-size: 1.2rem;
} }
.elementor-category-grid .category-card-name-below {
font-size: 1.2rem;
padding: .5em;
}
} }
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
/* 4) Desktop (≥ 769px) → respect des classes */ /* 7) Desktop (≥ 769px) → respect des classes */
/* —————————————————————————————————————————— */ /* —————————————————————————————————————————— */
@media screen and (min-width: 769px) { @media screen and (min-width: 769px) {
.elementor-category-grid.columns-1 { grid-template-columns: repeat(1, 1fr); } .elementor-category-grid.columns-1 { grid-template-columns: repeat(1, 1fr); }
@@ -98,4 +138,4 @@
.elementor-category-grid.columns-4 { grid-template-columns: repeat(4, 1fr); } .elementor-category-grid.columns-4 { grid-template-columns: repeat(4, 1fr); }
.elementor-category-grid.columns-5 { grid-template-columns: repeat(5, 1fr); } .elementor-category-grid.columns-5 { grid-template-columns: repeat(5, 1fr); }
.elementor-category-grid.columns-6 { grid-template-columns: repeat(6, 1fr); } .elementor-category-grid.columns-6 { grid-template-columns: repeat(6, 1fr); }
} }

View File

@@ -5,7 +5,7 @@ Tags: elementor, category, grid, widget, posts
Requires at least: 5.8 Requires at least: 5.8
Tested up to: 6.8 Tested up to: 6.8
Requires PHP: 7.0 Requires PHP: 7.0
Stable tag: 1.3.1 Stable tag: 1.4.0
License: GPLv2 or later License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html License URI: https://www.gnu.org/licenses/gpl-2.0.html
Responsive grid of post categories with images for Elementor. Responsive grid of post categories with images for Elementor.
@@ -46,6 +46,9 @@ Yes, enable the **“Hide categories without image”** option in the widgets
Open the **Style » Hover** tab: choose the overlay color & opacity, and the title color on hover. Open the **Style » Hover** tab: choose the overlay color & opacity, and the title color on hover.
== Changelog == == Changelog ==
= 1.4.0 =
* Added an option to have titles displayed under cards
= 1.3.1 = = 1.3.1 =
* Make style responsive * Make style responsive

View File

@@ -382,6 +382,20 @@ class Elementor_Category_Grid_Widget extends \Elementor\Widget_Base
] ]
); );
$this->add_control(
'title_position',
[
'label' => esc_html__('Position du titre', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::SELECT,
'options' => [
'overlay' => esc_html__('Sur la carte', 'category-grid-widget-for-elementor'),
'below' => esc_html__('Sous la carte', 'category-grid-widget-for-elementor'),
],
'default' => 'overlay',
'description' => esc_html__('Choisissez si le nom de la catégorie apparaît en overlay ou sous limage.', 'category-grid-widget-for-elementor'),
]
);
$this->end_controls_section(); $this->end_controls_section();
/* Section "Mise en page" pour les options d'affichage (colonnes, taille d'image) */ /* Section "Mise en page" pour les options d'affichage (colonnes, taille d'image) */
@@ -439,108 +453,91 @@ class Elementor_Category_Grid_Widget extends \Elementor\Widget_Base
{ {
$settings = $this->get_settings_for_display(); $settings = $this->get_settings_for_display();
// Récupérer les ID des catégories sélectionnées. // 1) Récupérer les IDs des catégories sélectionnées
$selected_ids = []; $selected_ids = [];
if (!empty($settings['categories'])) { if (!empty($settings['categories'])) {
// S'assurer d'avoir un tableau d'ID (entiers). $selected_ids = (array) $settings['categories'];
$selected_ids = is_array($settings['categories']) ? $settings['categories'] : [$settings['categories']]; $selected_ids = array_map('absint', $selected_ids);
$selected_ids = array_map('intval', $selected_ids);
} }
if (empty($selected_ids)) { if (empty($selected_ids)) {
// Aucune catégorie sélectionnée : ne rien afficher.
return; return;
} }
// Déterminer les catégories à afficher (inclure les sous-catégories si demandé). // 2) Construire la liste des IDs à afficher
$category_ids_to_show = $selected_ids;
// 1) Cas : on veut les sous-catégories de la page actuelle
if ('yes' === $settings['subcats_of_current']) { if ('yes' === $settings['subcats_of_current']) {
$queried = get_queried_object(); $queried = get_queried_object();
if ($queried && isset($queried->term_id) && 'category' === $queried->taxonomy) { if ($queried instanceof WP_Term && 'category' === $queried->taxonomy) {
$category_ids_to_show = get_term_children($queried->term_id, 'category'); $category_ids_to_show = get_term_children($queried->term_id, 'category');
} else { } else {
// si pas en archive catégorie, rien à afficher
return; return;
} }
// 2) Sinon, on part des catégories sélectionnées
} else { } else {
$category_ids_to_show = $selected_ids; $category_ids_to_show = $selected_ids;
// Si on veut uniquement leurs sous-catégories
if ('yes' === $settings['only_subcats_of_selected']) { if ('yes' === $settings['only_subcats_of_selected']) {
$sub_ids = []; $subs = [];
foreach ($selected_ids as $cat_id) { foreach ($selected_ids as $cat_id) {
$children = get_term_children($cat_id, 'category'); $children = get_term_children($cat_id, 'category');
if (is_array($children)) { if (is_array($children)) {
$sub_ids = array_merge($sub_ids, $children); $subs = array_merge($subs, $children);
} }
} }
$category_ids_to_show = array_unique($sub_ids); $category_ids_to_show = array_unique($subs);
} } elseif ('yes' === $settings['show_subcategories']) {
// Sinon, lancienne logique : on ajoute ou pas les sous-catégories selon show_subcategories
elseif ('yes' === $settings['show_subcategories']) {
foreach ($selected_ids as $cat_id) { foreach ($selected_ids as $cat_id) {
$category_ids_to_show = array_merge($category_ids_to_show, get_term_children($cat_id, 'category') ?: []); $children = get_term_children($cat_id, 'category') ?: [];
$category_ids_to_show = array_merge($category_ids_to_show, $children);
} }
$category_ids_to_show = array_unique($category_ids_to_show); $category_ids_to_show = array_unique($category_ids_to_show);
} }
} }
// Préparer les args pour get_terms() on ne précise PAS le tri si c'est par count // 3) Préparer args pour get_terms()
$term_args = [ $term_args = [
'taxonomy' => 'category', 'taxonomy' => 'category',
'include' => $category_ids_to_show, 'include' => $category_ids_to_show,
'hide_empty' => false, 'hide_empty' => false,
]; ];
if ('count' !== $settings['order_by']) { if ('count' !== $settings['order_by']) {
// tri natif pour 'name' ou 'id'
$term_args['orderby'] = $settings['order_by']; $term_args['orderby'] = $settings['order_by'];
$term_args['order'] = $settings['order']; $term_args['order'] = $settings['order'];
} }
// Récupérer les termes // 4) Récupérer et filtrer les termes
$raw_terms = get_terms($term_args); $raw_terms = get_terms($term_args);
if (is_wp_error($raw_terms) || empty($raw_terms)) { if (is_wp_error($raw_terms) || empty($raw_terms)) {
return; return;
} }
// Filtrer les termes sans image si demandé
$terms = []; $terms = [];
foreach ($raw_terms as $term) { foreach ($raw_terms as $term) {
$thumb_id = get_term_meta($term->term_id, 'thumbnail_id', true); $thumb_id = get_term_meta($term->term_id, 'thumbnail_id', true);
$image_url = $thumb_id $image_url = $thumb_id ? wp_get_attachment_image_url($thumb_id, $settings['image_size']) : '';
? wp_get_attachment_image_url($thumb_id, $settings['image_size'])
: '';
if ('yes' === $settings['hide_without_image'] && empty($image_url)) { if ('yes' === $settings['hide_without_image'] && empty($image_url)) {
continue; continue;
} }
// Stocker image_url temporaire pour éviter de recalculer
$term->_image_url = $image_url; $term->_image_url = $image_url;
$terms[] = $term; $terms[] = $term;
} }
if (empty($terms)) {
return;
}
// Si tri par nombre d'articles, calculer et trier manuellement // 5) Tri manuel si order_by=count
if ('count' === $settings['order_by']) { if ('count' === $settings['order_by']) {
// Calcul du total d'articles (directs + descendants) pour chaque terme
$counts = []; $counts = [];
foreach ($terms as $term) { foreach ($terms as $term) {
$total = (int) $term->count; $total = intval($term->count);
$desc_ids = get_term_children($term->term_id, 'category'); $children = get_term_children($term->term_id, 'category');
if (is_array($desc_ids)) { if (is_array($children)) {
foreach ($desc_ids as $desc_id) { foreach ($children as $child_id) {
$desc = get_term($desc_id, 'category'); $child = get_term($child_id, 'category');
if (!is_wp_error($desc)) { if (!is_wp_error($child)) {
$total += (int) $desc->count; $total += intval($child->count);
} }
} }
} }
$counts[$term->term_id] = $total; $counts[$term->term_id] = $total;
} }
// Tri selon lordre choisi
usort($terms, function ($a, $b) use ($counts, $settings) { usort($terms, function ($a, $b) use ($counts, $settings) {
$ca = $counts[$a->term_id]; $ca = $counts[$a->term_id];
$cb = $counts[$b->term_id]; $cb = $counts[$b->term_id];
@@ -548,40 +545,58 @@ class Elementor_Category_Grid_Widget extends \Elementor\Widget_Base
return 0; return 0;
} }
if ('ASC' === $settings['order']) { if ('ASC' === $settings['order']) {
return ($ca < $cb) ? -1 : 1; return $ca < $cb ? -1 : 1;
} }
return ($ca > $cb) ? -1 : 1; return $ca > $cb ? -1 : 1;
}); });
} }
// Calcul de la classe grille // 6) Affichage final
$columns = (int) $settings['columns']; $grid_class = 'columns-' . absint($settings['columns']);
$grid_class = 'columns-' . $columns; $is_below = ('below' === $settings['title_position']);
// Rendu HTML
echo '<div class="elementor-category-grid ' . esc_attr($grid_class) . '">'; echo '<div class="elementor-category-grid ' . esc_attr($grid_class) . '">';
foreach ($terms as $term) { foreach ($terms as $term) {
$thumbnail_id = get_term_meta($term->term_id, 'thumbnail_id', true); $thumb_id = get_term_meta($term->term_id, 'thumbnail_id', true);
echo '<div class="category-card">'; $link = get_term_link($term);
echo '<a href="' . esc_url(get_term_link($term)) . '">'; if (is_wp_error($link)) {
if (esc_url($term->_image_url)) { continue;
}
// Ouvre la carte et ajoute la classe si titre en dessous
$card_class = 'category-card' . ($is_below ? ' title-below' : '');
echo '<div class="' . esc_attr($card_class) . '">';
// Image cliquable
echo '<a href="' . esc_url($link) . '">';
if ($thumb_id) {
echo '<div class="category-card-image">'; echo '<div class="category-card-image">';
echo wp_get_attachment_image( echo wp_get_attachment_image(
$thumbnail_id, $thumb_id,
$settings['image_size'], $settings['image_size'],
false, false,
[ [
'alt' => esc_attr($term->name), 'alt' => esc_attr($term->name),
'class' => 'category-card-img', // optionnel : ajoutez vos classes CSS 'class' => 'category-card-img',
] ]
); );
echo '</div>'; echo '</div>';
} }
echo '<div class="category-card-name">' . esc_html($term->name) . '</div>';
echo '</a>'; echo '</a>';
echo '</div>';
}
echo '</div>';
}
// Titre (overlay ou below) **à lintérieur** de la carte
if ($is_below) {
echo '<div class="category-card-name-below">';
echo '<a href="' . esc_url($link) . '">' . esc_html($term->name) . '</a>';
echo '</div>';
} else {
echo '<div class="category-card-name overlay">' . esc_html($term->name) . '</div>';
}
echo '</div>'; // .category-card
}
echo '</div>'; // .elementor-category-grid
}
} }