Files
elementor-category-grid-widget/widgets/category-grid-widget.php

603 lines
23 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
if (!defined('ABSPATH')) {
exit; // Empêche l'accès direct.
}
use Elementor\Controls_Manager;
use Elementor\Group_Control_Typography;
use Elementor\Group_Control_Box_Shadow;
/**
* Classe Elementor_Category_Grid_Widget.
*
* Widget Elementor personnalisé qui affiche une grille de catégories d'articles.
* Chaque catégorie est affichée sous forme de carte avec son nom, son image et un lien vers son archive.
*/
class Elementor_Category_Grid_Widget extends \Elementor\Widget_Base
{
/**
* Identifiant unique du widget.
*
* @return string Slug du widget.
*/
public function get_name(): string
{
return 'category-grid';
}
/**
* Titre du widget affiché dans l'interface Elementor.
*
* @return string Titre du widget.
*/
public function get_title(): string
{
return esc_html__('Grille de Catégories', 'category-grid-widget-for-elementor');
}
/**
* Icône du widget (bibliothèque d'icônes Elementor).
*
* @return string Classe de l'icône du widget.
*/
public function get_icon(): string
{
return 'eicon-posts-grid';
}
/**
* Catégories Elementor dans lesquelles le widget apparaîtra.
*
* @return array Catégories du panneau Elementor.
*/
public function get_categories(): array
{
return ['general'];
}
/**
* Mots-clés facilitant la recherche du widget.
*
* @return array Mots-clés du widget.
*/
public function get_keywords(): array
{
return ['category', 'catégorie', 'grid', 'grille'];
}
protected function register_style_controls()
{
// 1) Styles de la grille (espacement)
$this->start_controls_section(
'section_style_grid',
[
'label' => esc_html__('Grille', 'category-grid-widget-for-elementor'),
'tab' => Controls_Manager::TAB_STYLE,
]
);
$this->add_responsive_control(
'grid_gap',
[
'label' => esc_html__('Espacement (gap)', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::SLIDER,
'size_units' => ['px', 'em', '%'],
'range' => [
'px' => ['min' => 0, 'max' => 100],
'em' => ['min' => 0, 'max' => 10],
],
'selectors' => [
'{{WRAPPER}} .elementor-category-grid' => 'gap: {{SIZE}}{{UNIT}};',
],
]
);
$this->end_controls_section();
// 2) Styles des cartes (fond, bordure, ombre)
$this->start_controls_section(
'section_style_card',
[
'label' => esc_html__('Carte', 'category-grid-widget-for-elementor'),
'tab' => Controls_Manager::TAB_STYLE,
]
);
$this->add_control(
'card_bg_color',
[
'label' => esc_html__('Fond de la carte', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::COLOR,
'default' => '',
'selectors' => [
'{{WRAPPER}} .category-card' => 'background-color: {{VALUE}};',
],
]
);
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'card_title_typography',
'label' => esc_html__('Typographie du titre', 'category-grid-widget-for-elementor'),
'selector' => '{{WRAPPER}} .category-card-name',
]
);
$this->add_control(
'card_border_radius',
[
'label' => esc_html__('Border radius', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::DIMENSIONS,
'size_units' => ['px', '%'],
'selectors' => [
'{{WRAPPER}} .category-card' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
],
]
);
$this->add_group_control(
\Elementor\Group_Control_Box_Shadow::get_type(),
[
'name' => 'card_box_shadow',
'label' => esc_html__('Ombre de la carte', 'category-grid-widget-for-elementor'),
'selector' => '{{WRAPPER}} .category-card',
]
);
$this->end_controls_section();
// 3) Styles du titre (couleur, arrière-plan du badge)
$this->start_controls_section(
'section_style_title',
[
'label' => esc_html__('Titre', 'category-grid-widget-for-elementor'),
'tab' => Controls_Manager::TAB_STYLE,
]
);
// 3.1) Switcher pour désactiver le fond
$this->add_control(
'disable_title_bg',
[
'label' => esc_html__('Désactiver le fond du titre', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::SWITCHER,
'label_on' => esc_html__('Oui', 'category-grid-widget-for-elementor'),
'label_off' => esc_html__('Non', 'category-grid-widget-for-elementor'),
'return_value' => 'yes',
'default' => 'no',
'selectors' => [
// Quand activé => on rend le fond totalement transparent
'{{WRAPPER}} .category-card-name' => 'background-color: transparent !important;',
],
]
);
// 3.2) Couleur du texte
$this->add_control(
'title_text_color',
[
'label' => esc_html__('Couleur du texte', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::COLOR,
'default' => '#ffffff',
'selectors' => [
'{{WRAPPER}} .category-card-name' => 'color: {{VALUE}};',
],
]
);
// 3.3) Couleur de fond du titre, **uniquement si** le fond n'est pas désactivé
$this->add_control(
'title_bg_color',
[
'label' => esc_html__('Fond du titre', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::COLOR,
'default' => 'rgba(0,0,0,0.4)',
'selectors' => [
'{{WRAPPER}} .category-card-name' => 'background-color: {{VALUE}};',
],
'condition' => [
// n'affiche ce picker que si disable_title_bg != 'yes'
'disable_title_bg!' => 'yes',
],
]
);
// 3.4) Typographie du titre
$this->add_group_control(
Group_Control_Typography::get_type(),
[
'name' => 'card_title_typography',
'label' => esc_html__('Typographie du titre', 'category-grid-widget-for-elementor'),
'selector' => '{{WRAPPER}} .category-card-name',
]
);
$this->end_controls_section();
// 4) Section Hover
$this->start_controls_section(
'section_style_hover',
[
'label' => esc_html__('Hover', 'category-grid-widget-for-elementor'),
'tab' => Controls_Manager::TAB_STYLE,
]
);
// 4.1) Couleur de l'overlay
$this->add_control(
'hover_overlay_color',
[
'label' => esc_html__('Couleur de loverlay', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::COLOR,
'default' => 'rgba(0,0,0,0.3)',
'selectors' => [
// sur lélément ::before (défini en CSS ci-dessous)
'{{WRAPPER}} .category-card::before' => 'background-color: {{VALUE}};',
],
]
);
// 4.2) Opacité de l'overlay
$this->add_control(
'hover_overlay_opacity',
[
'label' => esc_html__('Opacité de loverlay', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::SLIDER,
'size_units' => ['%'],
'range' => [
'%' => ['min' => 0, 'max' => 100],
],
'default' => ['size' => 30],
'selectors' => [
'{{WRAPPER}} .category-card::before' => 'opacity: 0;',
'{{WRAPPER}} .category-card:hover::before' => 'opacity: {{SIZE}}%;',
],
]
);
// 4.3) Couleur du titre au hover
$this->add_control(
'title_hover_color',
[
'label' => esc_html__('Couleur du titre au hover', 'category-grid-widget-for-elementor'),
'type' => Controls_Manager::COLOR,
'default' => '#ffffff',
'selectors' => [
'{{WRAPPER}} .category-card:hover .category-card-name' => 'color: {{VALUE}};',
],
]
);
$this->end_controls_section();
}
/**
* Enregistrer les contrôles du widget (champs configurables dans Elementor).
*/
protected function register_controls(): void
{
/* Section "Contenu" pour le choix des catégories et options de requête */
$this->start_controls_section(
'section_content',
[
'label' => esc_html__('Catégories', 'category-grid-widget-for-elementor'),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
// Contrôle : Sélection des catégories à afficher (multisélection).
$options = [];
$categories = get_categories(['hide_empty' => false]);
foreach ($categories as $cat) {
$options[$cat->term_id] = $cat->name;
}
$this->add_control(
'categories',
[
'label' => esc_html__('Catégories à afficher', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SELECT2,
'multiple' => true,
'label_block' => true,
'options' => $options,
'description' => esc_html__('Sélectionnez les catégories darticles à afficher.', 'category-grid-widget-for-elementor'),
]
);
// Contrôle : Inclure les sous-catégories.
$this->add_control(
'show_subcategories',
[
'label' => esc_html__('Afficher les sous-catégories', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__('Oui', 'category-grid-widget-for-elementor'),
'label_off' => esc_html__('Non', 'category-grid-widget-for-elementor'),
'return_value' => 'yes',
'default' => 'yes',
'description' => esc_html__('Inclure aussi les sous-catégories des catégories sélectionnées.', 'category-grid-widget-for-elementor'),
]
);
$this->add_control(
'only_subcats_of_selected',
[
'label' => esc_html__('Afficher seulement les sous-catégories des catégories sélectionnées', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__('Oui', 'category-grid-widget-for-elementor'),
'label_off' => esc_html__('Non', 'category-grid-widget-for-elementor'),
'return_value' => 'yes',
'default' => 'no',
'description' => esc_html__('Si activé, seules les sous-catégories des catégories choisies seront affichées (et pas les catégories parentes).', 'category-grid-widget-for-elementor'),
]
);
// Contrôle : Afficher les sous-catégories de la page catégorie actuelle
$this->add_control(
'subcats_of_current',
[
'label' => esc_html__('Afficher les sous-catégories de la catégorie actuelle', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__('Oui', 'category-grid-widget-for-elementor'),
'label_off' => esc_html__('Non', 'category-grid-widget-for-elementor'),
'return_value' => 'yes',
'default' => 'no',
'description' => esc_html__('Si activé et que lon est sur une page darchive de catégorie, affichera ses sous-catégories.', 'category-grid-widget-for-elementor'),
]
);
// ----------------------------------------------------------------------
// Contrôle : Ne montrer que les catégories avec image
$this->add_control(
'hide_without_image',
[
'label' => esc_html__('Afficher uniquement catégories avec image', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__('Oui', 'category-grid-widget-for-elementor'),
'label_off' => esc_html__('Non', 'category-grid-widget-for-elementor'),
'return_value' => 'yes',
'default' => 'no',
'description' => esc_html__('Si activé, les catégories sans image seront masquées.', 'category-grid-widget-for-elementor'),
]
);
// Contrôle : Trier par (critère de tri des catégories).
$this->add_control(
'order_by',
[
'label' => esc_html__('Trier par', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
'name' => esc_html__('Nom (alphabétique)', 'category-grid-widget-for-elementor'),
'count' => esc_html__('Nombre darticles', 'category-grid-widget-for-elementor'),
'id' => esc_html__('ID (date de création)', 'category-grid-widget-for-elementor'),
],
'default' => 'name',
]
);
// Contrôle : Ordre croissant/décroissant.
$this->add_control(
'order',
[
'label' => esc_html__('Ordre', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
'ASC' => esc_html__('Croissant (A-Z / 0-9)', 'category-grid-widget-for-elementor'),
'DESC' => esc_html__('Décroissant (Z-A / 9-0)', 'category-grid-widget-for-elementor'),
],
'default' => 'ASC',
]
);
$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();
/* Section "Mise en page" pour les options d'affichage (colonnes, taille d'image) */
$this->start_controls_section(
'section_layout',
[
'label' => esc_html__('Mise en page', 'category-grid-widget-for-elementor'),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
// Contrôle : Nombre de colonnes de la grille.
$this->add_control(
'columns',
[
'label' => esc_html__('Nombre de colonnes', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
1 => '1',
2 => '2',
3 => '3',
4 => '4',
5 => '5',
6 => '6',
],
'default' => 3,
]
);
// Contrôle : Taille des images affichées.
$this->add_control(
'image_size',
[
'label' => esc_html__('Taille des images', 'category-grid-widget-for-elementor'),
'type' => \Elementor\Controls_Manager::SELECT,
'options' => [
'thumbnail' => esc_html__('Miniature (thumbnail)', 'category-grid-widget-for-elementor'),
'medium' => esc_html__('Moyen (medium)', 'category-grid-widget-for-elementor'),
'large' => esc_html__('Grand (large)', 'category-grid-widget-for-elementor'),
'full' => esc_html__('Plein (full)', 'category-grid-widget-for-elementor'),
],
'default' => 'medium',
]
);
$this->end_controls_section();
$this->register_style_controls();
}
/**
* Générer l'affichage du widget côté front-end.
*/
protected function render(): void
{
$settings = $this->get_settings_for_display();
// 1) Récupérer les IDs des catégories sélectionnées
$selected_ids = [];
if (!empty($settings['categories'])) {
$selected_ids = (array) $settings['categories'];
$selected_ids = array_map('absint', $selected_ids);
}
if (empty($selected_ids)) {
return;
}
// 2) Construire la liste des IDs à afficher
if ('yes' === $settings['subcats_of_current']) {
$queried = get_queried_object();
if ($queried instanceof WP_Term && 'category' === $queried->taxonomy) {
$category_ids_to_show = get_term_children($queried->term_id, 'category');
} else {
return;
}
} else {
$category_ids_to_show = $selected_ids;
if ('yes' === $settings['only_subcats_of_selected']) {
$subs = [];
foreach ($selected_ids as $cat_id) {
$children = get_term_children($cat_id, 'category');
if (is_array($children)) {
$subs = array_merge($subs, $children);
}
}
$category_ids_to_show = array_unique($subs);
} elseif ('yes' === $settings['show_subcategories']) {
foreach ($selected_ids as $cat_id) {
$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);
}
}
// 3) Préparer args pour get_terms()
$term_args = [
'taxonomy' => 'category',
'include' => $category_ids_to_show,
'hide_empty' => false,
];
if ('count' !== $settings['order_by']) {
$term_args['orderby'] = $settings['order_by'];
$term_args['order'] = $settings['order'];
}
// 4) Récupérer et filtrer les termes
$raw_terms = get_terms($term_args);
if (is_wp_error($raw_terms) || empty($raw_terms)) {
return;
}
$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']) : '';
if ('yes' === $settings['hide_without_image'] && empty($image_url)) {
continue;
}
$term->_image_url = $image_url;
$terms[] = $term;
}
if (empty($terms)) {
return;
}
// 5) Tri manuel si order_by=count
if ('count' === $settings['order_by']) {
$counts = [];
foreach ($terms as $term) {
$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;
}
usort($terms, function ($a, $b) use ($counts, $settings) {
$ca = $counts[$a->term_id];
$cb = $counts[$b->term_id];
if ($ca === $cb) {
return 0;
}
if ('ASC' === $settings['order']) {
return $ca < $cb ? -1 : 1;
}
return $ca > $cb ? -1 : 1;
});
}
// 6) Affichage final
$grid_class = 'columns-' . absint($settings['columns']);
$is_below = ('below' === $settings['title_position']);
echo '<div class="elementor-category-grid ' . esc_attr($grid_class) . '">';
foreach ($terms as $term) {
$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 '<div class="' . esc_attr($card_class) . '">';
// Image cliquable
echo '<a href="' . esc_url($link) . '">';
if ($thumb_id) {
echo '<div class="category-card-image">';
echo wp_get_attachment_image(
$thumb_id,
$settings['image_size'],
false,
[
'alt' => esc_attr($term->name),
'class' => 'category-card-img',
]
);
echo '</div>';
}
echo '</a>';
// 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
}
}