From 7512050d56e20d87572ee353a6463f61e773c7f1 Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 15:24:17 +0200 Subject: [PATCH 01/10] add confirmation modal --- assets/js/app.js | 11 +++++++++++ src/Config/Action.php | 7 +++++++ src/Dto/ActionDto.php | 16 ++++++++++++++++ src/Factory/ActionFactory.php | 8 ++++++++ templates/crud/action.html.twig | 4 ++-- templates/crud/action_group.html.twig | 2 +- templates/crud/detail.html.twig | 12 ++++++++++++ templates/crud/edit.html.twig | 12 ++++++++++++ .../includes/_confirm_action_modal.html.twig | 18 ++++++++++++++++++ templates/crud/index.html.twig | 12 ++++++++++++ translations/EasyAdminBundle.ar.php | 5 +++++ translations/EasyAdminBundle.bg.php | 5 +++++ translations/EasyAdminBundle.ca.php | 5 +++++ translations/EasyAdminBundle.cs.php | 5 +++++ translations/EasyAdminBundle.da.php | 5 +++++ translations/EasyAdminBundle.de.php | 5 +++++ translations/EasyAdminBundle.el.php | 5 +++++ translations/EasyAdminBundle.en.php | 5 +++++ translations/EasyAdminBundle.es.php | 5 +++++ translations/EasyAdminBundle.eu.php | 5 +++++ translations/EasyAdminBundle.fa.php | 5 +++++ translations/EasyAdminBundle.fi.php | 5 +++++ translations/EasyAdminBundle.fr.php | 5 +++++ translations/EasyAdminBundle.gl.php | 5 +++++ translations/EasyAdminBundle.he.php | 5 +++++ translations/EasyAdminBundle.hr.php | 5 +++++ translations/EasyAdminBundle.hu.php | 5 +++++ translations/EasyAdminBundle.hy.php | 5 +++++ translations/EasyAdminBundle.id.php | 5 +++++ translations/EasyAdminBundle.it.php | 5 +++++ translations/EasyAdminBundle.lb.php | 5 +++++ translations/EasyAdminBundle.lt.php | 5 +++++ translations/EasyAdminBundle.mk.php | 5 +++++ translations/EasyAdminBundle.nl.php | 5 +++++ translations/EasyAdminBundle.no.php | 5 +++++ translations/EasyAdminBundle.pl.php | 5 +++++ translations/EasyAdminBundle.pt.php | 5 +++++ translations/EasyAdminBundle.pt_BR.php | 5 +++++ translations/EasyAdminBundle.ro.php | 5 +++++ translations/EasyAdminBundle.ru.php | 5 +++++ translations/EasyAdminBundle.sk.php | 5 +++++ translations/EasyAdminBundle.sl.php | 5 +++++ translations/EasyAdminBundle.sr_RS.php | 5 +++++ translations/EasyAdminBundle.sv.php | 5 +++++ translations/EasyAdminBundle.tr.php | 5 +++++ translations/EasyAdminBundle.uk.php | 5 +++++ translations/EasyAdminBundle.zh_CN.php | 5 +++++ translations/EasyAdminBundle.zh_TW.php | 5 +++++ 48 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 templates/crud/includes/_confirm_action_modal.html.twig diff --git a/assets/js/app.js b/assets/js/app.js index 6c792ed339..fcde1a42cf 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -30,6 +30,7 @@ class App { this.#createAutoCompleteFields(); this.#createBatchActions(); this.#createModalWindowsForDeleteActions(); + this.#createModalWindowsFormConfirmationActions(); this.#createPopovers(); this.#createTooltips(); @@ -396,6 +397,16 @@ class App { }); } + #createModalWindowsFormConfirmationActions() { + const modalTitles = document.querySelectorAll('.action-confirmation-title'); + modalTitles.forEach((modalTitle) => { + const titleContentWithPlaceholders = modalTitle.textContent; + const actionName = modalTitle.getAttribute('data-action-title'); + modalTitle.textContent = titleContentWithPlaceholders + .replace('%action_name%', actionName); + }) + } + #createPopovers() { document.querySelectorAll('[data-bs-toggle="popover"]').forEach((popoverElement) => { new bootstrap.Popover(popoverElement); diff --git a/src/Config/Action.php b/src/Config/Action.php index 1b1116fd44..ebd227828c 100644 --- a/src/Config/Action.php +++ b/src/Config/Action.php @@ -123,6 +123,13 @@ public function setIcon(?string $icon): self return $this; } + public function setConfirmationModal(string $modalText): self + { + $this->dto->setConfirmationModal($modalText); + + return $this; + } + /** * Use this to override the default CSS classes applied to actions and use instead your own CSS classes. * See also addCssClass() to add your own custom classes without removing the default ones. diff --git a/src/Dto/ActionDto.php b/src/Dto/ActionDto.php index 78344dbb36..dc4b1f115e 100644 --- a/src/Dto/ActionDto.php +++ b/src/Dto/ActionDto.php @@ -39,6 +39,7 @@ final class ActionDto private ButtonType $butonType = ButtonType::Submit; private ButtonVariant $variant = ButtonVariant::Default; private ButtonStyle $style = ButtonStyle::Solid; + private ?string $modalText = null; public function getType(): string { @@ -357,6 +358,21 @@ public function usesTextStyle(): bool return ButtonStyle::Text === $this->style; } + public function hasConfirmationModal(): bool + { + return isset($this->modalText); + } + + public function setConfirmationModal(string $modalText): void + { + $this->modalText = $modalText; + } + + public function getModalText(): ?string + { + return $this->modalText; + } + /** * @internal */ diff --git a/src/Factory/ActionFactory.php b/src/Factory/ActionFactory.php index 47e1e0c6ba..45148fe1c5 100644 --- a/src/Factory/ActionFactory.php +++ b/src/Factory/ActionFactory.php @@ -185,6 +185,14 @@ private function processAction(string $pageName, ActionDto $actionDto, ?EntityDt } } + if ($actionDto->hasConfirmationModal()) { + $actionDto->addHtmlAttributes([ + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#modal-confirmation-' . $actionDto->getName(), + 'data-action-url' => $actionDto->getLinkUrl(), + ]); + } + if (Action::DELETE === $actionDto->getName()) { $actionDto->addHtmlAttributes([ 'formaction' => $this->adminUrlGenerator->setController($adminContext->getCrud()->getControllerFqcn())->setAction(Action::DELETE)->setEntityId($entityDto->getPrimaryKeyValue())->generateUrl(), diff --git a/templates/crud/action.html.twig b/templates/crud/action.html.twig index f5ddb82c24..bbd7541696 100644 --- a/templates/crud/action.html.twig +++ b/templates/crud/action.html.twig @@ -10,8 +10,8 @@ htmlElement="{{ action.htmlElement }}" type="{{ action.buttonType.value }}" icon="{{ action.icon }}" - href="{{ action.htmlElement.isLink ? action.linkUrl : null }}" - action="{{ action.htmlElement.isForm ? action.linkUrl : null }}" + href="{{ action.hasConfirmationModal == false and action.htmlElement.isLink ? action.linkUrl : null }}" + action="{{ action.hasConfirmationModal == false and action.htmlElement.isForm ? action.linkUrl : null }}" method="{{ action.htmlElement.isForm ? 'POST' : null }}" > {%- if outerScope.action.label is not empty -%}{{ outerScope.action.label|trans|raw }}{%- endif -%} diff --git a/templates/crud/action_group.html.twig b/templates/crud/action_group.html.twig index 13804f1875..362ddcd476 100644 --- a/templates/crud/action_group.html.twig +++ b/templates/crud/action_group.html.twig @@ -55,7 +55,7 @@ label="{{ item.label }}" icon="{{ item.icon }}" class="{{ item.cssClass }} dropdown-item-variant-{{ item.variant.value }}" htmlAttributes="{{ item.htmlAttributes }}" renderAsForm="{{ item.isRenderedAsForm }}" - url="{{ item.linkUrl }}" renderLabelRaw="true" + url="{{ item.hasConfirmationModal == false ? item.linkUrl : null }}" renderLabelRaw="true" showBlankIcons="{{ group.hasAnyActionWithIcon }}" /> {% endif %} diff --git a/templates/crud/detail.html.twig b/templates/crud/detail.html.twig index 0d3d0d38c0..e4ca75a125 100644 --- a/templates/crud/detail.html.twig +++ b/templates/crud/detail.html.twig @@ -65,6 +65,18 @@ {% block delete_form %} {{ include('@EasyAdmin/crud/includes/_delete_form.html.twig', { entity_id: entity.primaryKeyValue }, with_context = false) }} {% endblock delete_form %} + + {% for action in entity.actions %} + {% if action.isActionGroup %} + {% for item in action.items %} + {% if item.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} + {% endif %} + {% endfor %} + {% elseif action.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} + {% endif %} + {% endfor %} {% endblock %} {% macro render_field_contents(entity, field) %} diff --git a/templates/crud/edit.html.twig b/templates/crud/edit.html.twig index 986f5163d7..3cda452e50 100644 --- a/templates/crud/edit.html.twig +++ b/templates/crud/edit.html.twig @@ -67,4 +67,16 @@ {% block delete_form %} {{ include('@EasyAdmin/crud/includes/_delete_form.html.twig', { entity_id: entity.primaryKeyValue }, with_context = false) }} {% endblock delete_form %} + + {% for action in entity.actions %} + {% if action.isActionGroup %} + {% for item in action.items %} + {% if item.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} + {% endif %} + {% endfor %} + {% elseif action.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} + {% endif %} + {% endfor %} {% endblock %} diff --git a/templates/crud/includes/_confirm_action_modal.html.twig b/templates/crud/includes/_confirm_action_modal.html.twig new file mode 100644 index 0000000000..3f6d101a11 --- /dev/null +++ b/templates/crud/includes/_confirm_action_modal.html.twig @@ -0,0 +1,18 @@ + diff --git a/templates/crud/index.html.twig b/templates/crud/index.html.twig index f495e44e9d..7a17f33021 100644 --- a/templates/crud/index.html.twig +++ b/templates/crud/index.html.twig @@ -265,4 +265,16 @@ {% if has_batch_actions %} {{ include('@EasyAdmin/crud/includes/_batch_action_modal.html.twig', {}, with_context = false) }} {% endif %} + + {% for action in entity.actions %} + {% if action.isActionGroup %} + {% for item in action.items %} + {% if item.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} + {% endif %} + {% endfor %} + {% elseif action.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} + {% endif %} + {% endfor %} {% endblock main %} diff --git a/translations/EasyAdminBundle.ar.php b/translations/EasyAdminBundle.ar.php index 128dcd6425..180710f944 100644 --- a/translations/EasyAdminBundle.ar.php +++ b/translations/EasyAdminBundle.ar.php @@ -67,6 +67,11 @@ 'action' => 'استمرار', ], + 'confirmation_modal' => [ + 'title' => 'سوف تقوم بتطبيق الأجراء "%action_name%"', + 'action' => 'استمرار', + ], + 'delete_modal' => [ 'title' => 'هل تريد حذف هذا العنصر؟', 'content' => 'هذا الإجراء غير قابل للإلغاء.', diff --git a/translations/EasyAdminBundle.bg.php b/translations/EasyAdminBundle.bg.php index 44e3b9cb8d..aad38f1ff9 100644 --- a/translations/EasyAdminBundle.bg.php +++ b/translations/EasyAdminBundle.bg.php @@ -67,6 +67,11 @@ 'action' => 'Извърши', ], + 'confirmation_modal' => [ + 'title' => 'Ще приложите действието "%action_name%".', + 'action' => 'Извърши', + ], + 'delete_modal' => [ 'title' => 'Наистина ли желаете да изтриете записа?', 'content' => 'Това действие е необратимо.', diff --git a/translations/EasyAdminBundle.ca.php b/translations/EasyAdminBundle.ca.php index 00941664e0..5a9a9505e3 100644 --- a/translations/EasyAdminBundle.ca.php +++ b/translations/EasyAdminBundle.ca.php @@ -67,6 +67,11 @@ 'action' => 'Continuar', ], + 'confirmation_modal' => [ + 'title' => 'Du vil anvende "%action_name%" handlingen.', + 'action' => 'Continuar', + ], + 'delete_modal' => [ 'title' => 'Realment vols esborrar aquest element?', 'content' => 'Aquesta acció no es pot desfer.', diff --git a/translations/EasyAdminBundle.cs.php b/translations/EasyAdminBundle.cs.php index 7d8bc9868b..9444195bc8 100644 --- a/translations/EasyAdminBundle.cs.php +++ b/translations/EasyAdminBundle.cs.php @@ -67,6 +67,11 @@ 'action' => 'Pokračovat', ], + 'confirmation_modal' => [ + 'title' => 'Opravdu chcete upravit vybrané položky?', + 'action' => 'Pokračovat', + ], + 'delete_modal' => [ 'title' => 'Opravdu chcete smazat tuto položku?', 'content' => 'Tuto akci není možné vrátit zpět.', diff --git a/translations/EasyAdminBundle.da.php b/translations/EasyAdminBundle.da.php index 5b0d071d43..122c7f9af8 100644 --- a/translations/EasyAdminBundle.da.php +++ b/translations/EasyAdminBundle.da.php @@ -67,6 +67,11 @@ 'action' => 'Udfør handling', ], + 'confirmation_modal' => [ + 'title' => 'Du vil anvende "%action_name%" handlingen.', + 'action' => 'Udfør handling', + ], + 'delete_modal' => [ 'title' => 'Er du sikker på du vil slette dette element?', 'content' => 'Denne operation kan ikke fortrydes.', diff --git a/translations/EasyAdminBundle.de.php b/translations/EasyAdminBundle.de.php index 5acf5206b5..0dcb94b258 100644 --- a/translations/EasyAdminBundle.de.php +++ b/translations/EasyAdminBundle.de.php @@ -67,6 +67,11 @@ 'action' => 'Fortfahren', ], + 'confirmation_modal' => [ + 'title' => 'Möchten Sie die ausgewählten Elemente wirklich verändern?', + 'action' => 'Fortfahren', + ], + 'delete_modal' => [ 'title' => 'Soll das Element wirklich gelöscht werden?', 'content' => 'Diese Aktion kann nicht rückgängig gemacht werden.', diff --git a/translations/EasyAdminBundle.el.php b/translations/EasyAdminBundle.el.php index dd6116a9a2..00c8f7a3d5 100644 --- a/translations/EasyAdminBundle.el.php +++ b/translations/EasyAdminBundle.el.php @@ -67,6 +67,11 @@ 'action' => 'Συνεχίστε.', ], + 'confirmation_modal' => [ + 'title' => 'Θα εφαρμόσετε την ενέργεια "%action_name%".', + 'action' => 'Συνεχίστε.', + ], + 'delete_modal' => [ 'title' => 'Θέλετε σίγουρα να διαγράψετε αυτό το αντικείμενο;', 'content' => 'Αυτή η ενέργεια δεν αναιρείται.', diff --git a/translations/EasyAdminBundle.en.php b/translations/EasyAdminBundle.en.php index 866667b0c6..4a0f291cde 100644 --- a/translations/EasyAdminBundle.en.php +++ b/translations/EasyAdminBundle.en.php @@ -67,6 +67,11 @@ 'action' => 'Proceed', ], + 'confirmation_modal' => [ + 'title' => 'You are going to apply the "%action_name%" action.', + 'action' => 'Proceed', + ], + 'delete_modal' => [ 'title' => 'Do you really want to delete this item?', 'content' => 'There is no undo for this operation.', diff --git a/translations/EasyAdminBundle.es.php b/translations/EasyAdminBundle.es.php index 23ab6a104f..218b44c4aa 100644 --- a/translations/EasyAdminBundle.es.php +++ b/translations/EasyAdminBundle.es.php @@ -67,6 +67,11 @@ 'action' => 'Continuar', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => '¿Realmente quieres borrar este elemento?', 'content' => 'Esta acción no se puede deshacer.', diff --git a/translations/EasyAdminBundle.eu.php b/translations/EasyAdminBundle.eu.php index 1e099497be..9522ea14a5 100644 --- a/translations/EasyAdminBundle.eu.php +++ b/translations/EasyAdminBundle.eu.php @@ -67,6 +67,11 @@ 'action' => 'Aurrera', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Ziur zaude elementu hau ezabatu nahi duzula?', 'content' => 'Ekintza hau ezin da desegin.', diff --git a/translations/EasyAdminBundle.fa.php b/translations/EasyAdminBundle.fa.php index c9edd91b20..03279dcf32 100644 --- a/translations/EasyAdminBundle.fa.php +++ b/translations/EasyAdminBundle.fa.php @@ -67,6 +67,11 @@ 'action' => 'ادامه دهید', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'واقعا می‌خواهید این آیتم را حذف کنید؟', 'content' => 'این عملیات غیرقابل بازگشت است.', diff --git a/translations/EasyAdminBundle.fi.php b/translations/EasyAdminBundle.fi.php index de6a320817..a5f49b5ef4 100644 --- a/translations/EasyAdminBundle.fi.php +++ b/translations/EasyAdminBundle.fi.php @@ -67,6 +67,11 @@ // 'action' => '', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Oletko varma että haluat poistaa tämän?', 'content' => 'Toimintoa ei voi peruuttaa.', diff --git a/translations/EasyAdminBundle.fr.php b/translations/EasyAdminBundle.fr.php index c1837ab41b..7dac7bb042 100644 --- a/translations/EasyAdminBundle.fr.php +++ b/translations/EasyAdminBundle.fr.php @@ -67,6 +67,11 @@ 'action' => 'Procéder', ], + 'confirmation_modal' => [ + 'title' => 'Vous allez appliquer l\'action "%action_name%".', + 'action' => 'Procéder', + ], + 'delete_modal' => [ 'title' => 'Voulez-vous supprimer cet élément ?', 'content' => 'Cette action est irréversible.', diff --git a/translations/EasyAdminBundle.gl.php b/translations/EasyAdminBundle.gl.php index 63886a0596..836a810faa 100644 --- a/translations/EasyAdminBundle.gl.php +++ b/translations/EasyAdminBundle.gl.php @@ -67,6 +67,11 @@ // 'action' => '', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => '¿Queres realmente borrar este elemento?', 'content' => 'Esta acción non se pode desfacer.', diff --git a/translations/EasyAdminBundle.he.php b/translations/EasyAdminBundle.he.php index 474b00f455..294631378d 100644 --- a/translations/EasyAdminBundle.he.php +++ b/translations/EasyAdminBundle.he.php @@ -67,6 +67,11 @@ 'action' => 'המשך', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'האם הנך בטוח שברצונך למחוק פריט זה?', 'content' => 'לא ניתן לבטל פעולה זו לאחר ביצועה.', diff --git a/translations/EasyAdminBundle.hr.php b/translations/EasyAdminBundle.hr.php index d1514fb3d7..626e0e9379 100644 --- a/translations/EasyAdminBundle.hr.php +++ b/translations/EasyAdminBundle.hr.php @@ -67,6 +67,11 @@ // 'action' => '', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Jeste li sigurni da želite izbrisati ovu stavku?', 'content' => 'Izbrisana stavka se ne može povratiti', diff --git a/translations/EasyAdminBundle.hu.php b/translations/EasyAdminBundle.hu.php index be923dcbe3..77eedae183 100644 --- a/translations/EasyAdminBundle.hu.php +++ b/translations/EasyAdminBundle.hu.php @@ -67,6 +67,11 @@ 'action' => 'Folytatás', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Biztos benne, hogy törli ezt az elemet?', 'content' => 'Ez a művelet visszavonhatatlan.', diff --git a/translations/EasyAdminBundle.hy.php b/translations/EasyAdminBundle.hy.php index 226419bd95..01f107b2ac 100644 --- a/translations/EasyAdminBundle.hy.php +++ b/translations/EasyAdminBundle.hy.php @@ -67,6 +67,11 @@ 'action' => 'Շարունակել', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Խնդրում ենք հաստատել, Դուք իրոք ցանկանում եք հեռացնել', 'content' => 'Այս գործողությունը չի կարող չեղարկվել։', diff --git a/translations/EasyAdminBundle.id.php b/translations/EasyAdminBundle.id.php index 9dcdc1d43e..be11ab2cab 100644 --- a/translations/EasyAdminBundle.id.php +++ b/translations/EasyAdminBundle.id.php @@ -67,6 +67,11 @@ 'action' => 'Proses', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Apakah Anda benar-benar ingin menghapus item ini?', 'content' => 'Tidak ada pembatalan untuk operasi ini.', diff --git a/translations/EasyAdminBundle.it.php b/translations/EasyAdminBundle.it.php index 25b8aa3f11..c258d09fca 100644 --- a/translations/EasyAdminBundle.it.php +++ b/translations/EasyAdminBundle.it.php @@ -67,6 +67,11 @@ 'action' => 'Procedi', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Vuoi eliminare questo elemento?', 'content' => 'Questa azione è irreversibile.', diff --git a/translations/EasyAdminBundle.lb.php b/translations/EasyAdminBundle.lb.php index 8349ce51eb..8a8132e81b 100644 --- a/translations/EasyAdminBundle.lb.php +++ b/translations/EasyAdminBundle.lb.php @@ -67,6 +67,11 @@ 'action' => 'Weidermaachen', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Soll dat Element wierklich geläscht ginn?', 'content' => 'Dës Aktioun kann net réckgängeg gemaach ginn.', diff --git a/translations/EasyAdminBundle.lt.php b/translations/EasyAdminBundle.lt.php index dbfb37ab64..e21e9c6dac 100644 --- a/translations/EasyAdminBundle.lt.php +++ b/translations/EasyAdminBundle.lt.php @@ -67,6 +67,11 @@ 'action' => 'Tęsti', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Ar tikrai norite ištrinti šį elementą?', 'content' => 'Šios operacijos atkurti nebegalėsite.', diff --git a/translations/EasyAdminBundle.mk.php b/translations/EasyAdminBundle.mk.php index 287d47e5ba..8e4d64e626 100644 --- a/translations/EasyAdminBundle.mk.php +++ b/translations/EasyAdminBundle.mk.php @@ -67,6 +67,11 @@ 'action' => 'Продолжи', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Дали навистина сакате да го избришете овој запис?', 'content' => 'За оваа операција нема поништување.', diff --git a/translations/EasyAdminBundle.nl.php b/translations/EasyAdminBundle.nl.php index 0ec572c64e..4fae89a442 100644 --- a/translations/EasyAdminBundle.nl.php +++ b/translations/EasyAdminBundle.nl.php @@ -67,6 +67,11 @@ 'action' => 'Verdergaan', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Weet je zeker dat je dit item wilt verwijderen?', 'content' => 'Deze actie kan niet ongedaan worden gemaakt.', diff --git a/translations/EasyAdminBundle.no.php b/translations/EasyAdminBundle.no.php index 7f78882f01..6067b39786 100644 --- a/translations/EasyAdminBundle.no.php +++ b/translations/EasyAdminBundle.no.php @@ -67,6 +67,11 @@ 'action' => 'Utfør handlinger', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Vil du virkelig slette dette elementet?', 'content' => 'Du kan ikke angre dette valget.', diff --git a/translations/EasyAdminBundle.pl.php b/translations/EasyAdminBundle.pl.php index 285778a871..ad8d1738bf 100644 --- a/translations/EasyAdminBundle.pl.php +++ b/translations/EasyAdminBundle.pl.php @@ -67,6 +67,11 @@ 'action' => 'Wykonaj', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Czy na pewno chcesz usunąć ten element?', 'content' => 'Tej operacji nie można cofnąć.', diff --git a/translations/EasyAdminBundle.pt.php b/translations/EasyAdminBundle.pt.php index 7feed57475..c185bae48d 100644 --- a/translations/EasyAdminBundle.pt.php +++ b/translations/EasyAdminBundle.pt.php @@ -67,6 +67,11 @@ 'action' => 'Proceder', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Tem a certeza que deseja excluir este item?', 'content' => 'Esta operação é irreversível.', diff --git a/translations/EasyAdminBundle.pt_BR.php b/translations/EasyAdminBundle.pt_BR.php index 29ebb811e6..1ae893d1da 100644 --- a/translations/EasyAdminBundle.pt_BR.php +++ b/translations/EasyAdminBundle.pt_BR.php @@ -67,6 +67,11 @@ 'action' => 'Continuar', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Você realmente deseja excluir esse item?', 'content' => 'Não há como desfazer essa operação.', diff --git a/translations/EasyAdminBundle.ro.php b/translations/EasyAdminBundle.ro.php index 9e9e5b2ae9..6acf234b97 100644 --- a/translations/EasyAdminBundle.ro.php +++ b/translations/EasyAdminBundle.ro.php @@ -67,6 +67,11 @@ 'action' => 'Procedează', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Ești sigur că vrei să ștergi acest item?', 'content' => 'Nu există posibilitatea de a reveni asupra acestei decizii.', diff --git a/translations/EasyAdminBundle.ru.php b/translations/EasyAdminBundle.ru.php index d23da4520a..5f624b1260 100644 --- a/translations/EasyAdminBundle.ru.php +++ b/translations/EasyAdminBundle.ru.php @@ -67,6 +67,11 @@ 'action' => 'Продолжить', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Вы действительно хотите удалить этот объект?', 'content' => 'Эту операцию нельзя отменить.', diff --git a/translations/EasyAdminBundle.sk.php b/translations/EasyAdminBundle.sk.php index dd8312c3fe..9f9c4078d5 100644 --- a/translations/EasyAdminBundle.sk.php +++ b/translations/EasyAdminBundle.sk.php @@ -67,6 +67,11 @@ 'action' => 'Pokračovať', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Naozaj chcete vymazať túto položku?', 'content' => 'Táto akcia sa nedá zvrátiť.', diff --git a/translations/EasyAdminBundle.sl.php b/translations/EasyAdminBundle.sl.php index 2d784d45da..5f94f24bb9 100644 --- a/translations/EasyAdminBundle.sl.php +++ b/translations/EasyAdminBundle.sl.php @@ -67,6 +67,11 @@ 'action' => 'Nadaljuj', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Ali res želite izbrisati ta element?', 'content' => 'Razveljavitev za to operacijo ne obstaja.', diff --git a/translations/EasyAdminBundle.sr_RS.php b/translations/EasyAdminBundle.sr_RS.php index 9850331f8a..5164f880e8 100644 --- a/translations/EasyAdminBundle.sr_RS.php +++ b/translations/EasyAdminBundle.sr_RS.php @@ -67,6 +67,11 @@ 'action' => 'Nastavi', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Da li sigurno želite da obrišete ovaj zapis?', 'content' => 'Ova operacija je nepovratna.', diff --git a/translations/EasyAdminBundle.sv.php b/translations/EasyAdminBundle.sv.php index f27c83042a..9d22f77f68 100644 --- a/translations/EasyAdminBundle.sv.php +++ b/translations/EasyAdminBundle.sv.php @@ -67,6 +67,11 @@ // 'action' => '', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Vill du verkligen ta bort detta?', 'content' => 'Du kan inte ångra det här.', diff --git a/translations/EasyAdminBundle.tr.php b/translations/EasyAdminBundle.tr.php index e216b13b72..6e0ccfd416 100644 --- a/translations/EasyAdminBundle.tr.php +++ b/translations/EasyAdminBundle.tr.php @@ -67,6 +67,11 @@ 'action' => 'İlerle', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Bu öğeyi silmek istediğinize emin misiniz?', 'content' => 'Bu işlem geri alınamaz.', diff --git a/translations/EasyAdminBundle.uk.php b/translations/EasyAdminBundle.uk.php index 1154e3d945..048121f99e 100644 --- a/translations/EasyAdminBundle.uk.php +++ b/translations/EasyAdminBundle.uk.php @@ -67,6 +67,11 @@ 'action' => 'Продовжити', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => 'Ви дійсно бажаєте видалити цей об\'єкт?', 'content' => 'Цю дію не можна буде відмінити.', diff --git a/translations/EasyAdminBundle.zh_CN.php b/translations/EasyAdminBundle.zh_CN.php index 996471b9c2..5661d9596c 100644 --- a/translations/EasyAdminBundle.zh_CN.php +++ b/translations/EasyAdminBundle.zh_CN.php @@ -67,6 +67,11 @@ 'action' => '继续', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => '是否删除', 'content' => '是否删除,该操作不可恢复', diff --git a/translations/EasyAdminBundle.zh_TW.php b/translations/EasyAdminBundle.zh_TW.php index 3e9855e598..e73847cc05 100644 --- a/translations/EasyAdminBundle.zh_TW.php +++ b/translations/EasyAdminBundle.zh_TW.php @@ -67,6 +67,11 @@ 'action' => '繼續', ], + 'confirmation_modal' => [ + // 'title' => '', + // 'action' => '', + ], + 'delete_modal' => [ 'title' => '您確定要刪除此項目嗎?', 'content' => '此動作無法復原。', From 52d0729584b4e66227f8e060972d8f989b016894 Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 15:38:52 +0200 Subject: [PATCH 02/10] fix index --- templates/crud/index.html.twig | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/templates/crud/index.html.twig b/templates/crud/index.html.twig index 7a17f33021..998846b580 100644 --- a/templates/crud/index.html.twig +++ b/templates/crud/index.html.twig @@ -196,8 +196,16 @@ {% for action in entity.actions %} {% if action.isActionGroup %} {{ include(action.templatePath, { group: action, entity: entity }, with_context = false) }} + {% for item in action.items %} + {% if item.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} + {% endif %} + {% endfor %} {% else %} {{ include(action.templatePath, { action: action, entity: entity, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }} + {% if action.hasConfirmationModal %} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} + {% endif %} {% endif %} {% endfor %} {% endif %} @@ -265,16 +273,4 @@ {% if has_batch_actions %} {{ include('@EasyAdmin/crud/includes/_batch_action_modal.html.twig', {}, with_context = false) }} {% endif %} - - {% for action in entity.actions %} - {% if action.isActionGroup %} - {% for item in action.items %} - {% if item.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} - {% endif %} - {% endfor %} - {% elseif action.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} - {% endif %} - {% endfor %} {% endblock main %} From dd505a5cf1c0061a00bc40852666069ffc518f1a Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 15:49:37 +0200 Subject: [PATCH 03/10] fix modal confirmation button --- .../crud/includes/_confirm_action_modal.html.twig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/templates/crud/includes/_confirm_action_modal.html.twig b/templates/crud/includes/_confirm_action_modal.html.twig index 3f6d101a11..d1ff7d03a2 100644 --- a/templates/crud/includes/_confirm_action_modal.html.twig +++ b/templates/crud/includes/_confirm_action_modal.html.twig @@ -9,9 +9,15 @@ {{ 'action.cancel'|trans([], 'EasyAdminBundle') }} - - {{ 'batch_action_modal.action'|trans(domain = 'EasyAdminBundle') }} - + + {{ 'confirmation_modal.action'|trans(domain = 'EasyAdminBundle') }} + From f60d3885c88b8bd261cf9eb9788b8155b74eb28d Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 15:52:17 +0200 Subject: [PATCH 04/10] fix include action parameter --- templates/crud/detail.html.twig | 2 +- templates/crud/edit.html.twig | 2 +- templates/crud/index.html.twig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/crud/detail.html.twig b/templates/crud/detail.html.twig index e4ca75a125..6a64d5cb69 100644 --- a/templates/crud/detail.html.twig +++ b/templates/crud/detail.html.twig @@ -74,7 +74,7 @@ {% endif %} {% endfor %} {% elseif action.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context = false) }} {% endif %} {% endfor %} {% endblock %} diff --git a/templates/crud/edit.html.twig b/templates/crud/edit.html.twig index 3cda452e50..729adcf480 100644 --- a/templates/crud/edit.html.twig +++ b/templates/crud/edit.html.twig @@ -76,7 +76,7 @@ {% endif %} {% endfor %} {% elseif action.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context = false) }} {% endif %} {% endfor %} {% endblock %} diff --git a/templates/crud/index.html.twig b/templates/crud/index.html.twig index 998846b580..e1842d55d3 100644 --- a/templates/crud/index.html.twig +++ b/templates/crud/index.html.twig @@ -204,7 +204,7 @@ {% else %} {{ include(action.templatePath, { action: action, entity: entity, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }} {% if action.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context = false) }} {% endif %} {% endif %} {% endfor %} From 611d4bc533d4c23cf2fb1e4733ea9c73241dce78 Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 16:18:13 +0200 Subject: [PATCH 05/10] improve translation --- translations/EasyAdminBundle.cs.php | 2 +- translations/EasyAdminBundle.de.php | 2 +- translations/EasyAdminBundle.es.php | 4 ++-- translations/EasyAdminBundle.eu.php | 4 ++-- translations/EasyAdminBundle.fa.php | 4 ++-- translations/EasyAdminBundle.hu.php | 2 +- translations/EasyAdminBundle.hy.php | 2 +- translations/EasyAdminBundle.id.php | 4 ++-- translations/EasyAdminBundle.it.php | 4 ++-- translations/EasyAdminBundle.lb.php | 2 +- translations/EasyAdminBundle.lt.php | 2 +- translations/EasyAdminBundle.mk.php | 2 +- translations/EasyAdminBundle.nl.php | 2 +- translations/EasyAdminBundle.no.php | 4 ++-- translations/EasyAdminBundle.pl.php | 2 +- translations/EasyAdminBundle.pt.php | 4 ++-- translations/EasyAdminBundle.pt_BR.php | 2 +- translations/EasyAdminBundle.ro.php | 2 +- translations/EasyAdminBundle.ru.php | 2 +- translations/EasyAdminBundle.sk.php | 2 +- translations/EasyAdminBundle.sl.php | 2 +- translations/EasyAdminBundle.sr_RS.php | 2 +- translations/EasyAdminBundle.tr.php | 2 +- translations/EasyAdminBundle.uk.php | 2 +- translations/EasyAdminBundle.zh_CN.php | 2 +- translations/EasyAdminBundle.zh_TW.php | 2 +- 26 files changed, 33 insertions(+), 33 deletions(-) diff --git a/translations/EasyAdminBundle.cs.php b/translations/EasyAdminBundle.cs.php index 9444195bc8..6775eee64b 100644 --- a/translations/EasyAdminBundle.cs.php +++ b/translations/EasyAdminBundle.cs.php @@ -68,7 +68,7 @@ ], 'confirmation_modal' => [ - 'title' => 'Opravdu chcete upravit vybrané položky?', + 'title' => 'Chcete použít akci "%action_name%".', 'action' => 'Pokračovat', ], diff --git a/translations/EasyAdminBundle.de.php b/translations/EasyAdminBundle.de.php index 0dcb94b258..8f834edc92 100644 --- a/translations/EasyAdminBundle.de.php +++ b/translations/EasyAdminBundle.de.php @@ -68,7 +68,7 @@ ], 'confirmation_modal' => [ - 'title' => 'Möchten Sie die ausgewählten Elemente wirklich verändern?', + 'title' => 'Sie möchten die Aktion „%action_name%“ verwenden.', 'action' => 'Fortfahren', ], diff --git a/translations/EasyAdminBundle.es.php b/translations/EasyAdminBundle.es.php index 218b44c4aa..7e7ebf31a5 100644 --- a/translations/EasyAdminBundle.es.php +++ b/translations/EasyAdminBundle.es.php @@ -68,8 +68,8 @@ ], 'confirmation_modal' => [ - // 'title' => '', - // 'action' => '', + 'title' => 'Desea utilizar la acción "%action_name%".', + 'action' => 'Continuar', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.eu.php b/translations/EasyAdminBundle.eu.php index 9522ea14a5..e16343e1e8 100644 --- a/translations/EasyAdminBundle.eu.php +++ b/translations/EasyAdminBundle.eu.php @@ -68,8 +68,8 @@ ], 'confirmation_modal' => [ - // 'title' => '', - // 'action' => '', + 'title' => '"%action_name%" ekintza erabili nahi duzu.', + 'action' => 'Aurrera', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.fa.php b/translations/EasyAdminBundle.fa.php index 03279dcf32..de0413dbfd 100644 --- a/translations/EasyAdminBundle.fa.php +++ b/translations/EasyAdminBundle.fa.php @@ -68,8 +68,8 @@ ], 'confirmation_modal' => [ - // 'title' => '', - // 'action' => '', + 'title' => 'شما قصد دارید اکشن "%action_name%" را اعمال کنید.', + 'action' => 'ادامه دهید', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.hu.php b/translations/EasyAdminBundle.hu.php index 77eedae183..0b232cac27 100644 --- a/translations/EasyAdminBundle.hu.php +++ b/translations/EasyAdminBundle.hu.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Folytatás', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.hy.php b/translations/EasyAdminBundle.hy.php index 01f107b2ac..6ff923d9c6 100644 --- a/translations/EasyAdminBundle.hy.php +++ b/translations/EasyAdminBundle.hy.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Շարունակել', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.id.php b/translations/EasyAdminBundle.id.php index be11ab2cab..52df1fd1c3 100644 --- a/translations/EasyAdminBundle.id.php +++ b/translations/EasyAdminBundle.id.php @@ -68,8 +68,8 @@ ], 'confirmation_modal' => [ - // 'title' => '', - // 'action' => '', + 'title' => 'Anda akan menerapkan tindakan "%action_name%"', + 'action' => 'Proses', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.it.php b/translations/EasyAdminBundle.it.php index c258d09fca..b467570132 100644 --- a/translations/EasyAdminBundle.it.php +++ b/translations/EasyAdminBundle.it.php @@ -68,8 +68,8 @@ ], 'confirmation_modal' => [ - // 'title' => '', - // 'action' => '', + 'title' => 'Stai per applicare l\'azione "%action_name%".', + 'action' => 'Procedi', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.lb.php b/translations/EasyAdminBundle.lb.php index 8a8132e81b..8bae952c04 100644 --- a/translations/EasyAdminBundle.lb.php +++ b/translations/EasyAdminBundle.lb.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Weidermaachen', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.lt.php b/translations/EasyAdminBundle.lt.php index e21e9c6dac..dbe6d8da60 100644 --- a/translations/EasyAdminBundle.lt.php +++ b/translations/EasyAdminBundle.lt.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Tęsti', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.mk.php b/translations/EasyAdminBundle.mk.php index 8e4d64e626..50613ac4ac 100644 --- a/translations/EasyAdminBundle.mk.php +++ b/translations/EasyAdminBundle.mk.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Продолжи', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.nl.php b/translations/EasyAdminBundle.nl.php index 4fae89a442..f2abb9fb79 100644 --- a/translations/EasyAdminBundle.nl.php +++ b/translations/EasyAdminBundle.nl.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Verdergaan', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.no.php b/translations/EasyAdminBundle.no.php index 6067b39786..9c5585d892 100644 --- a/translations/EasyAdminBundle.no.php +++ b/translations/EasyAdminBundle.no.php @@ -68,8 +68,8 @@ ], 'confirmation_modal' => [ - // 'title' => '', - // 'action' => '', + 'title' => 'Du vil utføre "%action_name%" handlingen.', + 'action' => 'Utfør handlinger', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.pl.php b/translations/EasyAdminBundle.pl.php index ad8d1738bf..1727b4e248 100644 --- a/translations/EasyAdminBundle.pl.php +++ b/translations/EasyAdminBundle.pl.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Wykonaj', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.pt.php b/translations/EasyAdminBundle.pt.php index c185bae48d..85dbdc12ba 100644 --- a/translations/EasyAdminBundle.pt.php +++ b/translations/EasyAdminBundle.pt.php @@ -68,8 +68,8 @@ ], 'confirmation_modal' => [ - // 'title' => '', - // 'action' => '', + 'title' => 'Vai aplicar a ação "%action_name%".', + 'action' => 'Proceder', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.pt_BR.php b/translations/EasyAdminBundle.pt_BR.php index 1ae893d1da..4fa6ca833a 100644 --- a/translations/EasyAdminBundle.pt_BR.php +++ b/translations/EasyAdminBundle.pt_BR.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Continuar', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.ro.php b/translations/EasyAdminBundle.ro.php index 6acf234b97..032d4aec55 100644 --- a/translations/EasyAdminBundle.ro.php +++ b/translations/EasyAdminBundle.ro.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Procedează', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.ru.php b/translations/EasyAdminBundle.ru.php index 5f624b1260..b96c2f66a7 100644 --- a/translations/EasyAdminBundle.ru.php +++ b/translations/EasyAdminBundle.ru.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Продолжить', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.sk.php b/translations/EasyAdminBundle.sk.php index 9f9c4078d5..e1dc15dd74 100644 --- a/translations/EasyAdminBundle.sk.php +++ b/translations/EasyAdminBundle.sk.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Pokračovať', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.sl.php b/translations/EasyAdminBundle.sl.php index 5f94f24bb9..35c3469cde 100644 --- a/translations/EasyAdminBundle.sl.php +++ b/translations/EasyAdminBundle.sl.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Nadaljuj', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.sr_RS.php b/translations/EasyAdminBundle.sr_RS.php index 5164f880e8..29520f78a4 100644 --- a/translations/EasyAdminBundle.sr_RS.php +++ b/translations/EasyAdminBundle.sr_RS.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Nastavi', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.tr.php b/translations/EasyAdminBundle.tr.php index 6e0ccfd416..e5aec6ba72 100644 --- a/translations/EasyAdminBundle.tr.php +++ b/translations/EasyAdminBundle.tr.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'İlerle', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.uk.php b/translations/EasyAdminBundle.uk.php index 048121f99e..060683ea7a 100644 --- a/translations/EasyAdminBundle.uk.php +++ b/translations/EasyAdminBundle.uk.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => 'Продовжити', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.zh_CN.php b/translations/EasyAdminBundle.zh_CN.php index 5661d9596c..35342f01d7 100644 --- a/translations/EasyAdminBundle.zh_CN.php +++ b/translations/EasyAdminBundle.zh_CN.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => '继续', ], 'delete_modal' => [ diff --git a/translations/EasyAdminBundle.zh_TW.php b/translations/EasyAdminBundle.zh_TW.php index e73847cc05..8eee7e979a 100644 --- a/translations/EasyAdminBundle.zh_TW.php +++ b/translations/EasyAdminBundle.zh_TW.php @@ -69,7 +69,7 @@ 'confirmation_modal' => [ // 'title' => '', - // 'action' => '', + 'action' => '繼續', ], 'delete_modal' => [ From 321f97fabe23ba8afb1044daf1370a214c4176f6 Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 16:21:16 +0200 Subject: [PATCH 06/10] linter fix --- assets/js/app.js | 3 +-- src/Config/Action.php | 8 ++++---- src/Factory/ActionFactory.php | 14 +++++++------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index fcde1a42cf..dd7b43349e 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -402,8 +402,7 @@ class App { modalTitles.forEach((modalTitle) => { const titleContentWithPlaceholders = modalTitle.textContent; const actionName = modalTitle.getAttribute('data-action-title'); - modalTitle.textContent = titleContentWithPlaceholders - .replace('%action_name%', actionName); + modalTitle.textContent = titleContentWithPlaceholders.replace('%action_name%', actionName); }) } diff --git a/src/Config/Action.php b/src/Config/Action.php index ebd227828c..50cda78575 100644 --- a/src/Config/Action.php +++ b/src/Config/Action.php @@ -124,11 +124,11 @@ public function setIcon(?string $icon): self } public function setConfirmationModal(string $modalText): self - { - $this->dto->setConfirmationModal($modalText); + { + $this->dto->setConfirmationModal($modalText); - return $this; - } + return $this; + } /** * Use this to override the default CSS classes applied to actions and use instead your own CSS classes. diff --git a/src/Factory/ActionFactory.php b/src/Factory/ActionFactory.php index 45148fe1c5..90ec681971 100644 --- a/src/Factory/ActionFactory.php +++ b/src/Factory/ActionFactory.php @@ -185,13 +185,13 @@ private function processAction(string $pageName, ActionDto $actionDto, ?EntityDt } } - if ($actionDto->hasConfirmationModal()) { - $actionDto->addHtmlAttributes([ - 'data-bs-toggle' => 'modal', - 'data-bs-target' => '#modal-confirmation-' . $actionDto->getName(), - 'data-action-url' => $actionDto->getLinkUrl(), - ]); - } + if ($actionDto->hasConfirmationModal()) { + $actionDto->addHtmlAttributes([ + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#modal-confirmation-'.$actionDto->getName(), + 'data-action-url' => $actionDto->getLinkUrl(), + ]); + } if (Action::DELETE === $actionDto->getName()) { $actionDto->addHtmlAttributes([ From 70e91b0d712c864bd9dc37f707f3ffa34f409f62 Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 16:23:56 +0200 Subject: [PATCH 07/10] missing semicolon --- assets/js/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/app.js b/assets/js/app.js index dd7b43349e..e8c21f210e 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -403,7 +403,7 @@ class App { const titleContentWithPlaceholders = modalTitle.textContent; const actionName = modalTitle.getAttribute('data-action-title'); modalTitle.textContent = titleContentWithPlaceholders.replace('%action_name%', actionName); - }) + }); } #createPopovers() { From 79b2d3f12ed86e2a65f1e65079fe0e7fa23a2e4c Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 16:51:40 +0200 Subject: [PATCH 08/10] fix typo --- assets/js/app.js | 913 ++++++++++++++++++++++++++--------------------- 1 file changed, 499 insertions(+), 414 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index e8c21f210e..3813f3912e 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,469 +1,554 @@ // any CSS you require will output into a single css file (app.css in this case) -require('../css/app.css'); +require("../css/app.css"); -import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'; -import Mark from 'mark.js/src/vanilla'; -import Autocomplete from './autocomplete'; -import { toggleVisibilityClasses } from './helpers'; +import bootstrap from "bootstrap/dist/js/bootstrap.bundle"; +import Mark from "mark.js/src/vanilla"; +import Autocomplete from "./autocomplete"; +import { toggleVisibilityClasses } from "./helpers"; // Provide Bootstrap variable globally to allow custom backend pages to use it window.bootstrap = bootstrap; -document.addEventListener('DOMContentLoaded', () => { - window.EasyAdminApp = new App(); +document.addEventListener("DOMContentLoaded", () => { + window.EasyAdminApp = new App(); }); class App { - #sidebarWidthLocalStorageKey; - #contentWidthLocalStorageKey; - - constructor() { - this.#sidebarWidthLocalStorageKey = 'ea/sidebar/width'; - this.#contentWidthLocalStorageKey = 'ea/content/width'; - - this.#removeHashFormUrl(); - this.#createMainMenu(); - this.#createLayoutResizeControls(); - this.#createNavigationToggler(); - this.#createSearchHighlight(); - this.#createFilters(); - this.#createAutoCompleteFields(); - this.#createBatchActions(); - this.#createModalWindowsForDeleteActions(); - this.#createModalWindowsFormConfirmationActions(); - this.#createPopovers(); - this.#createTooltips(); - - document.addEventListener('ea.collection.item-added', () => this.#createAutoCompleteFields()); + #sidebarWidthLocalStorageKey; + #contentWidthLocalStorageKey; + + constructor() { + this.#sidebarWidthLocalStorageKey = "ea/sidebar/width"; + this.#contentWidthLocalStorageKey = "ea/content/width"; + + this.#removeHashFormUrl(); + this.#createMainMenu(); + this.#createLayoutResizeControls(); + this.#createNavigationToggler(); + this.#createSearchHighlight(); + this.#createFilters(); + this.#createAutoCompleteFields(); + this.#createBatchActions(); + this.#createModalWindowsForDeleteActions(); + this.#createModalWindowsForConfirmationActions(); + this.#createPopovers(); + this.#createTooltips(); + + document.addEventListener("ea.collection.item-added", () => + this.#createAutoCompleteFields() + ); + } + + // When using tabs in forms, the selected tab is persisted (in the URL hash) so you + // can see the same tab when reloading the page (e.g. '#tab-contact-information'). + // This method removes the hash from URL in the index page to not show form-related + // information in the index page + #removeHashFormUrl() { + if (!window.location.href.includes("#")) { + return; } - // When using tabs in forms, the selected tab is persisted (in the URL hash) so you - // can see the same tab when reloading the page (e.g. '#tab-contact-information'). - // This method removes the hash from URL in the index page to not show form-related - // information in the index page - #removeHashFormUrl() { - if (!window.location.href.includes('#')) { - return; - } - - // remove the hash only in the index page - if (!document.querySelector('body').classList.contains('ea-index')) { - return; - } - - // don't set the hash to '' because that also removes the query parameters - const urlParts = window.location.href.split('#'); - const urlWithoutHash = urlParts[0]; - window.history.replaceState({}, '', urlWithoutHash); + // remove the hash only in the index page + if (!document.querySelector("body").classList.contains("ea-index")) { + return; } - #createMainMenu() { - // inspired by https://codepen.io/phileflanagan/pen/mwpQpY - const menuItemsWithSubmenus = document.querySelectorAll('#main-menu .menu-item.has-submenu'); - menuItemsWithSubmenus.forEach((menuItem) => { - const menuItemSubmenu = menuItem.querySelector('.submenu'); - if (null === menuItemSubmenu) { - return; + // don't set the hash to '' because that also removes the query parameters + const urlParts = window.location.href.split("#"); + const urlWithoutHash = urlParts[0]; + window.history.replaceState({}, "", urlWithoutHash); + } + + #createMainMenu() { + // inspired by https://codepen.io/phileflanagan/pen/mwpQpY + const menuItemsWithSubmenus = document.querySelectorAll( + "#main-menu .menu-item.has-submenu" + ); + menuItemsWithSubmenus.forEach((menuItem) => { + const menuItemSubmenu = menuItem.querySelector(".submenu"); + if (null === menuItemSubmenu) { + return; + } + + // needed because the menu accordion is based on the max-block-size property. + // visible elements must be initialized with a explicit max-block-size; otherwise + // when you click on them the first time, the animation is not smooth + if (menuItem.classList.contains("expanded")) { + menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; + } + + menuItem + .querySelector(".submenu-toggle") + .addEventListener("click", (event) => { + event.preventDefault(); + + // hide other submenus + menuItemsWithSubmenus.forEach((otherMenuItem) => { + if (menuItem === otherMenuItem) { + return; } - // needed because the menu accordion is based on the max-block-size property. - // visible elements must be initialized with a explicit max-block-size; otherwise - // when you click on them the first time, the animation is not smooth - if (menuItem.classList.contains('expanded')) { - menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; + const otherMenuItemSubmenu = + otherMenuItem.querySelector(".submenu"); + if (otherMenuItem.classList.contains("expanded")) { + otherMenuItemSubmenu.style.maxHeight = "0px"; + otherMenuItem.classList.remove("expanded"); } - - menuItem.querySelector('.submenu-toggle').addEventListener('click', (event) => { - event.preventDefault(); - - // hide other submenus - menuItemsWithSubmenus.forEach((otherMenuItem) => { - if (menuItem === otherMenuItem) { - return; - } - - const otherMenuItemSubmenu = otherMenuItem.querySelector('.submenu'); - if (otherMenuItem.classList.contains('expanded')) { - otherMenuItemSubmenu.style.maxHeight = '0px'; - otherMenuItem.classList.remove('expanded'); - } - }); - - // toggle the state of this submenu - if (menuItem.classList.contains('expanded')) { - menuItemSubmenu.style.maxHeight = '0px'; - menuItem.classList.remove('expanded'); - } else { - menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; - menuItem.classList.add('expanded'); - } - }); + }); + + // toggle the state of this submenu + if (menuItem.classList.contains("expanded")) { + menuItemSubmenu.style.maxHeight = "0px"; + menuItem.classList.remove("expanded"); + } else { + menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; + menuItem.classList.add("expanded"); + } }); + }); + } + + #createLayoutResizeControls() { + const sidebarResizerHandler = document.querySelector( + "#sidebar-resizer-handler" + ); + if (null !== sidebarResizerHandler) { + sidebarResizerHandler.addEventListener("click", () => { + const oldValue = + localStorage.getItem(this.#sidebarWidthLocalStorageKey) || "normal"; + const newValue = "normal" === oldValue ? "compact" : "normal"; + + document + .querySelector("body") + .classList.remove(`ea-sidebar-width-${oldValue}`); + document + .querySelector("body") + .classList.add(`ea-sidebar-width-${newValue}`); + localStorage.setItem(this.#sidebarWidthLocalStorageKey, newValue); + }); } - #createLayoutResizeControls() { - const sidebarResizerHandler = document.querySelector('#sidebar-resizer-handler'); - if (null !== sidebarResizerHandler) { - sidebarResizerHandler.addEventListener('click', () => { - const oldValue = localStorage.getItem(this.#sidebarWidthLocalStorageKey) || 'normal'; - const newValue = 'normal' === oldValue ? 'compact' : 'normal'; - - document.querySelector('body').classList.remove(`ea-sidebar-width-${oldValue}`); - document.querySelector('body').classList.add(`ea-sidebar-width-${newValue}`); - localStorage.setItem(this.#sidebarWidthLocalStorageKey, newValue); - }); - } - - const contentResizerHandler = document.querySelector('#content-resizer-handler'); - if (null !== contentResizerHandler) { - contentResizerHandler.addEventListener('click', () => { - const oldValue = localStorage.getItem(this.#contentWidthLocalStorageKey) || 'normal'; - const newValue = 'normal' === oldValue ? 'full' : 'normal'; - - document.querySelector('body').classList.remove(`ea-content-width-${oldValue}`); - document.querySelector('body').classList.add(`ea-content-width-${newValue}`); - localStorage.setItem(this.#contentWidthLocalStorageKey, newValue); - }); - } + const contentResizerHandler = document.querySelector( + "#content-resizer-handler" + ); + if (null !== contentResizerHandler) { + contentResizerHandler.addEventListener("click", () => { + const oldValue = + localStorage.getItem(this.#contentWidthLocalStorageKey) || "normal"; + const newValue = "normal" === oldValue ? "full" : "normal"; + + document + .querySelector("body") + .classList.remove(`ea-content-width-${oldValue}`); + document + .querySelector("body") + .classList.add(`ea-content-width-${newValue}`); + localStorage.setItem(this.#contentWidthLocalStorageKey, newValue); + }); } + } - #createNavigationToggler() { - const toggler = document.querySelector('#navigation-toggler'); - const cssClassName = 'ea-mobile-sidebar-visible'; - let modalBackdrop; + #createNavigationToggler() { + const toggler = document.querySelector("#navigation-toggler"); + const cssClassName = "ea-mobile-sidebar-visible"; + let modalBackdrop; - if (null === toggler) { - return; - } - - toggler.addEventListener('click', () => { - document.querySelector('body').classList.toggle(cssClassName); - - if (document.querySelector('body').classList.contains(cssClassName)) { - modalBackdrop = document.createElement('div'); - modalBackdrop.classList.add('modal-backdrop', 'fade', 'show'); - modalBackdrop.onclick = () => { - document.querySelector('body').classList.remove(cssClassName); - document.body.removeChild(modalBackdrop); - modalBackdrop = null; - }; - - document.body.appendChild(modalBackdrop); - } else if (modalBackdrop) { - document.body.removeChild(modalBackdrop); - modalBackdrop = null; - } - }); + if (null === toggler) { + return; } - #createSearchHighlight() { - const searchElement = document.querySelector('.form-action-search [name="query"]'); - if (null === searchElement) { - return; - } - - const searchQuery = searchElement.value; - if ('' === searchQuery.trim()) { - return; - } + toggler.addEventListener("click", () => { + document.querySelector("body").classList.toggle(cssClassName); - // splits a string into tokens, taking into account quoted strings - // Example: 'foo "bar baz" qux' => ['foo', 'bar baz', 'qux'] - const tokenizeString = (string) => { - const regex = /"([^"\\]*(\\.[^"\\]*)*)"|\S+/g; - const tokens = []; - - let match = regex.exec(string); - while (null !== match) { - tokens.push(match[0].replaceAll('"', '').trim()); - match = regex.exec(string); - } - - return tokens; + if (document.querySelector("body").classList.contains(cssClassName)) { + modalBackdrop = document.createElement("div"); + modalBackdrop.classList.add("modal-backdrop", "fade", "show"); + modalBackdrop.onclick = () => { + document.querySelector("body").classList.remove(cssClassName); + document.body.removeChild(modalBackdrop); + modalBackdrop = null; }; - const searchQueryTerms = tokenizeString(searchElement.value); - - const elementsToHighlight = document.querySelectorAll('table tbody td.searchable'); - const highlighter = new Mark(elementsToHighlight); - highlighter.mark(searchQueryTerms, { separateWordSearch: false }); + document.body.appendChild(modalBackdrop); + } else if (modalBackdrop) { + document.body.removeChild(modalBackdrop); + modalBackdrop = null; + } + }); + } + + #createSearchHighlight() { + const searchElement = document.querySelector( + '.form-action-search [name="query"]' + ); + if (null === searchElement) { + return; } - #createFilters() { - const filterButton = document.querySelector('.datagrid-filters .action-filters-button'); - if (null === filterButton) { - return; - } - - const filterModal = document.querySelector(filterButton.getAttribute('data-bs-target')); - - // this is needed to avoid errors when connection is slow - filterButton.setAttribute('href', filterButton.getAttribute('data-href')); - filterButton.removeAttribute('data-href'); - filterButton.classList.remove('disabled'); - - filterButton.addEventListener('click', (event) => { - const filterModalBody = filterModal.querySelector('.modal-body'); - filterModalBody.innerHTML = - '
'; - - fetch(filterButton.getAttribute('href')) - .then((response) => { - return response.text(); - }) - .then((text) => { - filterModalBody.innerHTML = text; - this.#createAutoCompleteFields(); - this.#createFilterToggles(); - }) - .catch((error) => { - console.error(error); - }); - - event.preventDefault(); - }); - - const removeFilter = (filterField) => { - filterField - .closest('form') - .querySelectorAll(`input[name^="filters[${filterField.dataset.filterProperty}]"]`) - .forEach((filterFieldInput) => { - filterFieldInput.remove(); - }); - - filterField.remove(); - }; - - document.querySelector('#modal-clear-button').addEventListener('click', () => { - filterModal.querySelectorAll('.filter-field').forEach((filterField) => { - removeFilter(filterField); - }); - filterModal.querySelector('form').submit(); - }); - - document.querySelector('#modal-apply-button').addEventListener('click', () => { - filterModal.querySelectorAll('.filter-checkbox:not(:checked)').forEach((notAppliedFilter) => { - removeFilter(notAppliedFilter.closest('.filter-field')); - }); - filterModal.querySelector('form').submit(); - }); + const searchQuery = searchElement.value; + if ("" === searchQuery.trim()) { + return; } - #createBatchActions() { - let lastUpdatedRowCheckbox = null; - const selectAllCheckbox = document.querySelector('.form-batch-checkbox-all'); - if (null === selectAllCheckbox) { - return; - } - - const rowCheckboxes = document.querySelectorAll('input[type="checkbox"].form-batch-checkbox'); - selectAllCheckbox.addEventListener('change', () => { - rowCheckboxes.forEach((rowCheckbox) => { - rowCheckbox.checked = selectAllCheckbox.checked; - rowCheckbox.dispatchEvent(new Event('change')); - }); - }); - - const deselectAllButton = document.querySelector('.deselect-batch-button'); - if (null !== deselectAllButton) { - deselectAllButton.addEventListener('click', () => { - selectAllCheckbox.checked = false; - selectAllCheckbox.dispatchEvent(new Event('change')); - }); - } - - rowCheckboxes.forEach((rowCheckbox, rowCheckboxIndex) => { - rowCheckbox.dataset.rowIndex = rowCheckboxIndex; - - rowCheckbox.addEventListener('click', (e) => { - if (lastUpdatedRowCheckbox && e.shiftKey) { - const lastIndex = Number.parseInt(lastUpdatedRowCheckbox.dataset.rowIndex); - const currentIndex = Number.parseInt(e.target.dataset.rowIndex); - const valueToApply = e.target.checked; - const lowest = Math.min(lastIndex, currentIndex); - const highest = Math.max(lastIndex, currentIndex); - - rowCheckboxes.forEach((rowCheckbox2, rowCheckboxIndex2) => { - if (lowest <= rowCheckboxIndex2 && rowCheckboxIndex2 <= highest) { - rowCheckbox2.checked = valueToApply; - rowCheckbox2.dispatchEvent(new Event('change')); - } - }); - } - lastUpdatedRowCheckbox = e.target; - }); + // splits a string into tokens, taking into account quoted strings + // Example: 'foo "bar baz" qux' => ['foo', 'bar baz', 'qux'] + const tokenizeString = (string) => { + const regex = /"([^"\\]*(\\.[^"\\]*)*)"|\S+/g; + const tokens = []; + + let match = regex.exec(string); + while (null !== match) { + tokens.push(match[0].replaceAll('"', "").trim()); + match = regex.exec(string); + } + + return tokens; + }; + + const searchQueryTerms = tokenizeString(searchElement.value); + + const elementsToHighlight = document.querySelectorAll( + "table tbody td.searchable" + ); + const highlighter = new Mark(elementsToHighlight); + highlighter.mark(searchQueryTerms, { separateWordSearch: false }); + } + + #createFilters() { + const filterButton = document.querySelector( + ".datagrid-filters .action-filters-button" + ); + if (null === filterButton) { + return; + } - rowCheckbox.addEventListener('change', () => { - const selectedRowCheckboxes = document.querySelectorAll( - 'input[type="checkbox"].form-batch-checkbox:checked' - ); - const row = rowCheckbox.closest('tr'); - const content = rowCheckbox.closest('.content'); - - if (rowCheckbox.checked) { - row.classList.add('selected-row'); - } else { - row.classList.remove('selected-row'); - selectAllCheckbox.checked = false; - } - - const rowsAreSelected = 0 !== selectedRowCheckboxes.length; - const contentTitle = document.querySelector('.content-header-title > .title'); - const filters = content.querySelector('.datagrid-filters'); - const globalActions = content.querySelector('.global-actions'); - const batchActions = content.querySelector('.batch-actions'); - - if (null !== contentTitle) { - toggleVisibilityClasses(contentTitle, rowsAreSelected); - } - if (null !== filters) { - toggleVisibilityClasses(filters, rowsAreSelected); - } - if (null !== globalActions) { - toggleVisibilityClasses(globalActions, rowsAreSelected); - } - if (null !== batchActions) { - toggleVisibilityClasses(batchActions, !rowsAreSelected); - } - }); + const filterModal = document.querySelector( + filterButton.getAttribute("data-bs-target") + ); + + // this is needed to avoid errors when connection is slow + filterButton.setAttribute("href", filterButton.getAttribute("data-href")); + filterButton.removeAttribute("data-href"); + filterButton.classList.remove("disabled"); + + filterButton.addEventListener("click", (event) => { + const filterModalBody = filterModal.querySelector(".modal-body"); + filterModalBody.innerHTML = + '
'; + + fetch(filterButton.getAttribute("href")) + .then((response) => { + return response.text(); + }) + .then((text) => { + filterModalBody.innerHTML = text; + this.#createAutoCompleteFields(); + this.#createFilterToggles(); + }) + .catch((error) => { + console.error(error); }); - const modalTitle = document.querySelector('#batch-action-confirmation-title'); - const titleContentWithPlaceholders = modalTitle.textContent; - - document.querySelectorAll('[data-action-batch]').forEach((dataActionBatch) => { - dataActionBatch.addEventListener('click', (event) => { - event.preventDefault(); - - const actionElement = event.currentTarget; - // There is still a possibility that actionName will remain undefined. The title attribute is not always present on elements with the [data-action-batch] attribute. - const actionName = actionElement.textContent.trim() || actionElement.getAttribute('title'); - const selectedItems = document.querySelectorAll('input[type="checkbox"].form-batch-checkbox:checked'); - modalTitle.textContent = titleContentWithPlaceholders - .replace('%action_name%', actionName) - .replace('%num_items%', selectedItems.length.toString()); - - document.querySelector('#modal-batch-action-button').addEventListener('click', () => { - // prevent double submission of the batch action form - actionElement.setAttribute('disabled', 'disabled'); - - const batchFormFields = { - batchActionName: actionElement.getAttribute('data-action-name'), - entityFqcn: actionElement.getAttribute('data-entity-fqcn'), - batchActionUrl: actionElement.getAttribute('data-action-url'), - batchActionCsrfToken: actionElement.getAttribute('data-action-csrf-token'), - }; - selectedItems.forEach((item, i) => { - batchFormFields[`batchActionEntityIds[${i}]`] = item.value; - }); - - const batchForm = document.createElement('form'); - batchForm.setAttribute('method', 'POST'); - batchForm.setAttribute('action', actionElement.getAttribute('data-action-url')); - for (const fieldName in batchFormFields) { - const formField = document.createElement('input'); - formField.setAttribute('type', 'hidden'); - formField.setAttribute('name', fieldName); - formField.setAttribute('value', batchFormFields[fieldName]); - batchForm.appendChild(formField); - } - - document.body.appendChild(batchForm); - batchForm.submit(); - }); - }); + event.preventDefault(); + }); + + const removeFilter = (filterField) => { + filterField + .closest("form") + .querySelectorAll( + `input[name^="filters[${filterField.dataset.filterProperty}]"]` + ) + .forEach((filterFieldInput) => { + filterFieldInput.remove(); }); - } - #createAutoCompleteFields() { - const autocomplete = new Autocomplete(); - document.querySelectorAll('[data-ea-widget="ea-autocomplete"]').forEach((autocompleteElement) => { - autocomplete.create(autocompleteElement); - }); - } + filterField.remove(); + }; - #createModalWindowsForDeleteActions() { - document.querySelectorAll('[data-action-name="delete"]').forEach((actionElement) => { - actionElement.addEventListener('click', (event) => { - event.preventDefault(); - - document.querySelector('#modal-delete-button').addEventListener('click', () => { - const deleteFormAction = actionElement.getAttribute('formaction'); - const deleteForm = document.querySelector('#delete-form'); - deleteForm.setAttribute('action', deleteFormAction); - deleteForm.submit(); - }); - }); + document + .querySelector("#modal-clear-button") + .addEventListener("click", () => { + filterModal.querySelectorAll(".filter-field").forEach((filterField) => { + removeFilter(filterField); }); + filterModal.querySelector("form").submit(); + }); + + document + .querySelector("#modal-apply-button") + .addEventListener("click", () => { + filterModal + .querySelectorAll(".filter-checkbox:not(:checked)") + .forEach((notAppliedFilter) => { + removeFilter(notAppliedFilter.closest(".filter-field")); + }); + filterModal.querySelector("form").submit(); + }); + } + + #createBatchActions() { + let lastUpdatedRowCheckbox = null; + const selectAllCheckbox = document.querySelector( + ".form-batch-checkbox-all" + ); + if (null === selectAllCheckbox) { + return; } - #createModalWindowsFormConfirmationActions() { - const modalTitles = document.querySelectorAll('.action-confirmation-title'); - modalTitles.forEach((modalTitle) => { - const titleContentWithPlaceholders = modalTitle.textContent; - const actionName = modalTitle.getAttribute('data-action-title'); - modalTitle.textContent = titleContentWithPlaceholders.replace('%action_name%', actionName); - }); + const rowCheckboxes = document.querySelectorAll( + 'input[type="checkbox"].form-batch-checkbox' + ); + selectAllCheckbox.addEventListener("change", () => { + rowCheckboxes.forEach((rowCheckbox) => { + rowCheckbox.checked = selectAllCheckbox.checked; + rowCheckbox.dispatchEvent(new Event("change")); + }); + }); + + const deselectAllButton = document.querySelector(".deselect-batch-button"); + if (null !== deselectAllButton) { + deselectAllButton.addEventListener("click", () => { + selectAllCheckbox.checked = false; + selectAllCheckbox.dispatchEvent(new Event("change")); + }); } - #createPopovers() { - document.querySelectorAll('[data-bs-toggle="popover"]').forEach((popoverElement) => { - new bootstrap.Popover(popoverElement); - }); - } + rowCheckboxes.forEach((rowCheckbox, rowCheckboxIndex) => { + rowCheckbox.dataset.rowIndex = rowCheckboxIndex; + + rowCheckbox.addEventListener("click", (e) => { + if (lastUpdatedRowCheckbox && e.shiftKey) { + const lastIndex = Number.parseInt( + lastUpdatedRowCheckbox.dataset.rowIndex + ); + const currentIndex = Number.parseInt(e.target.dataset.rowIndex); + const valueToApply = e.target.checked; + const lowest = Math.min(lastIndex, currentIndex); + const highest = Math.max(lastIndex, currentIndex); + + rowCheckboxes.forEach((rowCheckbox2, rowCheckboxIndex2) => { + if (lowest <= rowCheckboxIndex2 && rowCheckboxIndex2 <= highest) { + rowCheckbox2.checked = valueToApply; + rowCheckbox2.dispatchEvent(new Event("change")); + } + }); + } + lastUpdatedRowCheckbox = e.target; + }); + + rowCheckbox.addEventListener("change", () => { + const selectedRowCheckboxes = document.querySelectorAll( + 'input[type="checkbox"].form-batch-checkbox:checked' + ); + const row = rowCheckbox.closest("tr"); + const content = rowCheckbox.closest(".content"); + + if (rowCheckbox.checked) { + row.classList.add("selected-row"); + } else { + row.classList.remove("selected-row"); + selectAllCheckbox.checked = false; + } - #createTooltips() { - document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((tooltipElement) => { - new bootstrap.Tooltip(tooltipElement); - }); - } + const rowsAreSelected = 0 !== selectedRowCheckboxes.length; + const contentTitle = document.querySelector( + ".content-header-title > .title" + ); + const filters = content.querySelector(".datagrid-filters"); + const globalActions = content.querySelector(".global-actions"); + const batchActions = content.querySelector(".batch-actions"); - #createFilterToggles() { - document.querySelectorAll('.filter-checkbox').forEach((filterCheckbox) => { - filterCheckbox.addEventListener('change', () => { - const filterToggleLink = filterCheckbox.nextElementSibling; - const filterExpandedAttribute = filterCheckbox.nextElementSibling.getAttribute('aria-expanded'); - - if ( - (filterCheckbox.checked && 'false' === filterExpandedAttribute) || - (!filterCheckbox.checked && 'true' === filterExpandedAttribute) - ) { - filterToggleLink.click(); - } + if (null !== contentTitle) { + toggleVisibilityClasses(contentTitle, rowsAreSelected); + } + if (null !== filters) { + toggleVisibilityClasses(filters, rowsAreSelected); + } + if (null !== globalActions) { + toggleVisibilityClasses(globalActions, rowsAreSelected); + } + if (null !== batchActions) { + toggleVisibilityClasses(batchActions, !rowsAreSelected); + } + }); + }); + + const modalTitle = document.querySelector( + "#batch-action-confirmation-title" + ); + const titleContentWithPlaceholders = modalTitle.textContent; + + document + .querySelectorAll("[data-action-batch]") + .forEach((dataActionBatch) => { + dataActionBatch.addEventListener("click", (event) => { + event.preventDefault(); + + const actionElement = event.currentTarget; + // There is still a possibility that actionName will remain undefined. The title attribute is not always present on elements with the [data-action-batch] attribute. + const actionName = + actionElement.textContent.trim() || + actionElement.getAttribute("title"); + const selectedItems = document.querySelectorAll( + 'input[type="checkbox"].form-batch-checkbox:checked' + ); + modalTitle.textContent = titleContentWithPlaceholders + .replace("%action_name%", actionName) + .replace("%num_items%", selectedItems.length.toString()); + + document + .querySelector("#modal-batch-action-button") + .addEventListener("click", () => { + // prevent double submission of the batch action form + actionElement.setAttribute("disabled", "disabled"); + + const batchFormFields = { + batchActionName: actionElement.getAttribute("data-action-name"), + entityFqcn: actionElement.getAttribute("data-entity-fqcn"), + batchActionUrl: actionElement.getAttribute("data-action-url"), + batchActionCsrfToken: actionElement.getAttribute( + "data-action-csrf-token" + ), + }; + selectedItems.forEach((item, i) => { + batchFormFields[`batchActionEntityIds[${i}]`] = item.value; + }); + + const batchForm = document.createElement("form"); + batchForm.setAttribute("method", "POST"); + batchForm.setAttribute( + "action", + actionElement.getAttribute("data-action-url") + ); + for (const fieldName in batchFormFields) { + const formField = document.createElement("input"); + formField.setAttribute("type", "hidden"); + formField.setAttribute("name", fieldName); + formField.setAttribute("value", batchFormFields[fieldName]); + batchForm.appendChild(formField); + } + + document.body.appendChild(batchForm); + batchForm.submit(); }); }); - - document.querySelectorAll('form[data-ea-filters-form-id]').forEach((form) => { - // TODO: when using the native datepicker, 'change' isn't fired unless you input the entire date + time information - form.addEventListener('change', (event) => { - if (event.target.classList.contains('filter-checkbox')) { - return; - } - - const filterCheckbox = event.target.closest('.filter-field').querySelector('.filter-checkbox'); - if (!filterCheckbox.checked) { - filterCheckbox.checked = true; - } + }); + } + + #createAutoCompleteFields() { + const autocomplete = new Autocomplete(); + document + .querySelectorAll('[data-ea-widget="ea-autocomplete"]') + .forEach((autocompleteElement) => { + autocomplete.create(autocompleteElement); + }); + } + + #createModalWindowsForDeleteActions() { + document + .querySelectorAll('[data-action-name="delete"]') + .forEach((actionElement) => { + actionElement.addEventListener("click", (event) => { + event.preventDefault(); + + document + .querySelector("#modal-delete-button") + .addEventListener("click", () => { + const deleteFormAction = actionElement.getAttribute("formaction"); + const deleteForm = document.querySelector("#delete-form"); + deleteForm.setAttribute("action", deleteFormAction); + deleteForm.submit(); }); }); + }); + } + + #createModalWindowsForConfirmationActions() { + const modalTitles = document.querySelectorAll(".action-confirmation-title"); + modalTitles.forEach((modalTitle) => { + const titleContentWithPlaceholders = modalTitle.textContent; + const actionName = modalTitle.getAttribute("data-action-title"); + modalTitle.textContent = titleContentWithPlaceholders.replace( + "%action_name%", + actionName + ); + }); + } + + #createPopovers() { + document + .querySelectorAll('[data-bs-toggle="popover"]') + .forEach((popoverElement) => { + new bootstrap.Popover(popoverElement); + }); + } + + #createTooltips() { + document + .querySelectorAll('[data-bs-toggle="tooltip"]') + .forEach((tooltipElement) => { + new bootstrap.Tooltip(tooltipElement); + }); + } + + #createFilterToggles() { + document.querySelectorAll(".filter-checkbox").forEach((filterCheckbox) => { + filterCheckbox.addEventListener("change", () => { + const filterToggleLink = filterCheckbox.nextElementSibling; + const filterExpandedAttribute = + filterCheckbox.nextElementSibling.getAttribute("aria-expanded"); + + if ( + (filterCheckbox.checked && "false" === filterExpandedAttribute) || + (!filterCheckbox.checked && "true" === filterExpandedAttribute) + ) { + filterToggleLink.click(); + } + }); + }); + + document + .querySelectorAll("form[data-ea-filters-form-id]") + .forEach((form) => { + // TODO: when using the native datepicker, 'change' isn't fired unless you input the entire date + time information + form.addEventListener("change", (event) => { + if (event.target.classList.contains("filter-checkbox")) { + return; + } + + const filterCheckbox = event.target + .closest(".filter-field") + .querySelector(".filter-checkbox"); + if (!filterCheckbox.checked) { + filterCheckbox.checked = true; + } + }); + }); - document.querySelectorAll('[data-ea-comparison-id]').forEach((comparisonWidget) => { - comparisonWidget.addEventListener('change', (event) => { - const comparisonWidget = event.currentTarget; - const comparisonId = comparisonWidget.dataset.eaComparisonId; + document + .querySelectorAll("[data-ea-comparison-id]") + .forEach((comparisonWidget) => { + comparisonWidget.addEventListener("change", (event) => { + const comparisonWidget = event.currentTarget; + const comparisonId = comparisonWidget.dataset.eaComparisonId; - if (comparisonId === undefined) { - return; - } + if (comparisonId === undefined) { + return; + } - const secondValue = document.querySelector(`[data-ea-value2-of-comparison-id="${comparisonId}"]`); + const secondValue = document.querySelector( + `[data-ea-value2-of-comparison-id="${comparisonId}"]` + ); - if (secondValue === null) { - return; - } + if (secondValue === null) { + return; + } - toggleVisibilityClasses(secondValue, comparisonWidget.value !== 'between'); - }); + toggleVisibilityClasses( + secondValue, + comparisonWidget.value !== "between" + ); }); - } + }); + } } From af2cfd38c11f7f057fa56e543dcef7beca34c010 Mon Sep 17 00:00:00 2001 From: tsiatka Date: Fri, 17 Oct 2025 17:00:54 +0200 Subject: [PATCH 09/10] js lint --- assets/js/app.js | 913 +++++++++++++++++++++-------------------------- 1 file changed, 414 insertions(+), 499 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 3813f3912e..9e30338e9b 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,554 +1,469 @@ // any CSS you require will output into a single css file (app.css in this case) -require("../css/app.css"); +require('../css/app.css'); -import bootstrap from "bootstrap/dist/js/bootstrap.bundle"; -import Mark from "mark.js/src/vanilla"; -import Autocomplete from "./autocomplete"; -import { toggleVisibilityClasses } from "./helpers"; +import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'; +import Mark from 'mark.js/src/vanilla'; +import Autocomplete from './autocomplete'; +import { toggleVisibilityClasses } from './helpers'; // Provide Bootstrap variable globally to allow custom backend pages to use it window.bootstrap = bootstrap; -document.addEventListener("DOMContentLoaded", () => { - window.EasyAdminApp = new App(); +document.addEventListener('DOMContentLoaded', () => { + window.EasyAdminApp = new App(); }); class App { - #sidebarWidthLocalStorageKey; - #contentWidthLocalStorageKey; - - constructor() { - this.#sidebarWidthLocalStorageKey = "ea/sidebar/width"; - this.#contentWidthLocalStorageKey = "ea/content/width"; - - this.#removeHashFormUrl(); - this.#createMainMenu(); - this.#createLayoutResizeControls(); - this.#createNavigationToggler(); - this.#createSearchHighlight(); - this.#createFilters(); - this.#createAutoCompleteFields(); - this.#createBatchActions(); - this.#createModalWindowsForDeleteActions(); - this.#createModalWindowsForConfirmationActions(); - this.#createPopovers(); - this.#createTooltips(); - - document.addEventListener("ea.collection.item-added", () => - this.#createAutoCompleteFields() - ); - } - - // When using tabs in forms, the selected tab is persisted (in the URL hash) so you - // can see the same tab when reloading the page (e.g. '#tab-contact-information'). - // This method removes the hash from URL in the index page to not show form-related - // information in the index page - #removeHashFormUrl() { - if (!window.location.href.includes("#")) { - return; + #sidebarWidthLocalStorageKey; + #contentWidthLocalStorageKey; + + constructor() { + this.#sidebarWidthLocalStorageKey = 'ea/sidebar/width'; + this.#contentWidthLocalStorageKey = 'ea/content/width'; + + this.#removeHashFormUrl(); + this.#createMainMenu(); + this.#createLayoutResizeControls(); + this.#createNavigationToggler(); + this.#createSearchHighlight(); + this.#createFilters(); + this.#createAutoCompleteFields(); + this.#createBatchActions(); + this.#createModalWindowsForDeleteActions(); + this.#createModalWindowsForConfirmationActions(); + this.#createPopovers(); + this.#createTooltips(); + + document.addEventListener('ea.collection.item-added', () => this.#createAutoCompleteFields()); } - // remove the hash only in the index page - if (!document.querySelector("body").classList.contains("ea-index")) { - return; + // When using tabs in forms, the selected tab is persisted (in the URL hash) so you + // can see the same tab when reloading the page (e.g. '#tab-contact-information'). + // This method removes the hash from URL in the index page to not show form-related + // information in the index page + #removeHashFormUrl() { + if (!window.location.href.includes('#')) { + return; + } + + // remove the hash only in the index page + if (!document.querySelector('body').classList.contains('ea-index')) { + return; + } + + // don't set the hash to '' because that also removes the query parameters + const urlParts = window.location.href.split('#'); + const urlWithoutHash = urlParts[0]; + window.history.replaceState({}, '', urlWithoutHash); } - // don't set the hash to '' because that also removes the query parameters - const urlParts = window.location.href.split("#"); - const urlWithoutHash = urlParts[0]; - window.history.replaceState({}, "", urlWithoutHash); - } - - #createMainMenu() { - // inspired by https://codepen.io/phileflanagan/pen/mwpQpY - const menuItemsWithSubmenus = document.querySelectorAll( - "#main-menu .menu-item.has-submenu" - ); - menuItemsWithSubmenus.forEach((menuItem) => { - const menuItemSubmenu = menuItem.querySelector(".submenu"); - if (null === menuItemSubmenu) { - return; - } - - // needed because the menu accordion is based on the max-block-size property. - // visible elements must be initialized with a explicit max-block-size; otherwise - // when you click on them the first time, the animation is not smooth - if (menuItem.classList.contains("expanded")) { - menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; - } - - menuItem - .querySelector(".submenu-toggle") - .addEventListener("click", (event) => { - event.preventDefault(); - - // hide other submenus - menuItemsWithSubmenus.forEach((otherMenuItem) => { - if (menuItem === otherMenuItem) { - return; + #createMainMenu() { + // inspired by https://codepen.io/phileflanagan/pen/mwpQpY + const menuItemsWithSubmenus = document.querySelectorAll('#main-menu .menu-item.has-submenu'); + menuItemsWithSubmenus.forEach((menuItem) => { + const menuItemSubmenu = menuItem.querySelector('.submenu'); + if (null === menuItemSubmenu) { + return; } - const otherMenuItemSubmenu = - otherMenuItem.querySelector(".submenu"); - if (otherMenuItem.classList.contains("expanded")) { - otherMenuItemSubmenu.style.maxHeight = "0px"; - otherMenuItem.classList.remove("expanded"); + // needed because the menu accordion is based on the max-block-size property. + // visible elements must be initialized with a explicit max-block-size; otherwise + // when you click on them the first time, the animation is not smooth + if (menuItem.classList.contains('expanded')) { + menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; } - }); - - // toggle the state of this submenu - if (menuItem.classList.contains("expanded")) { - menuItemSubmenu.style.maxHeight = "0px"; - menuItem.classList.remove("expanded"); - } else { - menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; - menuItem.classList.add("expanded"); - } + + menuItem.querySelector('.submenu-toggle').addEventListener('click', (event) => { + event.preventDefault(); + + // hide other submenus + menuItemsWithSubmenus.forEach((otherMenuItem) => { + if (menuItem === otherMenuItem) { + return; + } + + const otherMenuItemSubmenu = otherMenuItem.querySelector('.submenu'); + if (otherMenuItem.classList.contains('expanded')) { + otherMenuItemSubmenu.style.maxHeight = '0px'; + otherMenuItem.classList.remove('expanded'); + } + }); + + // toggle the state of this submenu + if (menuItem.classList.contains('expanded')) { + menuItemSubmenu.style.maxHeight = '0px'; + menuItem.classList.remove('expanded'); + } else { + menuItemSubmenu.style.maxHeight = `${menuItemSubmenu.scrollHeight}px`; + menuItem.classList.add('expanded'); + } + }); }); - }); - } - - #createLayoutResizeControls() { - const sidebarResizerHandler = document.querySelector( - "#sidebar-resizer-handler" - ); - if (null !== sidebarResizerHandler) { - sidebarResizerHandler.addEventListener("click", () => { - const oldValue = - localStorage.getItem(this.#sidebarWidthLocalStorageKey) || "normal"; - const newValue = "normal" === oldValue ? "compact" : "normal"; - - document - .querySelector("body") - .classList.remove(`ea-sidebar-width-${oldValue}`); - document - .querySelector("body") - .classList.add(`ea-sidebar-width-${newValue}`); - localStorage.setItem(this.#sidebarWidthLocalStorageKey, newValue); - }); } - const contentResizerHandler = document.querySelector( - "#content-resizer-handler" - ); - if (null !== contentResizerHandler) { - contentResizerHandler.addEventListener("click", () => { - const oldValue = - localStorage.getItem(this.#contentWidthLocalStorageKey) || "normal"; - const newValue = "normal" === oldValue ? "full" : "normal"; - - document - .querySelector("body") - .classList.remove(`ea-content-width-${oldValue}`); - document - .querySelector("body") - .classList.add(`ea-content-width-${newValue}`); - localStorage.setItem(this.#contentWidthLocalStorageKey, newValue); - }); + #createLayoutResizeControls() { + const sidebarResizerHandler = document.querySelector('#sidebar-resizer-handler'); + if (null !== sidebarResizerHandler) { + sidebarResizerHandler.addEventListener('click', () => { + const oldValue = localStorage.getItem(this.#sidebarWidthLocalStorageKey) || 'normal'; + const newValue = 'normal' === oldValue ? 'compact' : 'normal'; + + document.querySelector('body').classList.remove(`ea-sidebar-width-${oldValue}`); + document.querySelector('body').classList.add(`ea-sidebar-width-${newValue}`); + localStorage.setItem(this.#sidebarWidthLocalStorageKey, newValue); + }); + } + + const contentResizerHandler = document.querySelector('#content-resizer-handler'); + if (null !== contentResizerHandler) { + contentResizerHandler.addEventListener('click', () => { + const oldValue = localStorage.getItem(this.#contentWidthLocalStorageKey) || 'normal'; + const newValue = 'normal' === oldValue ? 'full' : 'normal'; + + document.querySelector('body').classList.remove(`ea-content-width-${oldValue}`); + document.querySelector('body').classList.add(`ea-content-width-${newValue}`); + localStorage.setItem(this.#contentWidthLocalStorageKey, newValue); + }); + } } - } - #createNavigationToggler() { - const toggler = document.querySelector("#navigation-toggler"); - const cssClassName = "ea-mobile-sidebar-visible"; - let modalBackdrop; + #createNavigationToggler() { + const toggler = document.querySelector('#navigation-toggler'); + const cssClassName = 'ea-mobile-sidebar-visible'; + let modalBackdrop; - if (null === toggler) { - return; + if (null === toggler) { + return; + } + + toggler.addEventListener('click', () => { + document.querySelector('body').classList.toggle(cssClassName); + + if (document.querySelector('body').classList.contains(cssClassName)) { + modalBackdrop = document.createElement('div'); + modalBackdrop.classList.add('modal-backdrop', 'fade', 'show'); + modalBackdrop.onclick = () => { + document.querySelector('body').classList.remove(cssClassName); + document.body.removeChild(modalBackdrop); + modalBackdrop = null; + }; + + document.body.appendChild(modalBackdrop); + } else if (modalBackdrop) { + document.body.removeChild(modalBackdrop); + modalBackdrop = null; + } + }); } - toggler.addEventListener("click", () => { - document.querySelector("body").classList.toggle(cssClassName); + #createSearchHighlight() { + const searchElement = document.querySelector('.form-action-search [name="query"]'); + if (null === searchElement) { + return; + } + + const searchQuery = searchElement.value; + if ('' === searchQuery.trim()) { + return; + } - if (document.querySelector("body").classList.contains(cssClassName)) { - modalBackdrop = document.createElement("div"); - modalBackdrop.classList.add("modal-backdrop", "fade", "show"); - modalBackdrop.onclick = () => { - document.querySelector("body").classList.remove(cssClassName); - document.body.removeChild(modalBackdrop); - modalBackdrop = null; + // splits a string into tokens, taking into account quoted strings + // Example: 'foo "bar baz" qux' => ['foo', 'bar baz', 'qux'] + const tokenizeString = (string) => { + const regex = /"([^"\\]*(\\.[^"\\]*)*)"|\S+/g; + const tokens = []; + + let match = regex.exec(string); + while (null !== match) { + tokens.push(match[0].replaceAll('"', '').trim()); + match = regex.exec(string); + } + + return tokens; }; - document.body.appendChild(modalBackdrop); - } else if (modalBackdrop) { - document.body.removeChild(modalBackdrop); - modalBackdrop = null; - } - }); - } - - #createSearchHighlight() { - const searchElement = document.querySelector( - '.form-action-search [name="query"]' - ); - if (null === searchElement) { - return; - } + const searchQueryTerms = tokenizeString(searchElement.value); - const searchQuery = searchElement.value; - if ("" === searchQuery.trim()) { - return; + const elementsToHighlight = document.querySelectorAll('table tbody td.searchable'); + const highlighter = new Mark(elementsToHighlight); + highlighter.mark(searchQueryTerms, { separateWordSearch: false }); } - // splits a string into tokens, taking into account quoted strings - // Example: 'foo "bar baz" qux' => ['foo', 'bar baz', 'qux'] - const tokenizeString = (string) => { - const regex = /"([^"\\]*(\\.[^"\\]*)*)"|\S+/g; - const tokens = []; - - let match = regex.exec(string); - while (null !== match) { - tokens.push(match[0].replaceAll('"', "").trim()); - match = regex.exec(string); - } - - return tokens; - }; - - const searchQueryTerms = tokenizeString(searchElement.value); - - const elementsToHighlight = document.querySelectorAll( - "table tbody td.searchable" - ); - const highlighter = new Mark(elementsToHighlight); - highlighter.mark(searchQueryTerms, { separateWordSearch: false }); - } - - #createFilters() { - const filterButton = document.querySelector( - ".datagrid-filters .action-filters-button" - ); - if (null === filterButton) { - return; - } + #createFilters() { + const filterButton = document.querySelector('.datagrid-filters .action-filters-button'); + if (null === filterButton) { + return; + } - const filterModal = document.querySelector( - filterButton.getAttribute("data-bs-target") - ); - - // this is needed to avoid errors when connection is slow - filterButton.setAttribute("href", filterButton.getAttribute("data-href")); - filterButton.removeAttribute("data-href"); - filterButton.classList.remove("disabled"); - - filterButton.addEventListener("click", (event) => { - const filterModalBody = filterModal.querySelector(".modal-body"); - filterModalBody.innerHTML = - '
'; - - fetch(filterButton.getAttribute("href")) - .then((response) => { - return response.text(); - }) - .then((text) => { - filterModalBody.innerHTML = text; - this.#createAutoCompleteFields(); - this.#createFilterToggles(); - }) - .catch((error) => { - console.error(error); + const filterModal = document.querySelector(filterButton.getAttribute('data-bs-target')); + + // this is needed to avoid errors when connection is slow + filterButton.setAttribute('href', filterButton.getAttribute('data-href')); + filterButton.removeAttribute('data-href'); + filterButton.classList.remove('disabled'); + + filterButton.addEventListener('click', (event) => { + const filterModalBody = filterModal.querySelector('.modal-body'); + filterModalBody.innerHTML = + '
'; + + fetch(filterButton.getAttribute('href')) + .then((response) => { + return response.text(); + }) + .then((text) => { + filterModalBody.innerHTML = text; + this.#createAutoCompleteFields(); + this.#createFilterToggles(); + }) + .catch((error) => { + console.error(error); + }); + + event.preventDefault(); }); - event.preventDefault(); - }); - - const removeFilter = (filterField) => { - filterField - .closest("form") - .querySelectorAll( - `input[name^="filters[${filterField.dataset.filterProperty}]"]` - ) - .forEach((filterFieldInput) => { - filterFieldInput.remove(); - }); + const removeFilter = (filterField) => { + filterField + .closest('form') + .querySelectorAll(`input[name^="filters[${filterField.dataset.filterProperty}]"]`) + .forEach((filterFieldInput) => { + filterFieldInput.remove(); + }); - filterField.remove(); - }; + filterField.remove(); + }; - document - .querySelector("#modal-clear-button") - .addEventListener("click", () => { - filterModal.querySelectorAll(".filter-field").forEach((filterField) => { - removeFilter(filterField); + document.querySelector('#modal-clear-button').addEventListener('click', () => { + filterModal.querySelectorAll('.filter-field').forEach((filterField) => { + removeFilter(filterField); + }); + filterModal.querySelector('form').submit(); }); - filterModal.querySelector("form").submit(); - }); - - document - .querySelector("#modal-apply-button") - .addEventListener("click", () => { - filterModal - .querySelectorAll(".filter-checkbox:not(:checked)") - .forEach((notAppliedFilter) => { - removeFilter(notAppliedFilter.closest(".filter-field")); - }); - filterModal.querySelector("form").submit(); - }); - } - - #createBatchActions() { - let lastUpdatedRowCheckbox = null; - const selectAllCheckbox = document.querySelector( - ".form-batch-checkbox-all" - ); - if (null === selectAllCheckbox) { - return; - } - const rowCheckboxes = document.querySelectorAll( - 'input[type="checkbox"].form-batch-checkbox' - ); - selectAllCheckbox.addEventListener("change", () => { - rowCheckboxes.forEach((rowCheckbox) => { - rowCheckbox.checked = selectAllCheckbox.checked; - rowCheckbox.dispatchEvent(new Event("change")); - }); - }); - - const deselectAllButton = document.querySelector(".deselect-batch-button"); - if (null !== deselectAllButton) { - deselectAllButton.addEventListener("click", () => { - selectAllCheckbox.checked = false; - selectAllCheckbox.dispatchEvent(new Event("change")); - }); + document.querySelector('#modal-apply-button').addEventListener('click', () => { + filterModal.querySelectorAll('.filter-checkbox:not(:checked)').forEach((notAppliedFilter) => { + removeFilter(notAppliedFilter.closest('.filter-field')); + }); + filterModal.querySelector('form').submit(); + }); } - rowCheckboxes.forEach((rowCheckbox, rowCheckboxIndex) => { - rowCheckbox.dataset.rowIndex = rowCheckboxIndex; - - rowCheckbox.addEventListener("click", (e) => { - if (lastUpdatedRowCheckbox && e.shiftKey) { - const lastIndex = Number.parseInt( - lastUpdatedRowCheckbox.dataset.rowIndex - ); - const currentIndex = Number.parseInt(e.target.dataset.rowIndex); - const valueToApply = e.target.checked; - const lowest = Math.min(lastIndex, currentIndex); - const highest = Math.max(lastIndex, currentIndex); - - rowCheckboxes.forEach((rowCheckbox2, rowCheckboxIndex2) => { - if (lowest <= rowCheckboxIndex2 && rowCheckboxIndex2 <= highest) { - rowCheckbox2.checked = valueToApply; - rowCheckbox2.dispatchEvent(new Event("change")); - } - }); - } - lastUpdatedRowCheckbox = e.target; - }); - - rowCheckbox.addEventListener("change", () => { - const selectedRowCheckboxes = document.querySelectorAll( - 'input[type="checkbox"].form-batch-checkbox:checked' - ); - const row = rowCheckbox.closest("tr"); - const content = rowCheckbox.closest(".content"); - - if (rowCheckbox.checked) { - row.classList.add("selected-row"); - } else { - row.classList.remove("selected-row"); - selectAllCheckbox.checked = false; + #createBatchActions() { + let lastUpdatedRowCheckbox = null; + const selectAllCheckbox = document.querySelector('.form-batch-checkbox-all'); + if (null === selectAllCheckbox) { + return; } - const rowsAreSelected = 0 !== selectedRowCheckboxes.length; - const contentTitle = document.querySelector( - ".content-header-title > .title" - ); - const filters = content.querySelector(".datagrid-filters"); - const globalActions = content.querySelector(".global-actions"); - const batchActions = content.querySelector(".batch-actions"); + const rowCheckboxes = document.querySelectorAll('input[type="checkbox"].form-batch-checkbox'); + selectAllCheckbox.addEventListener('change', () => { + rowCheckboxes.forEach((rowCheckbox) => { + rowCheckbox.checked = selectAllCheckbox.checked; + rowCheckbox.dispatchEvent(new Event('change')); + }); + }); - if (null !== contentTitle) { - toggleVisibilityClasses(contentTitle, rowsAreSelected); - } - if (null !== filters) { - toggleVisibilityClasses(filters, rowsAreSelected); - } - if (null !== globalActions) { - toggleVisibilityClasses(globalActions, rowsAreSelected); - } - if (null !== batchActions) { - toggleVisibilityClasses(batchActions, !rowsAreSelected); + const deselectAllButton = document.querySelector('.deselect-batch-button'); + if (null !== deselectAllButton) { + deselectAllButton.addEventListener('click', () => { + selectAllCheckbox.checked = false; + selectAllCheckbox.dispatchEvent(new Event('change')); + }); } - }); - }); - - const modalTitle = document.querySelector( - "#batch-action-confirmation-title" - ); - const titleContentWithPlaceholders = modalTitle.textContent; - - document - .querySelectorAll("[data-action-batch]") - .forEach((dataActionBatch) => { - dataActionBatch.addEventListener("click", (event) => { - event.preventDefault(); - - const actionElement = event.currentTarget; - // There is still a possibility that actionName will remain undefined. The title attribute is not always present on elements with the [data-action-batch] attribute. - const actionName = - actionElement.textContent.trim() || - actionElement.getAttribute("title"); - const selectedItems = document.querySelectorAll( - 'input[type="checkbox"].form-batch-checkbox:checked' - ); - modalTitle.textContent = titleContentWithPlaceholders - .replace("%action_name%", actionName) - .replace("%num_items%", selectedItems.length.toString()); - - document - .querySelector("#modal-batch-action-button") - .addEventListener("click", () => { - // prevent double submission of the batch action form - actionElement.setAttribute("disabled", "disabled"); - - const batchFormFields = { - batchActionName: actionElement.getAttribute("data-action-name"), - entityFqcn: actionElement.getAttribute("data-entity-fqcn"), - batchActionUrl: actionElement.getAttribute("data-action-url"), - batchActionCsrfToken: actionElement.getAttribute( - "data-action-csrf-token" - ), - }; - selectedItems.forEach((item, i) => { - batchFormFields[`batchActionEntityIds[${i}]`] = item.value; - }); - - const batchForm = document.createElement("form"); - batchForm.setAttribute("method", "POST"); - batchForm.setAttribute( - "action", - actionElement.getAttribute("data-action-url") - ); - for (const fieldName in batchFormFields) { - const formField = document.createElement("input"); - formField.setAttribute("type", "hidden"); - formField.setAttribute("name", fieldName); - formField.setAttribute("value", batchFormFields[fieldName]); - batchForm.appendChild(formField); - } - - document.body.appendChild(batchForm); - batchForm.submit(); + + rowCheckboxes.forEach((rowCheckbox, rowCheckboxIndex) => { + rowCheckbox.dataset.rowIndex = rowCheckboxIndex; + + rowCheckbox.addEventListener('click', (e) => { + if (lastUpdatedRowCheckbox && e.shiftKey) { + const lastIndex = Number.parseInt(lastUpdatedRowCheckbox.dataset.rowIndex); + const currentIndex = Number.parseInt(e.target.dataset.rowIndex); + const valueToApply = e.target.checked; + const lowest = Math.min(lastIndex, currentIndex); + const highest = Math.max(lastIndex, currentIndex); + + rowCheckboxes.forEach((rowCheckbox2, rowCheckboxIndex2) => { + if (lowest <= rowCheckboxIndex2 && rowCheckboxIndex2 <= highest) { + rowCheckbox2.checked = valueToApply; + rowCheckbox2.dispatchEvent(new Event('change')); + } + }); + } + lastUpdatedRowCheckbox = e.target; + }); + + rowCheckbox.addEventListener('change', () => { + const selectedRowCheckboxes = document.querySelectorAll( + 'input[type="checkbox"].form-batch-checkbox:checked' + ); + const row = rowCheckbox.closest('tr'); + const content = rowCheckbox.closest('.content'); + + if (rowCheckbox.checked) { + row.classList.add('selected-row'); + } else { + row.classList.remove('selected-row'); + selectAllCheckbox.checked = false; + } + + const rowsAreSelected = 0 !== selectedRowCheckboxes.length; + const contentTitle = document.querySelector('.content-header-title > .title'); + const filters = content.querySelector('.datagrid-filters'); + const globalActions = content.querySelector('.global-actions'); + const batchActions = content.querySelector('.batch-actions'); + + if (null !== contentTitle) { + toggleVisibilityClasses(contentTitle, rowsAreSelected); + } + if (null !== filters) { + toggleVisibilityClasses(filters, rowsAreSelected); + } + if (null !== globalActions) { + toggleVisibilityClasses(globalActions, rowsAreSelected); + } + if (null !== batchActions) { + toggleVisibilityClasses(batchActions, !rowsAreSelected); + } }); }); - }); - } - - #createAutoCompleteFields() { - const autocomplete = new Autocomplete(); - document - .querySelectorAll('[data-ea-widget="ea-autocomplete"]') - .forEach((autocompleteElement) => { - autocomplete.create(autocompleteElement); - }); - } - - #createModalWindowsForDeleteActions() { - document - .querySelectorAll('[data-action-name="delete"]') - .forEach((actionElement) => { - actionElement.addEventListener("click", (event) => { - event.preventDefault(); - - document - .querySelector("#modal-delete-button") - .addEventListener("click", () => { - const deleteFormAction = actionElement.getAttribute("formaction"); - const deleteForm = document.querySelector("#delete-form"); - deleteForm.setAttribute("action", deleteFormAction); - deleteForm.submit(); + + const modalTitle = document.querySelector('#batch-action-confirmation-title'); + const titleContentWithPlaceholders = modalTitle.textContent; + + document.querySelectorAll('[data-action-batch]').forEach((dataActionBatch) => { + dataActionBatch.addEventListener('click', (event) => { + event.preventDefault(); + + const actionElement = event.currentTarget; + // There is still a possibility that actionName will remain undefined. The title attribute is not always present on elements with the [data-action-batch] attribute. + const actionName = actionElement.textContent.trim() || actionElement.getAttribute('title'); + const selectedItems = document.querySelectorAll('input[type="checkbox"].form-batch-checkbox:checked'); + modalTitle.textContent = titleContentWithPlaceholders + .replace('%action_name%', actionName) + .replace('%num_items%', selectedItems.length.toString()); + + document.querySelector('#modal-batch-action-button').addEventListener('click', () => { + // prevent double submission of the batch action form + actionElement.setAttribute('disabled', 'disabled'); + + const batchFormFields = { + batchActionName: actionElement.getAttribute('data-action-name'), + entityFqcn: actionElement.getAttribute('data-entity-fqcn'), + batchActionUrl: actionElement.getAttribute('data-action-url'), + batchActionCsrfToken: actionElement.getAttribute('data-action-csrf-token'), + }; + selectedItems.forEach((item, i) => { + batchFormFields[`batchActionEntityIds[${i}]`] = item.value; + }); + + const batchForm = document.createElement('form'); + batchForm.setAttribute('method', 'POST'); + batchForm.setAttribute('action', actionElement.getAttribute('data-action-url')); + for (const fieldName in batchFormFields) { + const formField = document.createElement('input'); + formField.setAttribute('type', 'hidden'); + formField.setAttribute('name', fieldName); + formField.setAttribute('value', batchFormFields[fieldName]); + batchForm.appendChild(formField); + } + + document.body.appendChild(batchForm); + batchForm.submit(); + }); }); }); - }); - } - - #createModalWindowsForConfirmationActions() { - const modalTitles = document.querySelectorAll(".action-confirmation-title"); - modalTitles.forEach((modalTitle) => { - const titleContentWithPlaceholders = modalTitle.textContent; - const actionName = modalTitle.getAttribute("data-action-title"); - modalTitle.textContent = titleContentWithPlaceholders.replace( - "%action_name%", - actionName - ); - }); - } - - #createPopovers() { - document - .querySelectorAll('[data-bs-toggle="popover"]') - .forEach((popoverElement) => { - new bootstrap.Popover(popoverElement); - }); - } - - #createTooltips() { - document - .querySelectorAll('[data-bs-toggle="tooltip"]') - .forEach((tooltipElement) => { - new bootstrap.Tooltip(tooltipElement); - }); - } - - #createFilterToggles() { - document.querySelectorAll(".filter-checkbox").forEach((filterCheckbox) => { - filterCheckbox.addEventListener("change", () => { - const filterToggleLink = filterCheckbox.nextElementSibling; - const filterExpandedAttribute = - filterCheckbox.nextElementSibling.getAttribute("aria-expanded"); - - if ( - (filterCheckbox.checked && "false" === filterExpandedAttribute) || - (!filterCheckbox.checked && "true" === filterExpandedAttribute) - ) { - filterToggleLink.click(); - } - }); - }); - - document - .querySelectorAll("form[data-ea-filters-form-id]") - .forEach((form) => { - // TODO: when using the native datepicker, 'change' isn't fired unless you input the entire date + time information - form.addEventListener("change", (event) => { - if (event.target.classList.contains("filter-checkbox")) { - return; - } - - const filterCheckbox = event.target - .closest(".filter-field") - .querySelector(".filter-checkbox"); - if (!filterCheckbox.checked) { - filterCheckbox.checked = true; - } + } + + #createAutoCompleteFields() { + const autocomplete = new Autocomplete(); + document.querySelectorAll('[data-ea-widget="ea-autocomplete"]').forEach((autocompleteElement) => { + autocomplete.create(autocompleteElement); }); - }); + } - document - .querySelectorAll("[data-ea-comparison-id]") - .forEach((comparisonWidget) => { - comparisonWidget.addEventListener("change", (event) => { - const comparisonWidget = event.currentTarget; - const comparisonId = comparisonWidget.dataset.eaComparisonId; + #createModalWindowsForDeleteActions() { + document.querySelectorAll('[data-action-name="delete"]').forEach((actionElement) => { + actionElement.addEventListener('click', (event) => { + event.preventDefault(); + + document.querySelector('#modal-delete-button').addEventListener('click', () => { + const deleteFormAction = actionElement.getAttribute('formaction'); + const deleteForm = document.querySelector('#delete-form'); + deleteForm.setAttribute('action', deleteFormAction); + deleteForm.submit(); + }); + }); + }); + } - if (comparisonId === undefined) { - return; - } + #createModalWindowsForConfirmationActions() { + const modalTitles = document.querySelectorAll('.action-confirmation-title'); + modalTitles.forEach((modalTitle) => { + const titleContentWithPlaceholders = modalTitle.textContent; + const actionName = modalTitle.getAttribute('data-action-title'); + modalTitle.textContent = titleContentWithPlaceholders.replace('%action_name%', actionName); + }); + } - const secondValue = document.querySelector( - `[data-ea-value2-of-comparison-id="${comparisonId}"]` - ); + #createPopovers() { + document.querySelectorAll('[data-bs-toggle="popover"]').forEach((popoverElement) => { + new bootstrap.Popover(popoverElement); + }); + } - if (secondValue === null) { - return; - } + #createTooltips() { + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((tooltipElement) => { + new bootstrap.Tooltip(tooltipElement); + }); + } - toggleVisibilityClasses( - secondValue, - comparisonWidget.value !== "between" - ); + #createFilterToggles() { + document.querySelectorAll('.filter-checkbox').forEach((filterCheckbox) => { + filterCheckbox.addEventListener('change', () => { + const filterToggleLink = filterCheckbox.nextElementSibling; + const filterExpandedAttribute = filterCheckbox.nextElementSibling.getAttribute('aria-expanded'); + + if ( + (filterCheckbox.checked && 'false' === filterExpandedAttribute) || + (!filterCheckbox.checked && 'true' === filterExpandedAttribute) + ) { + filterToggleLink.click(); + } + }); }); - }); - } + + document.querySelectorAll('form[data-ea-filters-form-id]').forEach((form) => { + // TODO: when using the native datepicker, 'change' isn't fired unless you input the entire date + time information + form.addEventListener('change', (event) => { + if (event.target.classList.contains('filter-checkbox')) { + return; + } + + const filterCheckbox = event.target.closest('.filter-field').querySelector('.filter-checkbox'); + if (!filterCheckbox.checked) { + filterCheckbox.checked = true; + } + }); + }); + + document.querySelectorAll('[data-ea-comparison-id]').forEach((comparisonWidget) => { + comparisonWidget.addEventListener('change', (event) => { + const comparisonWidget = event.currentTarget; + const comparisonId = comparisonWidget.dataset.eaComparisonId; + + if (comparisonId === undefined) { + return; + } + + const secondValue = document.querySelector(`[data-ea-value2-of-comparison-id="${comparisonId}"]`); + + if (secondValue === null) { + return; + } + + toggleVisibilityClasses(secondValue, comparisonWidget.value !== 'between'); + }); + }); + } } From ec96ead8cb51d0faa2ce3d75eee291d82a02b583 Mon Sep 17 00:00:00 2001 From: tsiatka Date: Mon, 20 Oct 2025 12:03:11 +0200 Subject: [PATCH 10/10] twig lint + remove useless html attribute --- src/Factory/ActionFactory.php | 1 - templates/crud/detail.html.twig | 4 +-- templates/crud/edit.html.twig | 4 +-- .../includes/_confirm_action_modal.html.twig | 32 +++++++++---------- templates/crud/index.html.twig | 8 ++--- 5 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/Factory/ActionFactory.php b/src/Factory/ActionFactory.php index 9da2a811ab..798ad3ea3e 100644 --- a/src/Factory/ActionFactory.php +++ b/src/Factory/ActionFactory.php @@ -199,7 +199,6 @@ private function processAction(string $pageName, ActionDto $actionDto, ?EntityDt $actionDto->addHtmlAttributes([ 'data-bs-toggle' => 'modal', 'data-bs-target' => '#modal-confirmation-'.$actionDto->getName(), - 'data-action-url' => $actionDto->getLinkUrl(), ]); } diff --git a/templates/crud/detail.html.twig b/templates/crud/detail.html.twig index c3078866be..82f41b3681 100644 --- a/templates/crud/detail.html.twig +++ b/templates/crud/detail.html.twig @@ -70,11 +70,11 @@ {% if action.isActionGroup %} {% for item in action.items %} {% if item.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context: false) }} {% endif %} {% endfor %} {% elseif action.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context: false) }} {% endif %} {% endfor %} {% endblock %} diff --git a/templates/crud/edit.html.twig b/templates/crud/edit.html.twig index e927bad6b8..50023a9ac7 100644 --- a/templates/crud/edit.html.twig +++ b/templates/crud/edit.html.twig @@ -72,11 +72,11 @@ {% if action.isActionGroup %} {% for item in action.items %} {% if item.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context: false) }} {% endif %} {% endfor %} {% elseif action.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context: false) }} {% endif %} {% endfor %} {% endblock %} diff --git a/templates/crud/includes/_confirm_action_modal.html.twig b/templates/crud/includes/_confirm_action_modal.html.twig index d1ff7d03a2..dad664ff86 100644 --- a/templates/crud/includes/_confirm_action_modal.html.twig +++ b/templates/crud/includes/_confirm_action_modal.html.twig @@ -1,24 +1,24 @@ - diff --git a/templates/crud/index.html.twig b/templates/crud/index.html.twig index c03aa8174f..b3c635a057 100644 --- a/templates/crud/index.html.twig +++ b/templates/crud/index.html.twig @@ -195,16 +195,16 @@ {% else %} {% for action in entity.actions %} {% if action.isActionGroup %} - {{ include(action.templatePath, {group: action, entity: entity}, with_context = false) }} + {{ include(action.templatePath, {group: action, entity: entity}, with_context: false) }} {% for item in action.items %} {% if item.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: item}, with_context: false) }} {% endif %} {% endfor %} {% else %} - {{ include(action.templatePath, {action: action, entity: entity, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown}, with_context = false) }} + {{ include(action.templatePath, {action: action, entity: entity, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown}, with_context: false) }} {% if action.hasConfirmationModal %} - {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context = false) }} + {{ include('@EasyAdmin/crud/includes/_confirm_action_modal.html.twig', {action: action}, with_context: false) }} {% endif %} {% endif %} {% endfor %}