Debounce user search

This commit is contained in:
Alexandre Iooss 2020-08-21 19:12:28 +02:00
parent 5ea1eed76d
commit 83d2c18d1e
2 changed files with 54 additions and 43 deletions

View File

@ -36,43 +36,40 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% block extrajavascript %} {% block extrajavascript %}
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function () { let pattern = '';
let old_pattern = null;
let searchbar_obj = $("#searchbar");
var timer_on = false;
var timer;
function reloadTable() { function reloadTable() {
let pattern = searchbar_obj.val(); pattern = $("#searchbar").val();
if (pattern === old_pattern || pattern === "") if (pattern.length > 2)
return; $("#user_table").load(location.pathname + "?search=" + pattern.replace(" ", "%20") + " #user_table", init_table);
}
$("#user_table").load(location.pathname + "?search=" + pattern.replace(" ", "%20") + " #user_table", init); function init_table() {
} // On row click, go to object
$(".table-row").click(function () {
searchbar_obj.keyup(function () { window.document.location = $(this).data("href");
if (timer_on)
clearTimeout(timer);
timer_on = true;
setTimeout(reloadTable, 0);
}); });
function init() { // Highlight searched terms
$(".table-row").click(function () { $("tr").each(function () {
window.document.location = $(this).data("href"); $(this).find("td:eq(0), td:eq(1), td:eq(2), td:eq(3), td:eq(5)").each(function () {
timer_on = false; $(this).html($(this).text().replace(new RegExp(pattern, 'i'), "<mark>$&</mark>"));
}); });
});
}
// Highlight searched terms $(document).ready(function () {
$("tr").each(function () { // Recover last search from url
$(this).find("td:eq(0), td:eq(1), td:eq(2), td:eq(3), td:eq(5)").each(function () { let searchParams = new URLSearchParams(window.location.search)
$(this).html($(this).text().replace(new RegExp(searchbar_obj.val(), 'i'), "<mark>$&</mark>")); if (searchParams.has('search'))
}); pattern = searchParams.get('search');
});
}
init(); // On search, refresh table
$("#searchbar").keyup(debounce(reloadTable, 300));
// First init
init_table();
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -7,7 +7,7 @@
* @param value the balance, in cents * @param value the balance, in cents
* @returns {string} * @returns {string}
*/ */
function pretty_money(value) { function pretty_money (value) {
if (value % 100 === 0) if (value % 100 === 0)
return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + " €"; return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + " €";
else else
@ -21,7 +21,7 @@ function pretty_money(value) {
* @param alert_type The type of the alert. Choices: info, success, warning, danger * @param alert_type The type of the alert. Choices: info, success, warning, danger
* @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored. * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored.
*/ */
function addMsg(msg, alert_type, timeout = -1) { function addMsg (msg, alert_type, timeout = -1) {
let msgDiv = $("#messages"); let msgDiv = $("#messages");
let html = msgDiv.html(); let html = msgDiv.html();
let id = Math.floor(10000 * Math.random() + 1); let id = Math.floor(10000 * Math.random() + 1);
@ -42,7 +42,7 @@ function addMsg(msg, alert_type, timeout = -1) {
* @param errs_obj [{error_code:erro_message}] * @param errs_obj [{error_code:erro_message}]
* @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored. * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored.
*/ */
function errMsg(errs_obj, timeout = -1) { function errMsg (errs_obj, timeout = -1) {
for (const err_msg of Object.values(errs_obj)) { for (const err_msg of Object.values(errs_obj)) {
addMsg(err_msg, 'danger', timeout); addMsg(err_msg, 'danger', timeout);
} }
@ -51,9 +51,9 @@ function errMsg(errs_obj, timeout = -1) {
var reloadWithTurbolinks = (function () { var reloadWithTurbolinks = (function () {
var scrollPosition; var scrollPosition;
function reload() { function reload () {
scrollPosition = [window.scrollX, window.scrollY]; scrollPosition = [window.scrollX, window.scrollY];
Turbolinks.visit(window.location.toString(), {action: 'replace'}) Turbolinks.visit(window.location.toString(), { action: 'replace' })
} }
document.addEventListener('turbolinks:load', function () { document.addEventListener('turbolinks:load', function () {
@ -69,7 +69,7 @@ var reloadWithTurbolinks = (function () {
/** /**
* Reload the balance of the user on the right top corner * Reload the balance of the user on the right top corner
*/ */
function refreshBalance() { function refreshBalance () {
$("#user_balance").load("/ #user_balance"); $("#user_balance").load("/ #user_balance");
} }
@ -78,14 +78,14 @@ function refreshBalance() {
* @param pattern The pattern that is queried * @param pattern The pattern that is queried
* @param fun For each found note with the matched alias `alias`, fun(note, alias) is called. * @param fun For each found note with the matched alias `alias`, fun(note, alias) is called.
*/ */
function getMatchedNotes(pattern, fun) { function getMatchedNotes (pattern, fun) {
$.getJSON("/api/note/alias/?format=json&alias=" + pattern + "&search=user|club&ordering=normalized_name", fun); $.getJSON("/api/note/alias/?format=json&alias=" + pattern + "&search=user|club&ordering=normalized_name", fun);
} }
/** /**
* Generate a <li> entry with a given id and text * Generate a <li> entry with a given id and text
*/ */
function li(id, text, extra_css) { function li (id, text, extra_css) {
return "<li class=\"list-group-item py-1 px-2 d-flex justify-content-between align-items-center text-truncate " return "<li class=\"list-group-item py-1 px-2 d-flex justify-content-between align-items-center text-truncate "
+ (extra_css ? extra_css : "") + "\"" + " id=\"" + id + "\">" + text + "</li>\n"; + (extra_css ? extra_css : "") + "\"" + " id=\"" + id + "\">" + text + "</li>\n";
} }
@ -94,7 +94,7 @@ function li(id, text, extra_css) {
* Return style to apply according to the balance of the note and the validation status of the email address * Return style to apply according to the balance of the note and the validation status of the email address
* @param note The concerned note. * @param note The concerned note.
*/ */
function displayStyle(note) { function displayStyle (note) {
if (!note) if (!note)
return ""; return "";
let balance = note.balance; let balance = note.balance;
@ -120,7 +120,7 @@ function displayStyle(note) {
* @param user_note_field * @param user_note_field
* @param profile_pic_field * @param profile_pic_field
*/ */
function displayNote(note, alias, user_note_field = null, profile_pic_field = null) { function displayNote (note, alias, user_note_field = null, profile_pic_field = null) {
if (!note.display_image) { if (!note.display_image) {
note.display_image = '/media/pic/default.png'; note.display_image = '/media/pic/default.png';
} }
@ -152,7 +152,7 @@ function displayNote(note, alias, user_note_field = null, profile_pic_field = nu
* (useful in consumptions, put null if not used) * (useful in consumptions, put null if not used)
* @returns an anonymous function to be compatible with jQuery events * @returns an anonymous function to be compatible with jQuery events
*/ */
function removeNote(d, note_prefix = "note", notes_display, note_list_id, user_note_field = null, profile_pic_field = null) { function removeNote (d, note_prefix = "note", notes_display, note_list_id, user_note_field = null, profile_pic_field = null) {
return (function () { return (function () {
let new_notes_display = []; let new_notes_display = [];
let html = ""; let html = "";
@ -199,8 +199,8 @@ function removeNote(d, note_prefix = "note", notes_display, note_list_id, user_n
* the associated note is not displayed. * the associated note is not displayed.
* Useful for a consumption if the item is selected before. * Useful for a consumption if the item is selected before.
*/ */
function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_prefix = "alias", function autoCompleteNote (field_id, note_list_id, notes, notes_display, alias_prefix = "alias",
note_prefix = "note", user_note_field = null, profile_pic_field = null, alias_click = null) { note_prefix = "note", user_note_field = null, profile_pic_field = null, alias_click = null) {
let field = $("#" + field_id); let field = $("#" + field_id);
// Configure tooltip // Configure tooltip
@ -348,7 +348,7 @@ function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_pr
// When a validate button is clicked, we switch the validation status // When a validate button is clicked, we switch the validation status
function de_validate(id, validated, resourcetype) { function de_validate (id, validated, resourcetype) {
let validate_obj = $("#validate_" + id); let validate_obj = $("#validate_" + id);
if (validate_obj.data("pending")) if (validate_obj.data("pending"))
@ -392,3 +392,17 @@ function de_validate(id, validated, resourcetype) {
} }
}); });
} }
/**
* Simple debouncer
* @param callback Function to call
* @param wait Debounced milliseconds
*/
function debounce (callback, wait) {
let timeout;
return (...args) => {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => callback.apply(context, args), wait);
};
}