MDL-68169 user: Add keyword filter
[moodle.git] / user / amd / src / local / participantsfilter / filter.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * Base Filter class for a filter type in the participants filter UI.
18  *
19  * @module     core_user/local/participantsfilter/filter
20  * @package    core_user
21  * @copyright  2020 Andrew Nicols <andrew@nicols.co.uk>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 import Autocomplete from 'core/form-autocomplete';
25 import Selectors from './selectors';
26 import {get_string as getString} from 'core/str';
28 /**
29  * Fetch all checked options in the select.
30  *
31  * This is a poor-man's polyfill for select.selectedOptions, which is not available in IE11.
32  *
33  * @param {HTMLSelectElement} select
34  * @returns {HTMLOptionElement[]} All selected options
35  */
36 const getOptionsForSelect = select => {
37     return select.querySelectorAll(':checked');
38 };
40 export default class {
42     /**
43      * Constructor for a new filter.
44      *
45      * @param {String} filterType The type of filter that this relates to
46      * @param {HTMLElement} rootNode The root node for the participants filterset
47      */
48     constructor(filterType, rootNode) {
49         this.filterType = filterType;
50         this.rootNode = rootNode;
52         this.addValueSelector();
53     }
55     /**
56      * Perform any tear-down for this filter type.
57      */
58     tearDown() {
59         // eslint-disable-line no-empty-function
60     }
62     /**
63      * Get the placeholder to use when showing the value selector.
64      *
65      * @return {Promise} Resolving to a String
66      */
67     get placeholder() {
68         return getString('placeholdertypeorselect', 'core_user');
69     }
71     /**
72      * Whether to show suggestions in the autocomplete.
73      *
74      * @return {Boolean}
75      */
76     get showSuggestions() {
77         return true;
78     }
80     /**
81      * Add the value selector to the filter row.
82      */
83     async addValueSelector() {
84         const filterValueNode = this.getFilterValueNode();
86         // Copy the data in place.
87         filterValueNode.innerHTML = this.getSourceDataForFilter().outerHTML;
89         const dataSource = filterValueNode.querySelector('select');
91         Autocomplete.enhance(
92             // The source select element.
93             dataSource,
95             // Whether to allow 'tags' (custom entries).
96             dataSource.dataset.allowCustom == "1",
98             // We do not require AJAX at all as standard.
99             null,
101             // The string to use as a placeholder.
102             await this.placeholder,
104             // Disable case sensitivity on searches.
105             false,
107             // Show suggestions.
108             this.showSuggestions,
110             // Do not override the 'no suggestions' string.
111             null,
113             // Close the suggestions if this is not a multi-select.
114             !dataSource.multiple,
116             // Template overrides.
117             {
118                 items: 'core_user/local/participantsfilter/autocomplete_selection_items',
119                 layout: 'core_user/local/participantsfilter/autocomplete_layout',
120                 selection: 'core_user/local/participantsfilter/autocomplete_selection',
121             }
122         );
123     }
125     /**
126      * Get the root node for this filter.
127      *
128      * @returns {HTMLElement}
129      */
130     get filterRoot() {
131         return this.rootNode.querySelector(Selectors.filter.byName(this.filterType));
132     }
134     /**
135      * Get the possible data for this filter type.
136      *
137      * @returns {Array}
138      */
139     getSourceDataForFilter() {
140         const filterDataNode = this.rootNode.querySelector(Selectors.filterset.regions.datasource);
142         return filterDataNode.querySelector(Selectors.data.fields.byName(this.filterType));
143     }
145     /**
146      * Get the HTMLElement which contains the value selector.
147      *
148      * @returns {HTMLElement}
149      */
150     getFilterValueNode() {
151         return this.filterRoot.querySelector(Selectors.filter.regions.values);
152     }
154     /**
155      * Get the name of this filter.
156      *
157      * @returns {String}
158      */
159     get name() {
160         return this.filterType;
161     }
163     /**
164      * Get the type of join specified.
165      *
166      * @returns {Number}
167      */
168     get jointype() {
169         return this.filterRoot.querySelector(Selectors.filter.fields.join).value;
170     }
172     /**
173      * Get the list of raw values for this filter type.
174      *
175      * @returns {Array}
176      */
177     get rawValues() {
178         const filterValueNode = this.getFilterValueNode();
179         const filterValueSelect = filterValueNode.querySelector('select');
181         return Object.values(getOptionsForSelect(filterValueSelect)).map(option => option.value);
182     }
184     /**
185      * Get the list of values for this filter type.
186      *
187      * @returns {Array}
188      */
189     get values() {
190         return this.rawValues.map(option => parseInt(option, 10));
191     }
193     /**
194      * Get the composed value for this filter.
195      *
196      * @returns {Object}
197      */
198     get filterValue() {
199         return {
200             name: this.name,
201             jointype: this.jointype,
202             values: this.values,
203         };
204     }