From fa39b4d5d4f60ad4789a05e379b29b49c9278976 Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Wed, 18 Aug 2021 15:55:43 +0200
Subject: [PATCH] [TASK] Use multi record selection in recycler module

The multi record selection component is now also used
in the recycler module. This increases consistency, as
custom checkbox logic could be removed.

Also the recyclers' JavaScript implementation of the
selection actions is simplified. To interact with the
multi record selection component, some events are used.
This also means, the functionality stays the same, while
most of it is now handled by the Multi record selection
component.

Resolves: #94926
Releases: master
Change-Id: I57d91d070bfa75c01d4cb87dfd0c5576d111dcfa
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/70678
Tested-by: core-ci <typo3@b13.com>
Tested-by: Jochen <rothjochen@gmail.com>
Tested-by: Simon Schaufelberger <simonschaufi+typo3@gmail.com>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Jochen <rothjochen@gmail.com>
Reviewed-by: Simon Schaufelberger <simonschaufi+typo3@gmail.com>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../Resources/Public/TypeScript/Recycler.ts   | 64 ++++++----------
 .../Controller/RecyclerModuleController.php   |  1 +
 .../Partials/RecordsTable/DeletedRecord.html  |  7 +-
 .../Templates/RecyclerModule/Index.html       | 74 +++++++++++++------
 .../Resources/Public/JavaScript/Recycler.js   |  2 +-
 5 files changed, 81 insertions(+), 67 deletions(-)

diff --git a/Build/Sources/TypeScript/recycler/Resources/Public/TypeScript/Recycler.ts b/Build/Sources/TypeScript/recycler/Resources/Public/TypeScript/Recycler.ts
index bd6e104fcf4a..570bb1d782a9 100644
--- a/Build/Sources/TypeScript/recycler/Resources/Public/TypeScript/Recycler.ts
+++ b/Build/Sources/TypeScript/recycler/Resources/Public/TypeScript/Recycler.ts
@@ -18,6 +18,7 @@ import DeferredAction = require('TYPO3/CMS/Backend/ActionButton/DeferredAction')
 import Modal = require('TYPO3/CMS/Backend/Modal');
 import Notification = require('TYPO3/CMS/Backend/Notification');
 import Severity = require('TYPO3/CMS/Backend/Severity');
+import RegularEvent from 'TYPO3/CMS/Core/Event/RegularEvent';
 
 enum RecyclerIdentifiers {
   searchForm = '#recycler-form',
@@ -28,9 +29,10 @@ enum RecyclerIdentifiers {
   recyclerTable = '#itemsInRecycler',
   paginator = '#recycler-index nav',
   reloadAction = 'a[data-action=reload]',
-  massUndo = 'button[data-action=massundo]',
-  massDelete = 'button[data-action=massdelete]',
-  toggleAll = '.t3js-toggle-all',
+  undo = 'a[data-action=undo]',
+  delete = 'a[data-action=delete]',
+  massUndo = 'button[data-multi-record-selection-action=massundo]',
+  massDelete = 'button[data-multi-record-selection-action=massdelete]',
 }
 
 /**
@@ -46,7 +48,6 @@ class Recycler {
     itemsPerPage: TYPO3.settings.Recycler.pagingSize,
   };
   public markedRecordsForMassAction: Array<string> = [];
-  public allToggled: boolean = false;
 
   /**
    * Reloads the page tree
@@ -77,7 +78,6 @@ class Recycler {
       $reloadAction: $(RecyclerIdentifiers.reloadAction),
       $massUndo: $(RecyclerIdentifiers.massUndo),
       $massDelete: $(RecyclerIdentifiers.massDelete),
-      $toggleAll: $(RecyclerIdentifiers.toggleAll),
     };
   }
 
@@ -127,10 +127,10 @@ class Recycler {
     });
 
     // clicking "recover" in single row
-    this.elements.$recyclerTable.on('click', '[data-action=undo]', this.undoRecord);
+    new RegularEvent('click', this.undoRecord).delegateTo(document, RecyclerIdentifiers.undo);
 
     // clicking "delete" in single row
-    this.elements.$recyclerTable.on('click', '[data-action=delete]', this.deleteRecord);
+    new RegularEvent('click', this.deleteRecord).delegateTo(document, RecyclerIdentifiers.delete);
 
     this.elements.$reloadAction.on('click', (e: JQueryEventObject): void => {
       e.preventDefault();
@@ -199,14 +199,9 @@ class Recycler {
     });
 
     // checkboxes in the table
-    this.elements.$toggleAll.on('click', (): void => {
-      this.allToggled = !this.allToggled;
-      $('input[type="checkbox"]').prop('checked', this.allToggled).trigger('change');
-    });
-    this.elements.$recyclerTable.on('change', 'tr input[type=checkbox]', this.handleCheckboxSelects);
-
-    this.elements.$massUndo.on('click', this.undoRecord);
-    this.elements.$massDelete.on('click', this.deleteRecord);
+    new RegularEvent('checkbox:state:changed', this.handleCheckboxStateChanged).bindTo(document);
+    new RegularEvent('multiRecordSelection:action:massundo', this.undoRecord).bindTo(document);
+    new RegularEvent('multiRecordSelection:action:massdelete', this.deleteRecord).bindTo(document);
   }
 
   /**
@@ -230,8 +225,8 @@ class Recycler {
   /**
    * Handles the clicks on checkboxes in the records table
    */
-  private handleCheckboxSelects = (e: JQueryEventObject): void => {
-    const $checkbox = $(e.currentTarget);
+  private handleCheckboxStateChanged = (e: Event): void => {
+    const $checkbox = $(e.target);
     const $tr = $checkbox.parents('tr');
     const table = $tr.data('table');
     const uid = $tr.data('uid');
@@ -239,29 +234,20 @@ class Recycler {
 
     if ($checkbox.prop('checked')) {
       this.markedRecordsForMassAction.push(record);
-      $tr.addClass('warning');
     } else {
       const index = this.markedRecordsForMassAction.indexOf(record);
       if (index > -1) {
         this.markedRecordsForMassAction.splice(index, 1);
       }
-      $tr.removeClass('warning');
     }
 
     if (this.markedRecordsForMassAction.length > 0) {
-      if (this.elements.$massUndo.hasClass('disabled')) {
-        this.elements.$massUndo.removeClass('disabled').removeAttr('disabled');
-      }
-      if (this.elements.$massDelete.hasClass('disabled')) {
-        this.elements.$massDelete.removeClass('disabled').removeAttr('disabled');
-      }
-
-      const btnTextUndo = this.createMessage(TYPO3.lang['button.undoselected'], [this.markedRecordsForMassAction.length]);
-      const btnTextDelete = this.createMessage(TYPO3.lang['button.deleteselected'], [this.markedRecordsForMassAction.length]);
-
-      this.elements.$massUndo.find('span.text').text(btnTextUndo);
-      this.elements.$massDelete.find('span.text').text(btnTextDelete);
-
+      this.elements.$massUndo.find('span.text').text(
+        this.createMessage(TYPO3.lang['button.undoselected'], [this.markedRecordsForMassAction.length])
+      );
+      this.elements.$massDelete.find('span.text').text(
+        this.createMessage(TYPO3.lang['button.deleteselected'], [this.markedRecordsForMassAction.length])
+      );
     } else {
       this.resetMassActionButtons();
     }
@@ -272,10 +258,9 @@ class Recycler {
    */
   private resetMassActionButtons(): void {
     this.markedRecordsForMassAction = [];
-    this.elements.$massUndo.addClass('disabled').attr('disabled', true);
     this.elements.$massUndo.find('span.text').text(TYPO3.lang['button.undo']);
-    this.elements.$massDelete.addClass('disabled').attr('disabled', true);
     this.elements.$massDelete.find('span.text').text(TYPO3.lang['button.delete']);
+    document.dispatchEvent(new Event('multiRecordSelection:actions:hide'));
   }
 
   /**
@@ -350,12 +335,12 @@ class Recycler {
     });
   }
 
-  private deleteRecord = (e: JQueryEventObject): void => {
+  private deleteRecord = (e: Event): void => {
     if (TYPO3.settings.Recycler.deleteDisable) {
       return;
     }
 
-    const $tr = $(e.currentTarget).parents('tr');
+    const $tr = $(e.target).parents('tr');
     const isMassDelete = $tr.parent().prop('tagName') !== 'TBODY'; // deleteRecord() was invoked by the mass delete button
     let records: Array<string>;
     let message: string;
@@ -389,8 +374,8 @@ class Recycler {
     ]);
   }
 
-  private undoRecord = (e: JQueryEventObject): void => {
-    const $tr = $(e.currentTarget).parents('tr');
+  private undoRecord = (e: Event): void => {
+    const $tr = $(e.target).parents('tr');
     const isMassUndo = $tr.parent().prop('tagName') !== 'TBODY'; // undoRecord() was invoked by the mass delete button
 
     let records: Array<string>;
@@ -501,9 +486,6 @@ class Recycler {
           if (reloadPageTree) {
             Recycler.refreshPageTree();
           }
-
-          // Reset toggle state
-          this.allToggled = false;
         });
       },
       complete: () => {
diff --git a/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php b/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php
index 288b4c5f1051..7a552df5cb05 100644
--- a/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php
+++ b/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php
@@ -156,6 +156,7 @@ class RecyclerModuleController
     {
         $this->pageRenderer->addInlineSettingArray('Recycler', $this->getJavaScriptConfiguration($request->getAttribute('normalizedParams')));
         $this->pageRenderer->addInlineLanguageLabelFile('EXT:recycler/Resources/Private/Language/locallang.xlf');
+        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/MultiRecordSelection');
         if ($this->isAccessibleForCurrentUser) {
             $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageRecord);
         }
diff --git a/typo3/sysext/recycler/Resources/Private/Partials/RecordsTable/DeletedRecord.html b/typo3/sysext/recycler/Resources/Private/Partials/RecordsTable/DeletedRecord.html
index 8cf321924519..e3cfee9adb66 100644
--- a/typo3/sysext/recycler/Resources/Private/Partials/RecordsTable/DeletedRecord.html
+++ b/typo3/sysext/recycler/Resources/Private/Partials/RecordsTable/DeletedRecord.html
@@ -1,10 +1,9 @@
 <html xmlns:be="http://typo3.org/ns/TYPO3/CMS/Backend/ViewHelpers" data-namespace-typo3-fluid="true">
 <tr data-uid="{record.uid}" data-table="{record.table}" data-recordtitle="{record.title}" data-parent-deleted="{record.isParentDeleted}">
     <td class="nowrap">
-        <label class="btn btn-default btn-checkbox">
-            <input type="checkbox">
-            <span class="t3-icon fa"></span>
-        </label>
+        <span class="form-check form-toggle">
+            <input class="form-check-input t3js-multi-record-selection-check" type="checkbox">
+        </span>
     </td>
     <td class="nowrap">{record.tableTitle}</td>
     <td class="nowrap"><f:format.raw>{record.icon}</f:format.raw> {record.title}</td>
diff --git a/typo3/sysext/recycler/Resources/Private/Templates/RecyclerModule/Index.html b/typo3/sysext/recycler/Resources/Private/Templates/RecyclerModule/Index.html
index 1f2dd5513ec2..1b52c3d81d48 100644
--- a/typo3/sysext/recycler/Resources/Private/Templates/RecyclerModule/Index.html
+++ b/typo3/sysext/recycler/Resources/Private/Templates/RecyclerModule/Index.html
@@ -27,14 +27,12 @@
                 </div>
             </div>
         </form>
-        <f:render section="buttons" arguments="{allowDelete: allowDelete}"/>
-        <div class="table-fit">
+        <f:render section="multiRecordSelectionActions" arguments="{allowDelete: allowDelete}"/>
+        <div class="table-fit mb-0">
             <table class="table table-hover" id="itemsInRecycler">
                 <thead>
                     <tr>
-                        <th>
-                            <button type="button" class="btn btn-default t3js-toggle-all"><core:icon identifier="actions-document-select" /></button>
-                        </th>
+                        <th><f:render section="multiRecordSelectionCheckboxActions" /></th>
                         <th><f:translate key="table.header.recordtype" /></th>
                         <th><f:translate key="table.header.record" /></th>
                         <th><f:translate key="table.header.tstamp" /></th>
@@ -47,27 +45,61 @@
                 </tbody>
             </table>
         </div>
-        <f:render section="buttons" arguments="{allowDelete: allowDelete}"/>
+        <f:render section="multiRecordSelectionActions" arguments="{allowDelete: allowDelete}"/>
         <nav>
         </nav>
     </div>
 </f:section>
 
-<f:section name="buttons">
-    <div class="form-group">
-        <button class="btn btn-default disabled" data-action="massundo" disabled>
-            <core:icon identifier="actions-edit-undo" />
-            <span class="text">
-                <f:translate key="button.undo" />
-            </span>
+<f:section name="multiRecordSelectionCheckboxActions">
+    <div class="btn-group dropdown position-static">
+        <button type="button" class="btn btn-borderless dropdown-toggle" data-bs-target="multi-record-selection-check-actions" data-bs-toggle="dropdown" data-bs-boundary="window" aria-expanded="false">
+            <core:icon identifier="content-special-div" size="small" />
         </button>
-        <f:if condition="{allowDelete}">
-            <button class="btn btn-default disabled" data-action="massdelete" disabled>
-                <core:icon identifier="actions-edit-delete" />
-                <span class="text">
-                    <f:translate key="button.delete" />
-                </span>
-            </button>
-        </f:if>
+        <ul id="multi-record-selection-check-actions" class="dropdown-menu">
+            <li>
+                <button type="button" class="btn btn-link dropdown-item disabled" data-multi-record-selection-check-action="check-all" title="{f:translate(key: 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.checkAll')}">
+                    <core:icon identifier="actions-check-square" size="small" /> <f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.checkAll" />
+                </button>
+            </li>
+            <li>
+                <button type="button" class="btn btn-link dropdown-item disabled" data-multi-record-selection-check-action="check-none" title="{f:translate(key: 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.uncheckAll')}">
+                    <core:icon identifier="actions-square" size="small" /> <f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.uncheckAll" />
+                </button>
+            </li>
+            <li>
+                <button type="button" class="btn btn-link dropdown-item" data-multi-record-selection-check-action="toggle" title="{f:translate(key: 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.toggleSelection')}">
+                    <core:icon identifier="actions-document-select" size="small" /> <f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.toggleSelection" />
+                </button>
+            </li>
+        </ul>
+    </div>
+</f:section>
+
+<f:section name="multiRecordSelectionActions">
+    <div class="multi-record-selection-actions-wrapper">
+        <div class="row row-cols-auto align-items-center g-2 t3js-multi-record-selection-actions hidden">
+            <div class="col">
+                <strong><f:translate key="LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.selection"/></strong>
+            </div>
+            <div class="col">
+                <button class="btn btn-default btn-sm" data-multi-record-selection-action="massundo" data-multi-record-selection-action-config='{"idField": "uid"}'>
+                    <core:icon identifier="actions-edit-undo" />
+                    <span class="text">
+                        <f:translate key="button.undo" />
+                    </span>
+                </button>
+            </div>
+            <f:if condition="{allowDelete}">
+                <div class="col">
+                    <button class="btn btn-default btn-sm" data-multi-record-selection-action="massdelete" data-multi-record-selection-action-config='{"idField": "uid"}'>
+                        <core:icon identifier="actions-edit-delete" />
+                        <span class="text">
+                            <f:translate key="button.delete" />
+                        </span>
+                    </button>
+                </div>
+            </f:if>
+        </div>
     </div>
 </f:section>
diff --git a/typo3/sysext/recycler/Resources/Public/JavaScript/Recycler.js b/typo3/sysext/recycler/Resources/Public/JavaScript/Recycler.js
index 87e1e3838bf7..3b20fa40e4fd 100644
--- a/typo3/sysext/recycler/Resources/Public/JavaScript/Recycler.js
+++ b/typo3/sysext/recycler/Resources/Public/JavaScript/Recycler.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","nprogress","TYPO3/CMS/Backend/ActionButton/DeferredAction","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Notification","TYPO3/CMS/Backend/Severity","TYPO3/CMS/Backend/Input/Clearable"],(function(e,t,a,s,l,n,i,r){"use strict";var d;a=__importDefault(a),s=__importDefault(s),function(e){e.searchForm="#recycler-form",e.searchText="#recycler-form [name=search-text]",e.searchSubmitBtn="#recycler-form button[type=submit]",e.depthSelector="#recycler-form [name=depth]",e.tableSelector="#recycler-form [name=pages]",e.recyclerTable="#itemsInRecycler",e.paginator="#recycler-index nav",e.reloadAction="a[data-action=reload]",e.massUndo="button[data-action=massundo]",e.massDelete="button[data-action=massdelete]",e.toggleAll=".t3js-toggle-all"}(d||(d={}));class o{constructor(){this.elements={},this.paging={currentPage:1,totalPages:1,totalItems:0,itemsPerPage:TYPO3.settings.Recycler.pagingSize},this.markedRecordsForMassAction=[],this.allToggled=!1,this.handleCheckboxSelects=e=>{const t=a.default(e.currentTarget),s=t.parents("tr"),l=s.data("table")+":"+s.data("uid");if(t.prop("checked"))this.markedRecordsForMassAction.push(l),s.addClass("warning");else{const e=this.markedRecordsForMassAction.indexOf(l);e>-1&&this.markedRecordsForMassAction.splice(e,1),s.removeClass("warning")}if(this.markedRecordsForMassAction.length>0){this.elements.$massUndo.hasClass("disabled")&&this.elements.$massUndo.removeClass("disabled").removeAttr("disabled"),this.elements.$massDelete.hasClass("disabled")&&this.elements.$massDelete.removeClass("disabled").removeAttr("disabled");const e=this.createMessage(TYPO3.lang["button.undoselected"],[this.markedRecordsForMassAction.length]),t=this.createMessage(TYPO3.lang["button.deleteselected"],[this.markedRecordsForMassAction.length]);this.elements.$massUndo.find("span.text").text(e),this.elements.$massDelete.find("span.text").text(t)}else this.resetMassActionButtons()},this.deleteRecord=e=>{if(TYPO3.settings.Recycler.deleteDisable)return;const t=a.default(e.currentTarget).parents("tr"),s="TBODY"!==t.parent().prop("tagName");let i,d;if(s)i=this.markedRecordsForMassAction,d=TYPO3.lang["modal.massdelete.text"];else{const e=t.data("uid"),a=t.data("table"),s=t.data("recordtitle");i=[a+":"+e],d="pages"===a?TYPO3.lang["modal.deletepage.text"]:TYPO3.lang["modal.deletecontent.text"],d=this.createMessage(d,[s,"["+i[0]+"]"])}n.confirm(TYPO3.lang["modal.delete.header"],d,r.error,[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){n.dismiss()}},{text:TYPO3.lang["button.delete"],btnClass:"btn-danger",action:new l(()=>Promise.resolve(this.callAjaxAction("delete",i,s)))}])},this.undoRecord=e=>{const t=a.default(e.currentTarget).parents("tr"),s="TBODY"!==t.parent().prop("tagName");let i,d,o;if(s)i=this.markedRecordsForMassAction,d=TYPO3.lang["modal.massundo.text"],o=!0;else{const e=t.data("uid"),a=t.data("table"),s=t.data("recordtitle");i=[a+":"+e],o="pages"===a,d=o?TYPO3.lang["modal.undopage.text"]:TYPO3.lang["modal.undocontent.text"],d=this.createMessage(d,[s,"["+i[0]+"]"]),o&&t.data("parentDeleted")&&(d+=TYPO3.lang["modal.undo.parentpages"])}let c=null;c=o?a.default("<div />").append(a.default("<p />").text(d),a.default("<div />",{class:"checkbox"}).append(a.default("<label />").append(TYPO3.lang["modal.undo.recursive"]).prepend(a.default("<input />",{id:"undo-recursive",type:"checkbox"})))):a.default("<p />").text(d),n.confirm(TYPO3.lang["modal.undo.header"],c,r.ok,[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){n.dismiss()}},{text:TYPO3.lang["button.undo"],btnClass:"btn-success",action:new l(()=>Promise.resolve(this.callAjaxAction("undo","object"==typeof i?i:[i],s,c.find("#undo-recursive").prop("checked"))))}])},a.default(()=>{this.initialize()})}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}getElements(){this.elements={$searchForm:a.default(d.searchForm),$searchTextField:a.default(d.searchText),$searchSubmitBtn:a.default(d.searchSubmitBtn),$depthSelector:a.default(d.depthSelector),$tableSelector:a.default(d.tableSelector),$recyclerTable:a.default(d.recyclerTable),$tableBody:a.default(d.recyclerTable).find("tbody"),$paginator:a.default(d.paginator),$reloadAction:a.default(d.reloadAction),$massUndo:a.default(d.massUndo),$massDelete:a.default(d.massDelete),$toggleAll:a.default(d.toggleAll)}}registerEvents(){this.elements.$searchForm.on("submit",e=>{e.preventDefault(),""!==this.elements.$searchTextField.val()&&this.loadDeletedElements()}),this.elements.$searchTextField.on("keyup",e=>{""!==a.default(e.currentTarget).val()?this.elements.$searchSubmitBtn.removeClass("disabled"):(this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements())}),this.elements.$searchTextField.get(0).clearable({onClear:()=>{this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements()}}),this.elements.$depthSelector.on("change",()=>{a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements()})}),this.elements.$tableSelector.on("change",()=>{this.paging.currentPage=1,this.loadDeletedElements()}),this.elements.$recyclerTable.on("click","[data-action=undo]",this.undoRecord),this.elements.$recyclerTable.on("click","[data-action=delete]",this.deleteRecord),this.elements.$reloadAction.on("click",e=>{e.preventDefault(),a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements()})}),this.elements.$paginator.on("click","a[data-action]",e=>{e.preventDefault();const t=a.default(e.currentTarget);let s=!1;switch(t.data("action")){case"previous":this.paging.currentPage>1&&(this.paging.currentPage--,s=!0);break;case"next":this.paging.currentPage<this.paging.totalPages&&(this.paging.currentPage++,s=!0);break;case"page":this.paging.currentPage=parseInt(t.find("span").text(),10),s=!0}s&&this.loadDeletedElements()}),TYPO3.settings.Recycler.deleteDisable?this.elements.$massDelete.remove():this.elements.$massDelete.show(),this.elements.$recyclerTable.on("show.bs.collapse hide.bs.collapse","tr.collapse",e=>{let t,s,l=a.default(e.currentTarget).prev("tr").find("[data-action=expand]").find(".t3-icon");switch(e.type){case"show":t="t3-icon-pagetree-collapse",s="t3-icon-pagetree-expand";break;case"hide":t="t3-icon-pagetree-expand",s="t3-icon-pagetree-collapse"}l.removeClass(t).addClass(s)}),this.elements.$toggleAll.on("click",()=>{this.allToggled=!this.allToggled,a.default('input[type="checkbox"]').prop("checked",this.allToggled).trigger("change")}),this.elements.$recyclerTable.on("change","tr input[type=checkbox]",this.handleCheckboxSelects),this.elements.$massUndo.on("click",this.undoRecord),this.elements.$massDelete.on("click",this.deleteRecord)}initialize(){s.default.configure({parent:".module-loading-indicator",showSpinner:!1}),this.getElements(),this.registerEvents(),TYPO3.settings.Recycler.depthSelection>0?this.elements.$depthSelector.val(TYPO3.settings.Recycler.depthSelection).trigger("change"):a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements()})}resetMassActionButtons(){this.markedRecordsForMassAction=[],this.elements.$massUndo.addClass("disabled").attr("disabled",!0),this.elements.$massUndo.find("span.text").text(TYPO3.lang["button.undo"]),this.elements.$massDelete.addClass("disabled").attr("disabled",!0),this.elements.$massDelete.find("span.text").text(TYPO3.lang["button.delete"])}loadAvailableTables(){return a.default.ajax({url:TYPO3.settings.ajaxUrls.recycler,dataType:"json",data:{action:"getTables",startUid:TYPO3.settings.Recycler.startUid,depth:this.elements.$depthSelector.find("option:selected").val()},beforeSend:()=>{s.default.start(),this.elements.$tableSelector.val(""),this.paging.currentPage=1},success:e=>{const t=[];this.elements.$tableSelector.children().remove(),a.default.each(e,(e,s)=>{const l=s[0],n=s[1],i=(s[2]?s[2]:TYPO3.lang.label_allrecordtypes)+" ("+n+")";t.push(a.default("<option />").val(l).text(i))}),t.length>0&&(this.elements.$tableSelector.append(t),""!==TYPO3.settings.Recycler.tableSelection&&this.elements.$tableSelector.val(TYPO3.settings.Recycler.tableSelection))},complete:()=>{s.default.done()}})}loadDeletedElements(){return a.default.ajax({url:TYPO3.settings.ajaxUrls.recycler,dataType:"json",data:{action:"getDeletedRecords",depth:this.elements.$depthSelector.find("option:selected").val(),startUid:TYPO3.settings.Recycler.startUid,table:this.elements.$tableSelector.find("option:selected").val(),filterTxt:this.elements.$searchTextField.val(),start:(this.paging.currentPage-1)*this.paging.itemsPerPage,limit:this.paging.itemsPerPage},beforeSend:()=>{s.default.start(),this.resetMassActionButtons()},success:e=>{this.elements.$tableBody.html(e.rows),this.buildPaginator(e.totalItems)},complete:()=>{s.default.done()}})}callAjaxAction(e,t,l,n=!1){let r={records:t,action:""},d=!1;if("undo"===e)r.action="undoRecords",r.recursive=n?1:0,d=!0;else{if("delete"!==e)return;r.action="deleteRecords"}return a.default.ajax({url:TYPO3.settings.ajaxUrls.recycler,type:"POST",dataType:"json",data:r,beforeSend:()=>{s.default.start()},success:e=>{e.success?i.success("",e.message):i.error("",e.message),this.paging.currentPage=1,a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements(),l&&this.resetMassActionButtons(),d&&o.refreshPageTree(),this.allToggled=!1})},complete:()=>{s.default.done()}})}createMessage(e,t){return void 0===e?"":e.replace(/\{([0-9]+)\}/g,(function(e,a){return t[a]}))}buildPaginator(e){if(0===e)return void this.elements.$paginator.contents().remove();if(this.paging.totalItems=e,this.paging.totalPages=Math.ceil(e/this.paging.itemsPerPage),1===this.paging.totalPages)return void this.elements.$paginator.contents().remove();const t=a.default("<ul />",{class:"pagination"}),s=[],l=a.default("<li />",{class:"page-item"}).append(a.default("<a />",{class:"page-link","data-action":"previous"}).append(a.default("<span />",{class:"t3-icon fa fa-arrow-left"}))),n=a.default("<li />",{class:"page-item"}).append(a.default("<a />",{class:"page-link","data-action":"next"}).append(a.default("<span />",{class:"t3-icon fa fa-arrow-right"})));1===this.paging.currentPage&&l.disablePagingAction(),this.paging.currentPage===this.paging.totalPages&&n.disablePagingAction();for(let e=1;e<=this.paging.totalPages;e++){const t=a.default("<li />",{class:"page-item"+(this.paging.currentPage===e?" active":"")});t.append(a.default("<a />",{class:"page-link","data-action":"page"}).append(a.default("<span />").text(e))),s.push(t)}t.append(l,s,n),this.elements.$paginator.html(t)}}return a.default.fn.disablePagingAction=function(){a.default(this).addClass("disabled").find(".t3-icon").unwrap().wrap(a.default("<span />",{class:"page-link"}))},new o}));
\ No newline at end of file
+var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","nprogress","TYPO3/CMS/Backend/ActionButton/DeferredAction","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Notification","TYPO3/CMS/Backend/Severity","TYPO3/CMS/Core/Event/RegularEvent","TYPO3/CMS/Backend/Input/Clearable"],(function(e,t,a,s,n,l,i,r,d){"use strict";var o;a=__importDefault(a),s=__importDefault(s),d=__importDefault(d),function(e){e.searchForm="#recycler-form",e.searchText="#recycler-form [name=search-text]",e.searchSubmitBtn="#recycler-form button[type=submit]",e.depthSelector="#recycler-form [name=depth]",e.tableSelector="#recycler-form [name=pages]",e.recyclerTable="#itemsInRecycler",e.paginator="#recycler-index nav",e.reloadAction="a[data-action=reload]",e.undo="a[data-action=undo]",e.delete="a[data-action=delete]",e.massUndo="button[data-multi-record-selection-action=massundo]",e.massDelete="button[data-multi-record-selection-action=massdelete]"}(o||(o={}));class c{constructor(){this.elements={},this.paging={currentPage:1,totalPages:1,totalItems:0,itemsPerPage:TYPO3.settings.Recycler.pagingSize},this.markedRecordsForMassAction=[],this.handleCheckboxStateChanged=e=>{const t=a.default(e.target),s=t.parents("tr"),n=s.data("table")+":"+s.data("uid");if(t.prop("checked"))this.markedRecordsForMassAction.push(n);else{const e=this.markedRecordsForMassAction.indexOf(n);e>-1&&this.markedRecordsForMassAction.splice(e,1)}this.markedRecordsForMassAction.length>0?(this.elements.$massUndo.find("span.text").text(this.createMessage(TYPO3.lang["button.undoselected"],[this.markedRecordsForMassAction.length])),this.elements.$massDelete.find("span.text").text(this.createMessage(TYPO3.lang["button.deleteselected"],[this.markedRecordsForMassAction.length]))):this.resetMassActionButtons()},this.deleteRecord=e=>{if(TYPO3.settings.Recycler.deleteDisable)return;const t=a.default(e.target).parents("tr"),s="TBODY"!==t.parent().prop("tagName");let i,d;if(s)i=this.markedRecordsForMassAction,d=TYPO3.lang["modal.massdelete.text"];else{const e=t.data("uid"),a=t.data("table"),s=t.data("recordtitle");i=[a+":"+e],d="pages"===a?TYPO3.lang["modal.deletepage.text"]:TYPO3.lang["modal.deletecontent.text"],d=this.createMessage(d,[s,"["+i[0]+"]"])}l.confirm(TYPO3.lang["modal.delete.header"],d,r.error,[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){l.dismiss()}},{text:TYPO3.lang["button.delete"],btnClass:"btn-danger",action:new n(()=>Promise.resolve(this.callAjaxAction("delete",i,s)))}])},this.undoRecord=e=>{const t=a.default(e.target).parents("tr"),s="TBODY"!==t.parent().prop("tagName");let i,d,o;if(s)i=this.markedRecordsForMassAction,d=TYPO3.lang["modal.massundo.text"],o=!0;else{const e=t.data("uid"),a=t.data("table"),s=t.data("recordtitle");i=[a+":"+e],o="pages"===a,d=o?TYPO3.lang["modal.undopage.text"]:TYPO3.lang["modal.undocontent.text"],d=this.createMessage(d,[s,"["+i[0]+"]"]),o&&t.data("parentDeleted")&&(d+=TYPO3.lang["modal.undo.parentpages"])}let c=null;c=o?a.default("<div />").append(a.default("<p />").text(d),a.default("<div />",{class:"checkbox"}).append(a.default("<label />").append(TYPO3.lang["modal.undo.recursive"]).prepend(a.default("<input />",{id:"undo-recursive",type:"checkbox"})))):a.default("<p />").text(d),l.confirm(TYPO3.lang["modal.undo.header"],c,r.ok,[{text:TYPO3.lang["button.cancel"],btnClass:"btn-default",trigger:function(){l.dismiss()}},{text:TYPO3.lang["button.undo"],btnClass:"btn-success",action:new n(()=>Promise.resolve(this.callAjaxAction("undo","object"==typeof i?i:[i],s,c.find("#undo-recursive").prop("checked"))))}])},a.default(()=>{this.initialize()})}static refreshPageTree(){top.document.dispatchEvent(new CustomEvent("typo3:pagetree:refresh"))}getElements(){this.elements={$searchForm:a.default(o.searchForm),$searchTextField:a.default(o.searchText),$searchSubmitBtn:a.default(o.searchSubmitBtn),$depthSelector:a.default(o.depthSelector),$tableSelector:a.default(o.tableSelector),$recyclerTable:a.default(o.recyclerTable),$tableBody:a.default(o.recyclerTable).find("tbody"),$paginator:a.default(o.paginator),$reloadAction:a.default(o.reloadAction),$massUndo:a.default(o.massUndo),$massDelete:a.default(o.massDelete)}}registerEvents(){this.elements.$searchForm.on("submit",e=>{e.preventDefault(),""!==this.elements.$searchTextField.val()&&this.loadDeletedElements()}),this.elements.$searchTextField.on("keyup",e=>{""!==a.default(e.currentTarget).val()?this.elements.$searchSubmitBtn.removeClass("disabled"):(this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements())}),this.elements.$searchTextField.get(0).clearable({onClear:()=>{this.elements.$searchSubmitBtn.addClass("disabled"),this.loadDeletedElements()}}),this.elements.$depthSelector.on("change",()=>{a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements()})}),this.elements.$tableSelector.on("change",()=>{this.paging.currentPage=1,this.loadDeletedElements()}),new d.default("click",this.undoRecord).delegateTo(document,o.undo),new d.default("click",this.deleteRecord).delegateTo(document,o.delete),this.elements.$reloadAction.on("click",e=>{e.preventDefault(),a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements()})}),this.elements.$paginator.on("click","a[data-action]",e=>{e.preventDefault();const t=a.default(e.currentTarget);let s=!1;switch(t.data("action")){case"previous":this.paging.currentPage>1&&(this.paging.currentPage--,s=!0);break;case"next":this.paging.currentPage<this.paging.totalPages&&(this.paging.currentPage++,s=!0);break;case"page":this.paging.currentPage=parseInt(t.find("span").text(),10),s=!0}s&&this.loadDeletedElements()}),TYPO3.settings.Recycler.deleteDisable?this.elements.$massDelete.remove():this.elements.$massDelete.show(),this.elements.$recyclerTable.on("show.bs.collapse hide.bs.collapse","tr.collapse",e=>{let t,s,n=a.default(e.currentTarget).prev("tr").find("[data-action=expand]").find(".t3-icon");switch(e.type){case"show":t="t3-icon-pagetree-collapse",s="t3-icon-pagetree-expand";break;case"hide":t="t3-icon-pagetree-expand",s="t3-icon-pagetree-collapse"}n.removeClass(t).addClass(s)}),new d.default("checkbox:state:changed",this.handleCheckboxStateChanged).bindTo(document),new d.default("multiRecordSelection:action:massundo",this.undoRecord).bindTo(document),new d.default("multiRecordSelection:action:massdelete",this.deleteRecord).bindTo(document)}initialize(){s.default.configure({parent:".module-loading-indicator",showSpinner:!1}),this.getElements(),this.registerEvents(),TYPO3.settings.Recycler.depthSelection>0?this.elements.$depthSelector.val(TYPO3.settings.Recycler.depthSelection).trigger("change"):a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements()})}resetMassActionButtons(){this.markedRecordsForMassAction=[],this.elements.$massUndo.find("span.text").text(TYPO3.lang["button.undo"]),this.elements.$massDelete.find("span.text").text(TYPO3.lang["button.delete"]),document.dispatchEvent(new Event("multiRecordSelection:actions:hide"))}loadAvailableTables(){return a.default.ajax({url:TYPO3.settings.ajaxUrls.recycler,dataType:"json",data:{action:"getTables",startUid:TYPO3.settings.Recycler.startUid,depth:this.elements.$depthSelector.find("option:selected").val()},beforeSend:()=>{s.default.start(),this.elements.$tableSelector.val(""),this.paging.currentPage=1},success:e=>{const t=[];this.elements.$tableSelector.children().remove(),a.default.each(e,(e,s)=>{const n=s[0],l=s[1],i=(s[2]?s[2]:TYPO3.lang.label_allrecordtypes)+" ("+l+")";t.push(a.default("<option />").val(n).text(i))}),t.length>0&&(this.elements.$tableSelector.append(t),""!==TYPO3.settings.Recycler.tableSelection&&this.elements.$tableSelector.val(TYPO3.settings.Recycler.tableSelection))},complete:()=>{s.default.done()}})}loadDeletedElements(){return a.default.ajax({url:TYPO3.settings.ajaxUrls.recycler,dataType:"json",data:{action:"getDeletedRecords",depth:this.elements.$depthSelector.find("option:selected").val(),startUid:TYPO3.settings.Recycler.startUid,table:this.elements.$tableSelector.find("option:selected").val(),filterTxt:this.elements.$searchTextField.val(),start:(this.paging.currentPage-1)*this.paging.itemsPerPage,limit:this.paging.itemsPerPage},beforeSend:()=>{s.default.start(),this.resetMassActionButtons()},success:e=>{this.elements.$tableBody.html(e.rows),this.buildPaginator(e.totalItems)},complete:()=>{s.default.done()}})}callAjaxAction(e,t,n,l=!1){let r={records:t,action:""},d=!1;if("undo"===e)r.action="undoRecords",r.recursive=l?1:0,d=!0;else{if("delete"!==e)return;r.action="deleteRecords"}return a.default.ajax({url:TYPO3.settings.ajaxUrls.recycler,type:"POST",dataType:"json",data:r,beforeSend:()=>{s.default.start()},success:e=>{e.success?i.success("",e.message):i.error("",e.message),this.paging.currentPage=1,a.default.when(this.loadAvailableTables()).done(()=>{this.loadDeletedElements(),n&&this.resetMassActionButtons(),d&&c.refreshPageTree()})},complete:()=>{s.default.done()}})}createMessage(e,t){return void 0===e?"":e.replace(/\{([0-9]+)\}/g,(function(e,a){return t[a]}))}buildPaginator(e){if(0===e)return void this.elements.$paginator.contents().remove();if(this.paging.totalItems=e,this.paging.totalPages=Math.ceil(e/this.paging.itemsPerPage),1===this.paging.totalPages)return void this.elements.$paginator.contents().remove();const t=a.default("<ul />",{class:"pagination"}),s=[],n=a.default("<li />",{class:"page-item"}).append(a.default("<a />",{class:"page-link","data-action":"previous"}).append(a.default("<span />",{class:"t3-icon fa fa-arrow-left"}))),l=a.default("<li />",{class:"page-item"}).append(a.default("<a />",{class:"page-link","data-action":"next"}).append(a.default("<span />",{class:"t3-icon fa fa-arrow-right"})));1===this.paging.currentPage&&n.disablePagingAction(),this.paging.currentPage===this.paging.totalPages&&l.disablePagingAction();for(let e=1;e<=this.paging.totalPages;e++){const t=a.default("<li />",{class:"page-item"+(this.paging.currentPage===e?" active":"")});t.append(a.default("<a />",{class:"page-link","data-action":"page"}).append(a.default("<span />").text(e))),s.push(t)}t.append(n,s,l),this.elements.$paginator.html(t)}}return a.default.fn.disablePagingAction=function(){a.default(this).addClass("disabled").find(".t3-icon").unwrap().wrap(a.default("<span />",{class:"page-link"}))},new c}));
\ No newline at end of file
-- 
GitLab