9472d3b2ee1d7b3e7f49f16268c2135719d7e71c
[yaffs-website] / web / core / modules / system / js / system.modules.es6.js
1 /**
2  * @file
3  * Module page behaviors.
4  */
5
6 (function ($, Drupal, debounce) {
7   /**
8    * Filters the module list table by a text input search string.
9    *
10    * Additionally accounts for multiple tables being wrapped in "package" details
11    * elements.
12    *
13    * Text search input: input.table-filter-text
14    * Target table:      input.table-filter-text[data-table]
15    * Source text:       .table-filter-text-source, .module-name, .module-description
16    *
17    * @type {Drupal~behavior}
18    */
19   Drupal.behaviors.tableFilterByText = {
20     attach(context, settings) {
21       const $input = $('input.table-filter-text').once('table-filter-text');
22       const $table = $($input.attr('data-table'));
23       let $rowsAndDetails;
24       let $rows;
25       let $details;
26       let searching = false;
27
28       function hidePackageDetails(index, element) {
29         const $packDetails = $(element);
30         const $visibleRows = $packDetails.find('tbody tr:visible');
31         $packDetails.toggle($visibleRows.length > 0);
32       }
33
34       function filterModuleList(e) {
35         const query = $(e.target).val();
36         // Case insensitive expression to find query at the beginning of a word.
37         const re = new RegExp(`\\b${query}`, 'i');
38
39         function showModuleRow(index, row) {
40           const $row = $(row);
41           const $sources = $row.find('.table-filter-text-source, .module-name, .module-description');
42           const textMatch = $sources.text().search(re) !== -1;
43           $row.closest('tr').toggle(textMatch);
44         }
45         // Search over all rows and packages.
46         $rowsAndDetails.show();
47
48         // Filter if the length of the query is at least 2 characters.
49         if (query.length >= 2) {
50           searching = true;
51           $rows.each(showModuleRow);
52
53           // Note that we first open all <details> to be able to use ':visible'.
54           // Mark the <details> elements that were closed before filtering, so
55           // they can be reclosed when filtering is removed.
56           $details.not('[open]').attr('data-drupal-system-state', 'forced-open');
57
58           // Hide the package <details> if they don't have any visible rows.
59           // Note that we first show() all <details> to be able to use ':visible'.
60           $details.attr('open', true).each(hidePackageDetails);
61
62           Drupal.announce(
63             Drupal.t(
64               '!modules modules are available in the modified list.',
65               { '!modules': $rowsAndDetails.find('tbody tr:visible').length },
66             ),
67           );
68         }
69         else if (searching) {
70           searching = false;
71           $rowsAndDetails.show();
72           // Return <details> elements that had been closed before filtering
73           // to a closed state.
74           $details.filter('[data-drupal-system-state="forced-open"]')
75             .removeAttr('data-drupal-system-state')
76             .attr('open', false);
77         }
78       }
79
80       function preventEnterKey(event) {
81         if (event.which === 13) {
82           event.preventDefault();
83           event.stopPropagation();
84         }
85       }
86
87       if ($table.length) {
88         $rowsAndDetails = $table.find('tr, details');
89         $rows = $table.find('tbody tr');
90         $details = $rowsAndDetails.filter('.package-listing');
91
92         $input.on({
93           keyup: debounce(filterModuleList, 200),
94           keydown: preventEnterKey,
95         });
96       }
97     },
98   };
99 }(jQuery, Drupal, Drupal.debounce));