MDL-68169 user: Add keyword filter
[moodle.git] / user / amd / src / local / participantsfilter / filter.js
CommitLineData
77ba77f1
AN
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/>.
15
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 */
24import Autocomplete from 'core/form-autocomplete';
25import Selectors from './selectors';
26import {get_string as getString} from 'core/str';
27
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 */
36const getOptionsForSelect = select => {
37 return select.querySelectorAll(':checked');
38};
39
40export default class {
41
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;
51
52 this.addValueSelector();
53 }
54
55 /**
56 * Perform any tear-down for this filter type.
57 */
58 tearDown() {
59 // eslint-disable-line no-empty-function
60 }
61
028ec17c
AN
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 }
70
71 /**
72 * Whether to show suggestions in the autocomplete.
73 *
74 * @return {Boolean}
75 */
76 get showSuggestions() {
77 return true;
78 }
79
77ba77f1
AN
80 /**
81 * Add the value selector to the filter row.
82 */
83 async addValueSelector() {
84 const filterValueNode = this.getFilterValueNode();
85
86 // Copy the data in place.
87 filterValueNode.innerHTML = this.getSourceDataForFilter().outerHTML;
88
89 const dataSource = filterValueNode.querySelector('select');
90
91 Autocomplete.enhance(
92 // The source select element.
93 dataSource,
94
95 // Whether to allow 'tags' (custom entries).
96 dataSource.dataset.allowCustom == "1",
97
98 // We do not require AJAX at all as standard.
99 null,
100
101 // The string to use as a placeholder.
028ec17c 102 await this.placeholder,
77ba77f1
AN
103
104 // Disable case sensitivity on searches.
105 false,
106
107 // Show suggestions.
028ec17c 108 this.showSuggestions,
77ba77f1
AN
109
110 // Do not override the 'no suggestions' string.
111 null,
112
113 // Close the suggestions if this is not a multi-select.
f14df62d
AN
114 !dataSource.multiple,
115
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 }
77ba77f1
AN
122 );
123 }
124
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 }
133
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);
141
142 return filterDataNode.querySelector(Selectors.data.fields.byName(this.filterType));
143 }
144
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 }
153
154 /**
155 * Get the name of this filter.
156 *
157 * @returns {String}
158 */
159 get name() {
160 return this.filterType;
161 }
162
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 }
171
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');
180
181 return Object.values(getOptionsForSelect(filterValueSelect)).map(option => option.value);
182 }
183
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 }
192
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 }
205}