From 123117b4b9d565b91f2a1951aeb12575a3c3c5fc Mon Sep 17 00:00:00 2001 From: Felix Eckhofer <felix@eckhofer.com> Date: Sat, 16 Nov 2024 20:49:41 +0100 Subject: [PATCH] Improve filtered list control - Add X button to clear filter - Restrict maximum height - Visually group list with filter input --- .../controllers/filteredlist_controller.js | 15 ++++++++++- app/views/assignments/_filteredlist.html.erb | 25 ++++++++++++------- app/views/sessions/_session.html.erb | 2 +- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/app/javascript/controllers/filteredlist_controller.js b/app/javascript/controllers/filteredlist_controller.js index 00f8492..102ba2d 100644 --- a/app/javascript/controllers/filteredlist_controller.js +++ b/app/javascript/controllers/filteredlist_controller.js @@ -9,7 +9,7 @@ function debounce(func, wait) { } export default class extends Controller { - static targets = ["input", "list"]; + static targets = ["input", "clear", "list"]; connect() { this.filter = debounce(this.filter_nodebounce.bind(this), 300); @@ -17,6 +17,13 @@ export default class extends Controller { filter_nodebounce() { const query = this.inputTarget.value.toLowerCase(); + + if (query) { + this.clearTarget.classList.remove("hidden"); + } else { + this.clearTarget.classList.add("hidden"); + } + // Filter options by the `data-filteredlist-match` attribute for (const option of this.listTarget.children) { if (option.dataset.filteredlistMatch.includes(query)) { @@ -26,4 +33,10 @@ export default class extends Controller { } } } + + clear_filter() { + this.inputTarget.value = ''; + this.filter_nodebounce(); + this.inputTarget.focus(); + } } diff --git a/app/views/assignments/_filteredlist.html.erb b/app/views/assignments/_filteredlist.html.erb index 100375c..06ed844 100644 --- a/app/views/assignments/_filteredlist.html.erb +++ b/app/views/assignments/_filteredlist.html.erb @@ -1,13 +1,20 @@ <div data-controller="filteredlist"> - <input - type="text" - data-filteredlist-target="input" - data-action="input->filteredlist#filter" - autocomplete="off" - class="w-full border border-gray-300 rounded-md shadow-sm text-sm p-1 focus:ring-indigo-500 focus:border-indigo-500" - placeholder="Filter..." - /> - <ul data-filteredlist-target="list" class="flex flex-row flex-wrap gap-1 my-1 flex-shrink-0"> + <div class="relative"> + <input + type="text" + data-filteredlist-target="input" + data-action="input->filteredlist#filter" + autocomplete="off" + class="w-full bg-white/60 border border-gray-300 rounded-t-md shadow-sm text-sm p-1 pr-8 focus:ring-indigo-500 focus:border-indigo-500" + placeholder="Filter..." + /> + <button + data-action="click->filteredlist#clear_filter" + aria-label="Clear filter" + data-filteredlist-target="clear" + class="absolute hidden inset-y-0 right-0 px-2 text-gray-500"> × </button> + </div> + <ul data-filteredlist-target="list" class="max-h-32 overflow-scroll flex flex-row flex-wrap gap-1 my-1 mt-0 flex-shrink-0 bg-black/10 rounded-b-md p-2"> <% users.each do |user| %> <%= render partial: 'assignments/filteredlist_option', locals: { session: session, user: user } %> <% end %> diff --git a/app/views/sessions/_session.html.erb b/app/views/sessions/_session.html.erb index dd370bf..a0eeadb 100644 --- a/app/views/sessions/_session.html.erb +++ b/app/views/sessions/_session.html.erb @@ -1,5 +1,5 @@ <% unassigned_users = User.all - session.assignments.collect(&:user) %> -<%= turbo_frame_tag dom_id(session), method: "morph" do %> +<%= turbo_frame_tag dom_id(session), method: "morph", class: "w-full" do %> <div class="session shadow hover:shadow-lg overflow-scroll text-sm w-full !h-full min-h-full hover:!min-h-max <%= session.translators_needed? ? "translators-needed" : "no-translators-needed" %> <%= session.backup_needed? ? "backup-needed" : "no-backup-needed" %> <%= session.assignees? ? "has-assignees" : "no-assignees" %> <%= (session.ends_at < Time.now ? "past" : "") %>"> <h4> <small class="text-2xs uppercase font-light bg-black/10 rounded-sm p-1 mr-1 lang-<%= session.language %>"><%= session.language %></small> -- GitLab