PluginProbe
ElasticPress / 5.3.5
ElasticPress v5.3.5
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / assets / js / comments.js

comments.js in ElasticPress 5.3.5, at assets/js/comments.js

291 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { findAncestorByClass, debounce } from './utils/helpers';
2
3 const widgetSearchComments = document.querySelectorAll('.ep-widget-search-comments');
4
5 let selectedResultIndex;
6
7 widgetSearchComments.forEach((element) => {
8 const { id } = element;
9 const input = document.createElement('input');
10 input.setAttribute('autocomplete', 'off');
11 input.setAttribute('type', 'search');
12 input.setAttribute('class', 'ep-widget-search-comments-input');
13 input.setAttribute('id', `${id}-s`);
14
15 const resultList = document.createElement('ul');
16 resultList.setAttribute('class', 'ep-widget-search-comments-results');
17
18 element.appendChild(input);
19 element.appendChild(resultList);
20 });
21
22 // these are the keycodes we listen for in handleUpDown,
23 // and in handleKeyup
24 const keyCodes = [
25 38, // up
26 40, // down
27 13, // enter
28 ];
29
30 /**
31 * Hide the result list
32 *
33 * @param {HTMLInputElement} inputElement The input element used in the widget
34 */
35 function hideResultsBox(inputElement) {
36 selectedResultIndex = undefined;
37
38 const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments');
39 const resultList = widget.querySelector('.ep-widget-search-comments-results');
40
41 while (resultList.firstChild) {
42 resultList.removeChild(resultList.firstChild);
43 }
44 }
45
46 /**
47 * Update the result list
48 *
49 * @param {object} comments Comments to be showed
50 * @param {HTMLInputElement} inputElement The input element used in the widget
51 */
52 const updateResultsBox = (comments, inputElement) => {
53 let items = '';
54 let itemHTML = '';
55
56 Object.keys(comments).forEach((id, index) => {
57 if (comments[id]?.content && comments[id]?.link) {
58 itemHTML = `
59 <li class="ep-widget-search-comments-result-item">
60 <a href="${comments[id].link}">
61 ${comments[id].content}
62 </a>
63 </li>
64 `;
65
66 if (typeof window.epCommentWidgetItemHTMLFilter !== 'undefined') {
67 itemHTML = window.epCommentWidgetItemHTMLFilter(
68 itemHTML,
69 comments[id],
70 index,
71 inputElement.value,
72 );
73 }
74
75 items += itemHTML;
76 }
77 });
78
79 const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments');
80 const resultList = widget.querySelector('.ep-widget-search-comments-results');
81
82 if (typeof window.epCommentWidgetItemsHTMLFilter !== 'undefined') {
83 items = window.epCommentWidgetItemsHTMLFilter(items, inputElement.value);
84 }
85
86 resultList.innerHTML = items;
87 };
88
89 /**
90 * Update the result list to inform the user that no results were found
91 *
92 * @param {HTMLInputElement} inputElement The input element used in the widget
93 */
94 const showNotFoundInResultsBox = (inputElement) => {
95 const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments');
96 const resultList = widget.querySelector('.ep-widget-search-comments-results');
97
98 let itemHTML = `<li class="ep-widget-search-comments-result-item-not-found">${window.epc.noResultsFoundText}</li>`;
99
100 if (typeof window.epCommentWidgetItemNotFoundHTMLFilter !== 'undefined') {
101 itemHTML = window.epCommentWidgetItemNotFoundHTMLFilter(
102 itemHTML,
103 window.epc.noResultsFoundText,
104 inputElement.value,
105 );
106 }
107
108 resultList.innerHTML = itemHTML;
109 };
110
111 function hasMinimumLength(inputElement) {
112 const minimumLength = window.epc.minimumLengthToSearch || 2;
113 return inputElement?.value?.trim().length >= minimumLength;
114 }
115
116 /**
117 * Add class to the widget element while results are being loaded
118 *
119 * @param {boolean} isLoading Whether results are loading
120 * @param {Node} inputElement Search input field
121 */
122 function setIsLoading(isLoading, inputElement) {
123 const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments');
124
125 if (isLoading) {
126 widget.classList.add('ep-widget-search-comments-is-loading');
127 } else {
128 widget.classList.remove('ep-widget-search-comments-is-loading');
129 }
130 }
131
132 /**
133 * Fetch comments
134 *
135 * @param {HTMLInputElement} inputElement The input element used in the widget
136 * @returns {(false|Promise)} Try to fetch comments
137 */
138 function fetchResults(inputElement) {
139 if (hasMinimumLength(inputElement)) {
140 const widget = findAncestorByClass(inputElement, 'ep-widget-search-comments');
141 const postTypeElement = widget.querySelector('.ep-widget-search-comments-post-type');
142 const postTypeQueryParameter = postTypeElement?.value
143 ? `&post_type=${postTypeElement.value.trim()}`
144 : '';
145
146 setIsLoading(true, inputElement);
147 return fetch(
148 `${window.epc.restApiEndpoint}?s=${inputElement.value.trim()}${postTypeQueryParameter}`,
149 )
150 .then((response) => {
151 if (!response.ok) {
152 throw response;
153 }
154
155 return response.json();
156 })
157 .then((comments) => {
158 if (Object.keys(comments).length === 0) {
159 if (inputElement.value.trim()) {
160 showNotFoundInResultsBox(inputElement);
161 } else {
162 hideResultsBox(inputElement);
163 }
164 } else {
165 updateResultsBox(comments, inputElement);
166 }
167 })
168 .catch(() => {
169 hideResultsBox(inputElement);
170 })
171 .finally(() => {
172 setIsLoading(false, inputElement);
173 });
174 }
175 return false;
176 }
177
178 /**
179 * Handle up, down and enter key
180 *
181 * @param {Event} event keyup event
182 */
183 const handleUpDownEnter = (event) => {
184 if (!keyCodes.includes(event.keyCode)) {
185 return;
186 }
187
188 const widget = findAncestorByClass(event.target, 'ep-widget-search-comments');
189 const resultList = widget.querySelector('.ep-widget-search-comments-results');
190 const sizeResult = resultList.querySelectorAll('.ep-widget-search-comments-result-item').length;
191 const results = resultList.children;
192
193 const previousSelectedResultIndex = selectedResultIndex;
194
195 switch (event.keyCode) {
196 case 38: // Up
197 selectedResultIndex =
198 selectedResultIndex - 1 < 0 || typeof selectedResultIndex === 'undefined'
199 ? sizeResult - 1
200 : selectedResultIndex - 1;
201
202 break;
203
204 case 40: // Down
205 if (
206 typeof selectedResultIndex === 'undefined' ||
207 selectedResultIndex + 1 > sizeResult - 1
208 ) {
209 selectedResultIndex = 0;
210 } else {
211 selectedResultIndex += 1;
212 }
213
214 break;
215
216 case 13: // Enter
217 if (results[selectedResultIndex]?.classList.contains('selected') || sizeResult === 1) {
218 const indexItem = selectedResultIndex || 0;
219
220 if (results[indexItem]) {
221 const linkToComment = results[indexItem]
222 .querySelector('a')
223 ?.getAttribute('href');
224
225 window.location.href = linkToComment;
226 }
227 }
228
229 break;
230
231 default:
232 break;
233 }
234
235 if (typeof previousSelectedResultIndex === 'number') {
236 results[previousSelectedResultIndex].classList.remove('selected');
237 results[previousSelectedResultIndex].setAttribute('aria-selected', 'false');
238 }
239
240 results[selectedResultIndex]?.classList.add('selected');
241 results[selectedResultIndex]?.setAttribute('aria-selected', 'true');
242 };
243
244 const debounceFetchResults = debounce(fetchResults, 500);
245
246 /**
247 * Callback for keyup in Widget Search Comment container.
248 *
249 * Calls a debounced function to get the search results via
250 * api rest request.
251 *
252 * @param {event} event - keyup event
253 */
254 const handleKeyup = (event) => {
255 event.preventDefault();
256 const { target, key, keyCode } = event;
257
258 if (key === 'Escape' || key === 'Esc' || keyCode === 27) {
259 hideResultsBox(target);
260 target.setAttribute('aria-expanded', false);
261
262 return;
263 }
264
265 if (keyCodes.includes(keyCode) && target.value !== '') {
266 handleUpDownEnter(event);
267
268 return;
269 }
270
271 if (hasMinimumLength(target)) {
272 debounceFetchResults(target);
273 } else {
274 hideResultsBox(target);
275 }
276 };
277
278 widgetSearchComments.forEach((element) => {
279 const input = element.querySelector('.ep-widget-search-comments-input');
280
281 input.addEventListener('keyup', handleKeyup);
282 input.addEventListener('keydown', (event) => {
283 if (event.keyCode === 38) {
284 event.preventDefault();
285 }
286 });
287 input.addEventListener('blur', function () {
288 setTimeout(() => hideResultsBox(input), 200);
289 });
290 });
291