17 Commits e5470ee30e ... b145bdd4b0

Author SHA1 Message Date
  Sangfroid b145bdd4b0 Correction du bug de click sur le bouton deleteLink 1 month ago
  Sangfroid 6f22c1fb54 Suppression d'un dump 1 month ago
  Sangfroid 3cb385e9a6 Petite correction champs disabled quand existant 1 month ago
  Sangfroid eb75eaae19 Modif bordure autocomplete 1 month ago
  Sangfroid 49a58d0798 Couleur de fond du footer dark/light 1 month ago
  Sangfroid 5bebe2fd1d Suppression de module js inutiles 1 month ago
  Sangfroid b1b8e08977 Couleur de fond aucomplete compatible dark/light 1 month ago
  Sangfroid ec53a041c9 Read Only sur champs déjà enregistrés 1 month ago
  Sangfroid bb2785e71f Autocomplete presque fonctionnel 1 month ago
  Sangfroid 195ecab084 Pas d'autocomplete si résultat requete à 0 1 month ago
  Sangfroid 8c8f627f15 Merge branch 'develop' into form-js 1 month ago
  Sangfroid c4701fd0f2 Autocomplete et addCollection en vanilla 1 month ago
  François Drouhard 50073b4180 Ménage fichiers js 2 months ago
  François Drouhard eaabfc25ea Merge branch 'improvement/views' into test-js 2 months ago
  François Drouhard ffd2c69f81 Merge branch 'develop' into test-js 2 months ago
  François Drouhard dff34e3a4f Scripts changemet état en js pur 2 months ago
  François Drouhard cd79cf3543 suppression jquery pour changeView 2 months ago

+ 6 - 11
assets/app.js

@@ -5,26 +5,21 @@ import './styles/app.scss';
 import 'bootstrap';
 import './bootstrap';
 import 'font-awesome/css/font-awesome.css';
-import 'jquery-ui/themes/base/all.css';
-import 'jquery-ui/ui/widgets/autocomplete';
 import 'bootstrap-star-rating';
 import 'bootstrap-star-rating/locales/fr'
 import 'bootstrap-star-rating/js/locales/fr'
 import 'bootstrap-star-rating/css/star-rating.css';
 import 'bootstrap-star-rating/themes/krajee-fa/theme';
 import 'bootstrap-star-rating/themes/krajee-fa/theme.css';
-import './js/suivifilms2';
-import './js/addCollectionWidget';
 import './js/filtre';
-import './js/switchView';
 import './js/colorModeBootstrap'
+import { switchView } from './js/switchViewVanilla';
+import { switchEtat } from './js/switchEtat';
+import './js/addCollectionVanilla'
 
-jQuery(function() {
-    $('.add-another-collection-widget').addCollection();
-
-    $('[data-fonction="switch"]').switchEtat();
+const sv = switchView("#switchview");
+const se = switchEtat ('[data-fonction="switch"]');
 
+jQuery(function() {
     $('[data-toggle="star-filter"]').filtreParNote();
-
-    $('#switchview').switchView();
 })

+ 59 - 0
assets/js/addCollectionVanilla.js

@@ -0,0 +1,59 @@
+import { autocomplete } from './autocomplete'
+
+(() => {
+    document
+        .querySelectorAll('ul.collection-form li')
+        .forEach(item => {
+            item.querySelector('input').disabled = true
+            addDeleteLink(item)
+        });
+
+    document
+    .querySelectorAll('.add_item_link')
+    .forEach(btn => {
+        btn.addEventListener("click", addFormToCollection)
+    });
+
+    function addFormToCollection(e) {
+        const collectionHolder = document.querySelector('#' + e.currentTarget.dataset.collectionHolderId);
+        const path = e.currentTarget.dataset.path ?? '';
+    
+        const item = document.createElement('li');
+        item.className = collectionHolder.dataset.classLi
+    
+        item.innerHTML = collectionHolder
+        .dataset
+        .prototype
+        .replace(
+            /__name__/g,
+            collectionHolder.dataset.index
+        );
+
+        if (path !== '') {
+            autocomplete(item, path);
+        }
+
+        collectionHolder.appendChild(item);
+
+        addDeleteLink(item);
+    
+        collectionHolder.dataset.index++;
+    };
+
+    function addDeleteLink(item) {
+        const input = item.querySelector('input') ?? item.querySelector('select');
+        const deleteLink = document.createElement('button');
+        deleteLink.type = 'button';
+        deleteLink.className = 'btn btn-outline-secondary';
+        const i = document.createElement('i');
+        i.className = 'fa fa-trash fa-lg text-danger';
+        deleteLink.appendChild(i);
+
+        deleteLink.addEventListener("click", (event) => {
+            item.remove();
+            event.preventDefault(); // évite qu'un # apparaisse dans l'URL
+        });
+
+        input.after(deleteLink);
+    };
+})();

+ 0 - 90
assets/js/addCollectionWidget.js

@@ -1,90 +0,0 @@
-(function ($) {
-    $.fn.addCollection = function() {
-        function requete(chemin, handleData) {
-            $.ajax({
-                url: chemin,
-                type: 'POST',
-                dataType: 'json',
-                success: function (reponse) {
-                    let reponseArray = parseJson(reponse);
-                    handleData(reponseArray);
-                }
-            })
-        }
-        
-        function parseJson(data) {
-            let tableau = [];
-            for (let i = 0; i < data.length; i++) {
-                let UnObjetJson = data[i];
-                let obj = JSON.parse(UnObjetJson);
-                sortie = Object.values(obj)
-                tableau.push(sortie[0]);
-            }
-            return tableau;
-        }
-
-        function addDeleteLink($prototype) {
-            // Création du lien
-            let $deleteLink = $('<button type="button" class="btn btn-outline-secondary"><i class="fa fa-trash fa-lg text-danger"></i></button>');
-    
-            // Ajout du lien
-            $prototype.find(":input").after($deleteLink);
-    
-            // Ajout du listener sur le clic du lien pour effectivement supprimer la catégorie
-            $deleteLink.on("click", function(e) {
-                $prototype.remove();
-    
-                e.preventDefault(); // évite qu'un # apparaisse dans l'URL
-                return false;
-            });
-        }
-
-        this.each(function() {
-            // Chemin pour la requête
-            var chemin = $(this).data('path');
-
-            // On récupère le fieldset pour ajouter les delete-link aux éléments existants
-            var $container = $($(this).attr("data-list-selector"));
-            $li = $container.find('li');
-            $li.each(function() {
-                if (chemin !== "") {
-                    $(this).find(':input').prop("readonly", true);
-                }
-                addDeleteLink($(this));
-            })
-      
-            $(this).on("click", function(e) {
-                e.preventDefault();
-                var list = jQuery(jQuery(this).attr('data-list-selector'));
-                // Try to find the counter of the list or use the length of the list
-                var counter = list.data('widget-counter') | list.children().length;
-
-                // grab the prototype template
-                var newWidget = list.attr('data-prototype');
-                // replace the "__name__" used in the id and name of the prototype
-                // with a number that's unique to your emails
-                // end name attribute looks like name="contact[emails][2]"
-                newWidget = newWidget.replace(/__name__/g, counter);
-                // Increase the counter
-                counter++;
-                // And store it, the length cannot be used if deleting widgets is allowed
-                list.data('widget-counter', counter);
-                // create a new list element and add it to the list
-                var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
-                
-                if (chemin !== "") {
-                    requete (chemin, function(output) {
-                        newElem.find(':input').autocomplete({
-                            source: output
-                        });
-                    });
-                }
-                
-                addDeleteLink(newElem);
-
-                newElem.appendTo(list);
-            })
-        })
-    }
-
-})(jQuery);

+ 140 - 0
assets/js/autocomplete.js

@@ -0,0 +1,140 @@
+export function autocomplete (inputField, path) {
+    const input = inputField.querySelector('input');
+    const inputId = input.id;
+    input.autocomplete = 'off';
+
+    const results = document.createElement('ul');
+    results.className="autocomplete-results";
+
+    let selectedLi = null;
+    let inputValue = '';
+
+    let clickOccured = false;
+
+    const leaveSelectedLi = () => {
+        if (selectedLi !== null) {
+            selectedLi.classList.remove('selected');
+            selectedLi = null;
+        }
+    }
+
+    const selectLi = (element) => {
+        leaveSelectedLi();
+        selectedLi = element;
+        if (element !== null) {
+            selectedLi.classList.add('selected');
+        }
+        return selectedLi;
+    }
+
+    /*input.addEventListener('blur', () => {
+        setTimeout(() => {
+            if (!clickOccured) {
+                results.hidden = true;
+            }
+        },500);
+        clickOccured = false;
+    });*/
+
+    document.addEventListener('click', () => {
+        results.hidden = true;
+    })
+
+    input.addEventListener('keydown', (e) => {
+        if (e.key === 'Enter') {
+            e.preventDefault();
+            if (selectedLi !== null) {
+                selectedLi.click();
+            }
+        }
+        const liste = results.querySelectorAll('li');
+        if(liste.length === 0) {
+            return;
+        } else {
+            results.hidden = false;
+        }
+        if (e.key === 'ArrowDown') {
+            e.preventDefault();
+            if (selectedLi === null) {
+                input.value = selectLi(results.querySelector('li')).innerText;
+            } else {
+                selectLi(results.querySelector('#' + inputId + '-ui-' + (Number(selectedLi.dataset.id) + 1)));
+                input.value = selectedLi !== null ? selectedLi.innerText : inputValue
+            }
+        }
+        if(e.key === 'ArrowUp') {
+            e.preventDefault();
+            if (selectedLi === null) {
+                input.value = selectLi(liste[liste.length - 1]).innerText;
+            } else {
+                selectLi(results.querySelector('#' + inputId + '-ui-' + (Number(selectedLi.dataset.id) - 1)));
+                input.value = selectedLi !== null ? selectedLi.innerText : inputValue
+            }
+        }
+    })
+
+    const clearResults = () => {
+        results.innerText = '';
+        results.hidden = true;
+    }
+
+    input.addEventListener('input', (e) => {
+        const term = input.value.trim();
+
+        if (term.length === 0) {
+            // Ne pas envoyer de requête si le champ est vide
+            clearResults();
+            return;
+        }
+
+        // Envoyer une requête Fetch
+        fetch(path+ '?query=' + encodeURIComponent(term), {
+            headers: {
+                'X-Requested-With': 'XMLHttpRequest'
+            }
+        })
+        .then(response => {
+            if (!response.ok) {
+                throw new Error('Erreur lors de la récupération des suggestions');
+            }
+            return response.json();
+        })
+        .then(data => {
+            showResults(data);
+            return true;
+        })
+        .catch(error => {
+            console.error(error);
+            return false;
+        });
+    });
+
+    const showResults = (data) => {
+        inputValue = input.value;
+        results.hidden = false;
+        results.innerHTML = '';
+        let i = 0;
+        leaveSelectedLi();
+        data.forEach(element => {
+            const li = document.createElement('li');
+            li.innerText = element;
+            li.classList.add('autocomplete-item');
+            li.id = inputId + '-ui-' + (++i);
+            li.dataset.id = i;
+            results.appendChild(li);
+            li.addEventListener('mouseover', (e) => {
+                selectLi(e.target);
+            });
+            
+            li.addEventListener('click', (e) => {
+                clickOccured = true;
+                leaveSelectedLi();
+                input.value = e.target.innerText;
+                clearResults();
+            },true)
+        })
+        if (data.length > 0) {
+            input.after(results);
+        }
+    }
+}

+ 0 - 30
assets/js/suivifilms2.js

@@ -1,30 +0,0 @@
-(function ($) {
-    $.fn.switchEtat = function () {
-        this.each(function() {
-            $(this).on("click", function(e) {
-                e.preventDefault();
-                let $icone = $(this).children('i');
-                let contenu = $(this).data('content');
-                let iconeEtatUn = $(this).data('icone-actif');
-                let iconeEtatDeux = $(this).data('icone-inactif');
-                let chemin = $(this).data('path');
-                $.ajax({
-                    type: 'POST',
-                    url: chemin,
-                    data: 'id_film=' + contenu,
-                    success: function (data) {
-                        if ($icone.attr('class') === iconeEtatUn) {
-                            $icone.attr('class', iconeEtatDeux);
-                        } else {
-                            $icone.attr('class', iconeEtatUn);
-                        }
-                        return;
-                    },
-                    complete: function () {
-
-                    }
-                })
-            })
-        })
-    }
-})(jQuery);

+ 40 - 0
assets/js/switchEtat.js

@@ -0,0 +1,40 @@
+export const switchEtat = (selector) => {
+    const buttons = document.querySelectorAll(selector);
+    /** @type (Element) */
+    for (const button of buttons) {
+        button.addEventListener("click", (event) => {
+            event.preventDefault();
+            reqSwitch(button);
+        })
+    }
+
+    function reqSwitch(button) {
+        const icone = button.querySelector('i');
+        const id = button.dataset.content;
+        const state1 = button.dataset.iconeActif;
+        const state2 = button.dataset.iconeInactif;
+        const path = button.dataset.path;
+
+        const req = fetch(
+            path, {
+                method: "PATCH",
+                body: JSON.stringify({"id_film": id})
+            }
+        );
+
+        req
+            .then((response) => {
+                if (!response.ok) {
+                    throw new Error(`Erreur Http ${response.status}`);
+                }
+                return response.json();
+            })
+            .then((json) => {
+                if (icone.className === state1) {
+                    icone.className = state2;
+                } else {
+                    icone.className = state1;
+                }
+            })
+    }
+}

+ 0 - 19
assets/js/switchView.js

@@ -1,19 +0,0 @@
-(function ($) {
-    $.fn.switchView = function () {
-        $(this).on('click', function(e) {
-            e.preventDefault();
-            change();
-        });
-
-        function change () {
-            $.ajax({
-                url: "/changeview",
-                type: 'GET',
-                dataType: 'json',
-                success: function (reponse) {
-                    window.location.reload();
-                }
-            })
-        }
-    }
-})(jQuery);

+ 24 - 0
assets/js/switchViewVanilla.js

@@ -0,0 +1,24 @@
+export function switchView(idSelector) {
+    const button = document.querySelector(idSelector);
+    if (!button) {
+        return
+    }
+    button.addEventListener("click", (event) => {
+        event.preventDefault();
+        change();
+    })
+    
+    function change() {
+        const fetchPromise = fetch("/changeview");
+        fetchPromise
+            .then((response) => {
+                if (!response.ok) {
+                    throw new Error(`Erreur Http : ${response.status}`);
+                }
+                return response.json();
+            })
+            .then((json) => {
+                window.location.reload();
+            })
+    }
+}

+ 31 - 1
assets/styles/app.scss

@@ -25,4 +25,34 @@ body {
     margin-bottom: 10px;
     border-top-left-radius: 0;
     border-top-right-radius: 0;
-}
+}
+
+.autocomplete-results {
+    position: absolute;
+    top: 100%;
+    left: 0;
+    right: 0;
+    width: 100%;
+    border-radius: 9px;
+    border: 1px solid var(--bs-border-color);
+    border-top: none;
+    list-style-type: none;
+    padding-left: 0;
+    cursor: pointer;
+    z-index: 99;
+    background-color: var(--bs-body-bg);
+    color: var(--bs-body-color);
+}
+
+.autocomplete-item {
+    padding-left: 1rem;
+}
+
+.autocomplete-item:active, .selected {
+    background-color: $primary;
+}
+
+#footer {
+    background-color: var(--bs-body-bg);
+    color: var(--bs-body-color);
+}

+ 3 - 4
package.json

@@ -3,15 +3,14 @@
         "@babel/core": "^7.17.0",
         "@babel/preset-env": "^7.16.0",
         "@hotwired/stimulus": "^3.0.0",
-        "@popperjs/core": "^2.10.2",
+        "@popperjs/core": "^2.11.8",
         "@symfony/stimulus-bridge": "^3.2.1",
         "@symfony/webpack-encore": "^4.0.0",
         "bootstrap": "^5.3.3",
         "bootstrap-star-rating": "^4.1.2",
-        "core-js": "^3.23.0",
+        "core-js": "^3.36.1",
         "font-awesome": "^4.7.0",
-        "jquery": "^3.6.0",
-        "jquery-ui": "^1.13.0",
+        "jquery": "^3.7.1",
         "regenerator-runtime": "^0.13.9",
         "sass": "^1.55.0",
         "sass-loader": "^13.0.0",

+ 11 - 9
src/Controller/VideothequeController.php

@@ -188,28 +188,30 @@ class VideothequeController extends AbstractController
     }
 
     #[Route("/ajax_req_realisateurs", name: "videotheque_ajax_realisateurs")]
-    public function ajaxRealisateurs(RealisateurRepository $repo, SerializerInterface $serializer): Response
+    public function ajaxRealisateurs(Request $request, RealisateurRepository $repo): Response
     {
-        $realisateurs = $repo->findNomsComplets();
+        $query = $request->query->get('query');
+        $realisateurs = $repo->findRealisateurLike($query);
         $liste = array();
-        foreach ($realisateurs as $key=>$nom)
+        foreach ($realisateurs as $realisateur)
         {
-            $liste[$key] = $serializer->serialize($nom, 'json');
+            $liste[] = $realisateur->getNomComplet();
         }
 
         return new JsonResponse($liste);
     }
 
     #[Route("/ajax_req_genres", name: "videotheque_ajax_genres")]
-    public function ajaxGenres(GenreRepository $repo, SerializerInterface $serializer): Response
+    public function ajaxGenres(Request $request, GenreRepository $repo): Response
     {
-        $genres = $repo->findGenres();
+        $query = $request->query->get('query');
+        $genres = $repo->findGenreLike($query);
         $liste = array();
-        foreach ($genres as $key=>$nom)
+        foreach ($genres as $genre)
         {
-            $liste[$key] = $serializer->serialize($nom, 'json');
+            $liste[] = $genre->getName();
         }
-
+        
         return new JsonResponse($liste);
     }
 }

+ 9 - 15
src/Controller/VideothequePersonnelleController.php

@@ -14,7 +14,7 @@ use Symfony\Component\HttpFoundation\Response;
 
 class VideothequePersonnelleController extends AbstractController
 {
-	#[Route("/maliste/", name:"videothequepersonnelle_maliste")]
+	#[Route("/maliste", name:"videothequepersonnelle_maliste")]
 	public function maListeAction (Request $request, FilmRepository $repo, OptionsManager $options): Response
     {
         $films = $repo->findTousFavoritesByUser($this->getUser());
@@ -25,31 +25,25 @@ class VideothequePersonnelleController extends AbstractController
         ));
     }
 
-    #[Route("/maliste/modifieravoir/", name:"maliste_modifier_a_voir")]
+    #[Route("/maliste/modifieravoir", name:"maliste_modifier_a_voir", methods: ['PATCH'])]
     public function modifierFilmDansListeAction(Request $request, EntityManagerInterface $em, FilmRepository $repo, FilmManager $filmManager): Response
     {
         $result = null;
-        $film = $repo->find($request->request->get('id_film'));
-        if ($request->isXmlHttpRequest())
-        {
-            $result = $filmManager->inverseUserWantToView($film);
-            $em->flush();
-        }
+        $film = $repo->find($request->toArray()['id_film']);
+        $result = $filmManager->inverseUserWantToView($film);
+        $em->flush();
         /*$resultat = $this->get('serializer')->serialize($film, 'json');*/
         return new JsonResponse((object)['newState' => $result]);
     }
 
-    #[Route("/maliste/modifiervus/", name:"maliste_modifier_vus")]
+    #[Route("/maliste/modifiervus", name:"maliste_modifier_vus", methods: ['PATCH'])]
     public function modifierFilmVusAction(Request $request, FilmRepository $repo, EntityManagerInterface $em, FilmManager $filmManager): Response
     {
         $result = null;
 
-        $film = $repo->find($request->request->get('id_film'));
-        if ($request->isXmlHttpRequest())
-        {
-            $result = $filmManager->inverseUserWhoSeen($film);
-            $em->flush();
-        }
+        $film = $repo->find($request->toArray()['id_film']);
+        $result = $filmManager->inverseUserWhoSeen($film);
+        $em->flush();
         return new JsonResponse((object)['newState' => $result]);
     }
 

+ 11 - 0
src/Repository/GenreRepository.php

@@ -25,6 +25,7 @@ class GenreRepository extends ServiceEntityRepository
     {
         parent::__construct($registry, Genre::class);   
     }
+
     public function findGenres ()
     {
         $qb = $this->createQueryBuilder('g');
@@ -34,4 +35,14 @@ class GenreRepository extends ServiceEntityRepository
 
         return $query->getArrayResult();
     }
+
+    public function findGenreLike(?string $query = null): ?array
+    {
+        $qb = $this->createQueryBuilder('g');
+        $qb
+            ->where($qb->expr()->like('g.name', ':genreName'))
+            ->setParameter('genreName', '%'.$query.'%')
+        ;
+        return $qb->getQuery()->getResult();
+    }
 }

+ 10 - 0
src/Repository/RealisateurRepository.php

@@ -87,4 +87,14 @@ class RealisateurRepository extends ServiceEntityRepository
 
         return $query->getArrayResult();
     }
+
+    public function findRealisateurLike(?string $query = null): ?array
+    {
+        $qb = $this->createQueryBuilder('r');
+        $qb
+            ->where($qb->expr()->like('r.nomComplet', ':realName'))
+            ->setParameter('realName', '%'.$query.'%')
+        ;
+        return $qb->getQuery()->getResult();
+    }
 }

+ 1 - 1
templates/footer.html.twig

@@ -1,4 +1,4 @@
-<div class="text-center fixed-bottom bg-dark" data-bs-theme="dark">
+<div id="footer" class="text-center fixed-bottom">
     <span class="text-muted small">Certaines informations sont issues de l'API de <a href="https://www.themoviedb.org">TMDB</a></span>
     <br><span class="text-muted small">Code source disponible sur <a target="_blank" href="https://gogs.fdlibre.eu/sangfroid/films-symfony-4-2">Gogs</a> - Licence <a target="_blank" href="http://www.wtfpl.net">WTFLP</a></span>
 </div>

+ 22 - 19
templates/videotheque/form.html.twig

@@ -1,4 +1,5 @@
-    {{ form_start(form, {'attr': {'class': ''}}) }}
+    {% set classLi = "list-group-item mb-3" %}
+    {{ form_start(form, {'attr': {'class': ''} }) }}
     <div class="container-fluid">
         <div class="row">
             <div class="col-md">
@@ -24,13 +25,13 @@
                             {{ form_label(form.mediaVideos, 'Bandes annonces') }}
                         </div>
                         <div class="card-body">
-                            <button type="button" id="add_media-video" class="btn btn-link add-another-collection-widget mb-3" data-path="" data-list-selector="#media-video-fields-list">Ajouter une bande annonce</button>
-                            <ul id="media-video-fields-list"
-                                data-prototype="{{ form_widget(form.mediaVideos.vars.prototype)|e }}"
-                                data-widget-tags="{{ '<li class="list-group-item mb-3"></li>'|e }}"
-                                data-widget-counter="{{ form.children|length }}">
+                            <button type="button" id="add_media-video" class="btn btn-link add_item_link mb-3" data-collection-holder-id="media-video">Ajouter une bande annonce</button>
+                            <ul id="media-video" class="collection-form ps-0"
+                                data-index="{{ form.mediaVideos|length > 0 ? form.mediaVideos|last.vars.name + 1 : 0 }}"
+                                data-class-li="{{ classLi }}"
+                                data-prototype="{{ form_widget(form.mediaVideos.vars.prototype)|e('html_attr') }}">
                                 {% for mediaVideoField in form.mediaVideos %}
-                                <li class="list-group-item mb-3">
+                                <li class="{{ classLi }}">
                                     {{ form_widget(mediaVideoField) }}
                                     {{ form_errors(mediaVideoField) }}
                                 </li>
@@ -50,13 +51,14 @@
                         {{ form_label(form.genres, 'Genres') }}
                     </div>
                     <div class="card-body">
-                        <button type="button" id="add_genre" class="btn btn-link add-another-collection-widget mb-3" data-path="{{ path('videotheque_ajax_genres') }}" data-list-selector="#genre-fields-list">Ajouter un genre</button>
-                        <ul id="genre-fields-list"
-                            data-prototype="{{ form_widget(form.genres.vars.prototype)|e }}"
-                            data-widget-tags="{{ '<li class="list-group-item mb-3"></li>'|e }}"
-                            data-widget-counter="{{ form.children|length }}">
+                        <button type="button" id="add_genre" class="btn btn-link add_item_link mb-3" data-path="{{ path('videotheque_ajax_genres') }}" data-collection-holder-id="genre-fields-list">Ajouter un genre</button>
+                        <ul id="genre-fields-list" class="collection-form ps-0"
+                            data-index="{{ form.genres|length > 0 ? form.genres|last.vars.name + 1 : 0 }}"
+                            data-prototype="{{ form_widget(form.genres.vars.prototype)|e('html_attr') }}"
+                            data-class-li="{{ classLi }}"
+                        >
                             {% for genreField in form.genres %}
-                            <li class="list-group-item mb-3">
+                            <li class="{{ classLi }}">
                                 {{ form_widget(genreField) }}
                                 {{ form_errors(genreField) }}
                             </li>
@@ -74,13 +76,14 @@
                         {{ form_label(form.realisateurs, 'Réalisateurs') }}
                     </div>
                     <div class="card-body">
-                        <button type="button" id="add_realisateur" class="btn btn-link add-another-collection-widget mb-3" data-path="{{ path('videotheque_ajax_realisateurs') }}" data-list-selector="#realisateur-fields-list">Ajouter un réalisateur</button>
-                        <ul id="realisateur-fields-list"
-                            data-prototype="{{ form_widget(form.realisateurs.vars.prototype)|e }}"
-                            data-widget-tags="{{ '<li class="list-group-item mb-3"></li>'|e }}"
-                            data-widget-counter="{{ form.children|length }}">
+                        <button type="button" id="add_realisateur" class="btn btn-link add_item_link mb-3" data-path="{{ path('videotheque_ajax_realisateurs') }}" data-collection-holder-id="realisateur-fields-list">Ajouter un réalisateur</button>
+                        <ul id="realisateur-fields-list" class="collection-form ps-0"
+                            data-index="{{ form.realisateurs|length > 0 ? form.realisateurs|last.vars.name + 1 : 0 }}"
+                            data-prototype="{{ form_widget(form.realisateurs.vars.prototype)|e('html_attr') }}"
+                            data-class-li="{{ classLi }}"
+                        >
                             {% for realisateurField in form.realisateurs %}
-                            <li class="list-group-item mb-3">
+                            <li class="{{ classLi }}">
                                 {{ form_widget(realisateurField) }}
                                 {{ form_errors(realisateurField) }}
                             </li>

+ 12 - 19
yarn.lock

@@ -1004,10 +1004,10 @@
     error-stack-parser "^2.0.0"
     string-width "^4.2.3"
 
-"@popperjs/core@^2.10.2":
-  version "2.11.7"
-  resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7"
-  integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==
+"@popperjs/core@^2.11.8":
+  version "2.11.8"
+  resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
+  integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
 
 "@sinclair/typebox@^0.25.16":
   version "0.25.24"
@@ -1860,10 +1860,10 @@ core-js-compat@^3.25.1:
   dependencies:
     browserslist "^4.21.5"
 
-core-js@^3.23.0:
-  version "3.30.0"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.0.tgz#64ac6f83bc7a49fd42807327051701d4b1478dea"
-  integrity sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg==
+core-js@^3.36.1:
+  version "3.36.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.36.1.tgz#c97a7160ebd00b2de19e62f4bbd3406ab720e578"
+  integrity sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==
 
 core-util-is@~1.0.0:
   version "1.0.3"
@@ -2758,17 +2758,10 @@ jest-worker@^29.1.2:
     merge-stream "^2.0.0"
     supports-color "^8.0.0"
 
-jquery-ui@^1.13.0:
-  version "1.13.2"
-  resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.2.tgz#de03580ae6604773602f8d786ad1abfb75232034"
-  integrity sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q==
-  dependencies:
-    jquery ">=1.8.0 <4.0.0"
-
-"jquery@>=1.8.0 <4.0.0", jquery@^3.6.0:
-  version "3.6.4"
-  resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f"
-  integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==
+jquery@^3.7.1:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de"
+  integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==
 
 js-tokens@^4.0.0:
   version "4.0.0"