diff --git a/admin/js/category-image.js b/admin/js/category-image.js
new file mode 100644
index 0000000..044bb8b
--- /dev/null
+++ b/admin/js/category-image.js
@@ -0,0 +1,32 @@
+jQuery( document ).ready( function( $ ) {
+ var frame;
+ // Quand on clique sur "Choisir une image"
+ $( '.ccgw-upload-image' ).on( 'click', function( e ) {
+ e.preventDefault();
+ // Si le frame existe déjà, on l’ouvre
+ if ( frame ) {
+ frame.open();
+ return;
+ }
+ // Créer le frame
+ frame = wp.media({
+ title: ccgw_data.title || 'Choisir une image de catégorie',
+ button: { text: ccgw_data.button || 'Sélectionner' },
+ multiple: false
+ });
+ // Quand on sélectionne une image
+ frame.on( 'select', function() {
+ var attachment = frame.state().get('selection').first().toJSON();
+ $( '#category-image-id' ).val( attachment.id );
+ $( '#category-image-wrapper' ).html( '
' );
+ });
+ frame.open();
+ });
+
+ // Quand on clique sur "Supprimer l’image"
+ $( '.ccgw-remove-image' ).on( 'click', function( e ) {
+ e.preventDefault();
+ $( '#category-image-id' ).val('');
+ $( '#category-image-wrapper' ).html('');
+ });
+});
diff --git a/elementor-category-grid-widget.php b/elementor-category-grid-widget.php
new file mode 100644
index 0000000..d27414b
--- /dev/null
+++ b/elementor-category-grid-widget.php
@@ -0,0 +1,164 @@
+ esc_js( __( 'Sélectionner une image de catégorie', 'category-grid-widget' ) ),
+// 'button' => esc_js( __( 'Sélectionner', 'category-grid-widget' ) ),
+// ]
+// );
+// }
+// add_action( 'admin_enqueue_scripts', 'ccgw_enqueue_media_uploader' );
+/**
+ * Charger les scripts Media Uploader et localiser les données JS
+ * sur les écrans d'ajout ET d'édition de catégorie.
+ */
+function ccgw_enqueue_media_uploader( $hook_suffix ) {
+ // On ne charge que sur edit-tags.php (création) OU term.php (édition)
+ if ( ! in_array( $hook_suffix, [ 'edit-tags.php', 'term.php' ], true ) ) {
+ return;
+ }
+ // Et seulement pour la taxonomie 'category'
+ if ( empty( $_GET['taxonomy'] ) || 'category' !== $_GET['taxonomy'] ) {
+ return;
+ }
+
+ // Charge la librairie media de WP
+ wp_enqueue_media();
+
+ // Ton script JS
+ wp_enqueue_script(
+ 'ccgw-category-image',
+ plugin_dir_url( __FILE__ ) . 'admin/js/category-image.js',
+ [ 'jquery' ],
+ '1.0',
+ true
+ );
+
+ // Variable JS
+ wp_localize_script(
+ 'ccgw-category-image',
+ 'ccgw_data',
+ [
+ 'title' => esc_js( __( 'Sélectionner une image de catégorie', 'category-grid-widget' ) ),
+ 'button' => esc_js( __( 'Sélectionner', 'category-grid-widget' ) ),
+ ]
+ );
+}
+add_action( 'admin_enqueue_scripts', 'ccgw_enqueue_media_uploader' );
+
+
+/**
+ * Affiche le champ d'upload dans le formulaire de création de catégorie.
+ */
+function ccgw_category_image_field( $taxonomy ) { ?>
+
+term_id, 'thumbnail_id', true );
+ $image_url = $image_id ? wp_get_attachment_thumb_url( $image_id ) : '';
+ ?>
+
+ |
+
+ |
+
+
+
+
+ ; ?>)
+
+
+
+
+
+
+ |
+
+register( new \Elementor_Category_Grid_Widget() );
+}
+add_action( 'elementor/widgets/register', 'register_category_grid_widget' );
diff --git a/widgets/category-grid-widget.php b/widgets/category-grid-widget.php
new file mode 100644
index 0000000..a3b1e68
--- /dev/null
+++ b/widgets/category-grid-widget.php
@@ -0,0 +1,272 @@
+start_controls_section(
+ 'section_content',
+ [
+ 'label' => esc_html__( 'Catégories', 'category-grid-widget' ),
+ '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' ),
+ 'type' => \Elementor\Controls_Manager::SELECT2,
+ 'multiple' => true,
+ 'label_block' => true,
+ 'options' => $options,
+ 'description' => esc_html__( 'Sélectionnez les catégories d’articles à afficher.', 'category-grid-widget' ),
+ ]
+ );
+
+ // Contrôle : Inclure les sous-catégories.
+ $this->add_control(
+ 'show_subcategories',
+ [
+ 'label' => esc_html__( 'Afficher les sous-catégories', 'category-grid-widget' ),
+ 'type' => \Elementor\Controls_Manager::SWITCHER,
+ 'label_on' => esc_html__( 'Oui', 'category-grid-widget' ),
+ 'label_off' => esc_html__( 'Non', 'category-grid-widget' ),
+ 'return_value' => 'yes',
+ 'default' => 'yes',
+ 'description' => esc_html__( 'Inclure aussi les sous-catégories des catégories sélectionnées.', 'category-grid-widget' ),
+ ]
+ );
+
+ // Contrôle : Trier par (critère de tri des catégories).
+ $this->add_control(
+ 'order_by',
+ [
+ 'label' => esc_html__( 'Trier par', 'category-grid-widget' ),
+ 'type' => \Elementor\Controls_Manager::SELECT,
+ 'options' => [
+ 'name' => esc_html__( 'Nom (alphabétique)', 'category-grid-widget' ),
+ 'count' => esc_html__( 'Nombre d’articles', 'category-grid-widget' ),
+ 'id' => esc_html__( 'ID (date de création)', 'category-grid-widget' ),
+ ],
+ 'default' => 'name',
+ ]
+ );
+
+ // Contrôle : Ordre croissant/décroissant.
+ $this->add_control(
+ 'order',
+ [
+ 'label' => esc_html__( 'Ordre', 'category-grid-widget' ),
+ 'type' => \Elementor\Controls_Manager::SELECT,
+ 'options' => [
+ 'ASC' => esc_html__( 'Croissant (A-Z / 0-9)', 'category-grid-widget' ),
+ 'DESC' => esc_html__( 'Décroissant (Z-A / 9-0)', 'category-grid-widget' ),
+ ],
+ 'default' => 'ASC',
+ ]
+ );
+
+ $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' ),
+ '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' ),
+ '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' ),
+ 'type' => \Elementor\Controls_Manager::SELECT,
+ 'options' => [
+ 'thumbnail' => esc_html__( 'Miniature (thumbnail)', 'category-grid-widget' ),
+ 'medium' => esc_html__( 'Moyen (medium)', 'category-grid-widget' ),
+ 'large' => esc_html__( 'Grand (large)', 'category-grid-widget' ),
+ 'full' => esc_html__( 'Plein (full)', 'category-grid-widget' ),
+ ],
+ 'default' => 'medium',
+ ]
+ );
+
+ $this->end_controls_section();
+ }
+
+ /**
+ * Générer l'affichage du widget côté front-end.
+ */
+ protected function render(): void {
+ $settings = $this->get_settings_for_display();
+
+ // Récupérer les ID 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 );
+ }
+ 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;
+ if ( ! empty( $settings['show_subcategories'] ) && $settings['show_subcategories'] === 'yes' ) {
+ // Ajouter les sous-catégories de chaque catégorie sélectionnée.
+ foreach ( $selected_ids as $cat_id ) {
+ $child_ids = get_term_children( $cat_id, 'category' );
+ if ( is_array( $child_ids ) && ! empty( $child_ids ) ) {
+ $category_ids_to_show = array_merge( $category_ids_to_show, $child_ids );
+ }
+ }
+ // Éliminer les doublons d'ID au cas où.
+ $category_ids_to_show = array_unique( $category_ids_to_show );
+ }
+
+ // Préparer les arguments de requête pour récupérer les termes.
+ $term_args = [
+ 'taxonomy' => 'category',
+ 'include' => $category_ids_to_show,
+ 'hide_empty' => false,
+ ];
+ // Appliquer les options de tri.
+ if ( ! empty( $settings['order_by'] ) ) {
+ $term_args['orderby'] = $settings['order_by'];
+ }
+ if ( ! empty( $settings['order'] ) ) {
+ $term_args['order'] = $settings['order'];
+ }
+
+ // Obtenir les termes de catégories à afficher.
+ $categories = get_terms( $term_args );
+ if ( is_wp_error( $categories ) || empty( $categories ) ) {
+ return;
+ }
+
+ // Calculer la classe CSS pour le nombre de colonnes (pour usage dans le CSS).
+ $columns = ! empty( $settings['columns'] ) ? (int) $settings['columns'] : 3;
+ $grid_class = 'columns-' . $columns;
+
+ // Début du markup HTML de la grille.
+ echo '';
+ foreach ( $categories as $term ) {
+ $term_name = $term->name;
+ $term_link = get_term_link( $term );
+ if ( is_wp_error( $term_link ) ) {
+ continue; // Ignorer si le lien d'archive pose un problème.
+ }
+ // Récupérer l'image de la catégorie via sa méta 'thumbnail_id' (image mise en avant de la catégorie).
+ $image_url = '';
+ $thumbnail_id = get_term_meta( $term->term_id, 'thumbnail_id', true );
+ if ( $thumbnail_id ) {
+ $size = ! empty( $settings['image_size'] ) ? $settings['image_size'] : 'medium';
+ $image_url = wp_get_attachment_image_url( $thumbnail_id, $size );
+ }
+
+ echo '
'; // .category-card
+ }
+ echo '
'; // .elementor-category-grid
+
+ // Astuce : ajouter du CSS (feuille de style ou balise