diff --git a/category-grid-widget-for-elementor.php b/category-grid-widget-for-elementor.php index ecef7f9..5bb30c1 100644 --- a/category-grid-widget-for-elementor.php +++ b/category-grid-widget-for-elementor.php @@ -3,7 +3,7 @@ * Plugin Name: 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. - * Version: 1.3.1 + * Version: 1.4.0 * Author: MrRaph_ * Author URI: https://mrraph.photo * Requires at least: 5.8 @@ -45,7 +45,7 @@ function ccgw_enqueue_media_uploader($hook_suffix) 'ccgw-category-image', plugin_dir_url(__FILE__) . 'admin/js/category-image.js', ['jquery'], - '1.3.1', + '1.4.0', true ); @@ -223,7 +223,7 @@ function ccgw_enqueue_front_styles() 'ccgw-category-grid-style', plugin_dir_url(__FILE__) . 'css/style.css', [], - '1.3.1' + '1.4.0' ); } } diff --git a/css/style.css b/css/style.css index 76cd25f..dc128dd 100644 --- a/css/style.css +++ b/css/style.css @@ -7,16 +7,30 @@ } /* —————————————————————————————————————————— */ -/* 0) Cartes, overlay et images */ +/* 1) Structure de la carte */ /* —————————————————————————————————————————— */ .elementor-category-grid .category-card { position: relative; overflow: hidden; - /* ratio fixe 5:2 */ - height: 0; + /* ratio fixe 5:2 pour l’image uniquement */ +} +.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 l’ancien 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 l’image */ padding-bottom: 40%; } +/* Overlay via pseudo */ .elementor-category-grid .category-card::before { content: ""; position: absolute; @@ -26,20 +40,22 @@ pointer-events: none; } +/* Image plein cadre */ .elementor-category-grid .category-card img { position: absolute; inset: 0; - width: 100%; - height: 100%; + width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; } - .elementor-category-grid .category-card:hover img { transform: scale(1.05); } -.elementor-category-grid .category-card-name { +/* —————————————————————————————————————————— */ +/* 2) Titre overlay */ +/* —————————————————————————————————————————— */ +.elementor-category-grid .category-card-name.overlay { position: absolute; top: 50%; left: 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 { display: grid; gap: 1rem; - /* auto-fit avec min 150px : plusieurs tuiles même sur petits écrans */ 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) { .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; padding: .4em .8em; } + .elementor-category-grid .category-card-name-below { + font-size: 1rem; + padding: .4em; + } } /* —————————————————————————————————————————— */ -/* 3) Tablette (481px–768px) → 2 colonnes fixes */ +/* 6) Tablette (481px–768px) → 2 colonnes fixes */ /* —————————————————————————————————————————— */ @media screen and (min-width: 481px) and (max-width: 768px) { .elementor-category-grid { grid-template-columns: repeat(2, 1fr) !important; } - .elementor-category-grid .category-card-name { + .elementor-category-grid .category-card-name.overlay { 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) { .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-5 { grid-template-columns: repeat(5, 1fr); } .elementor-category-grid.columns-6 { grid-template-columns: repeat(6, 1fr); } -} +} \ No newline at end of file diff --git a/readme.txt b/readme.txt index 47eb0c2..5d90513 100644 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Tags: elementor, category, grid, widget, posts Requires at least: 5.8 Tested up to: 6.8 Requires PHP: 7.0 -Stable tag: 1.3.1 +Stable tag: 1.4.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Responsive grid of post categories with images for Elementor. @@ -46,6 +46,9 @@ Yes, enable the **“Hide categories without image”** option in the widget’s Open the **Style » Hover** tab: choose the overlay color & opacity, and the title color on hover. == Changelog == += 1.4.0 = +* Added an option to have titles displayed under cards + = 1.3.1 = * Make style responsive diff --git a/widgets/category-grid-widget.php b/widgets/category-grid-widget.php index 3a56b26..942f97e 100644 --- a/widgets/category-grid-widget.php +++ b/widgets/category-grid-widget.php @@ -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 l’image.', 'category-grid-widget-for-elementor'), + ] + ); + $this->end_controls_section(); /* 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(); - // 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 = []; if (!empty($settings['categories'])) { - // S'assurer d'avoir un tableau d'ID (entiers). - $selected_ids = is_array($settings['categories']) ? $settings['categories'] : [$settings['categories']]; - $selected_ids = array_map('intval', $selected_ids); + $selected_ids = (array) $settings['categories']; + $selected_ids = array_map('absint', $selected_ids); } if (empty($selected_ids)) { - // Aucune catégorie sélectionnée : ne rien afficher. return; } - // Déterminer les catégories à afficher (inclure les sous-catégories si demandé). - $category_ids_to_show = $selected_ids; - - // 1) Cas : on veut les sous-catégories de la page actuelle + // 2) Construire la liste des IDs à afficher if ('yes' === $settings['subcats_of_current']) { $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'); } else { - // si pas en archive catégorie, rien à afficher return; } - - // 2) Sinon, on part des catégories sélectionnées } else { $category_ids_to_show = $selected_ids; - // Si on veut uniquement leurs sous-catégories if ('yes' === $settings['only_subcats_of_selected']) { - $sub_ids = []; + $subs = []; foreach ($selected_ids as $cat_id) { $children = get_term_children($cat_id, 'category'); if (is_array($children)) { - $sub_ids = array_merge($sub_ids, $children); + $subs = array_merge($subs, $children); } } - $category_ids_to_show = array_unique($sub_ids); - } - // Sinon, l’ancienne logique : on ajoute ou pas les sous-catégories selon show_subcategories - elseif ('yes' === $settings['show_subcategories']) { + $category_ids_to_show = array_unique($subs); + } elseif ('yes' === $settings['show_subcategories']) { 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); } } - // 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 = [ 'taxonomy' => 'category', 'include' => $category_ids_to_show, 'hide_empty' => false, ]; if ('count' !== $settings['order_by']) { - // tri natif pour 'name' ou 'id' $term_args['orderby'] = $settings['order_by']; $term_args['order'] = $settings['order']; } - // Récupérer les termes + // 4) Récupérer et filtrer les termes $raw_terms = get_terms($term_args); if (is_wp_error($raw_terms) || empty($raw_terms)) { return; } - - // Filtrer les termes sans image si demandé $terms = []; foreach ($raw_terms as $term) { $thumb_id = get_term_meta($term->term_id, 'thumbnail_id', true); - $image_url = $thumb_id - ? wp_get_attachment_image_url($thumb_id, $settings['image_size']) - : ''; - + $image_url = $thumb_id ? wp_get_attachment_image_url($thumb_id, $settings['image_size']) : ''; if ('yes' === $settings['hide_without_image'] && empty($image_url)) { continue; } - - // Stocker image_url temporaire pour éviter de recalculer $term->_image_url = $image_url; $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']) { - // Calcul du total d'articles (directs + descendants) pour chaque terme $counts = []; foreach ($terms as $term) { - $total = (int) $term->count; - $desc_ids = get_term_children($term->term_id, 'category'); - if (is_array($desc_ids)) { - foreach ($desc_ids as $desc_id) { - $desc = get_term($desc_id, 'category'); - if (!is_wp_error($desc)) { - $total += (int) $desc->count; + $total = intval($term->count); + $children = get_term_children($term->term_id, 'category'); + if (is_array($children)) { + foreach ($children as $child_id) { + $child = get_term($child_id, 'category'); + if (!is_wp_error($child)) { + $total += intval($child->count); } } } $counts[$term->term_id] = $total; } - // Tri selon l’ordre choisi usort($terms, function ($a, $b) use ($counts, $settings) { $ca = $counts[$a->term_id]; $cb = $counts[$b->term_id]; @@ -548,40 +545,58 @@ class Elementor_Category_Grid_Widget extends \Elementor\Widget_Base return 0; } 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 - $columns = (int) $settings['columns']; - $grid_class = 'columns-' . $columns; + // 6) Affichage final + $grid_class = 'columns-' . absint($settings['columns']); + $is_below = ('below' === $settings['title_position']); - // Rendu HTML echo '
'; + foreach ($terms as $term) { - $thumbnail_id = get_term_meta($term->term_id, 'thumbnail_id', true); - echo '
'; - echo ''; - if (esc_url($term->_image_url)) { + $thumb_id = get_term_meta($term->term_id, 'thumbnail_id', true); + $link = get_term_link($term); + if (is_wp_error($link)) { + continue; + } + + // Ouvre la carte et ajoute la classe si titre en dessous + $card_class = 'category-card' . ($is_below ? ' title-below' : ''); + echo ''; - } - echo '
'; - } + // Titre (overlay ou below) **à l’intérieur** de la carte + if ($is_below) { + echo '
'; + echo '' . esc_html($term->name) . ''; + echo '
'; + } else { + echo '
' . esc_html($term->name) . '
'; + } + + echo '
'; // .category-card + } + + echo ''; // .elementor-category-grid + } }