Implement extensive search (#1406)

* Implement advanced search

Up to 5 individual tokens (separated by spaces) can be given as search string.
Each token is individually searched for in all selected fields.

Examples (assuming the relevant fields are selected for search):
- a part named `foo` with a tag `bar` will be found with the search string "foo bar".
- a part named `bar baz` will be found with the search string "baz bar".
- a part with the ID 123 and in storage location `a_qux_b` will be found with the search string "qux 123".

* Add tests

These were created with the help of GPT-5.2.
Disclaimer: I don't have the experience to judge the quality or validity of the results.

* Restructure query buildup

* Update tests

* Move options from Settings to localStorage

* Consider mutual exclusivity of search options

If regex search is enabled, the other two options are disabled (only visually). This should give the user a fair idea of what's happening while keeping things as simple as possible.

* Added translations for the checkboxes

* Fix stimulus controller to allow handling multiple instances of the dropdown menu

* Added tooltips for the different search mode options

---------

Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
This commit is contained in:
d-buchmann 2026-07-27 18:42:32 +02:00 committed by GitHub
parent 49ffd3c938
commit 1686df968f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 434 additions and 34 deletions

View file

@ -1,11 +1,11 @@
{% macro settings_drodown(show_label_instead_icon = true) %}
{% macro settings_dropdown(show_label_instead_icon = true) %}
<div class="dropdown">
<button class="btn dropdown-toggle my-2" type="button" id="navbar-search-options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-bs-auto-close="true">
{% if show_label_instead_icon %}{% trans %}search.options.label{% endtrans %}{% else %}<i class="fa-solid fa-gear"></i>{% endif %}
<span class="caret"></span>
</button>
<div class="dropdown-menu" aria-labelledby="navbar-search-options">
<div class="dropdown-menu" aria-labelledby="navbar-search-options" {{ stimulus_controller('elements/search_config') }}>
<div class="px-2" style="width: max-content;">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="search_name" name="name" value="1" checked {{ stimulus_controller('elements/localStorage_checkbox') }}>
@ -66,10 +66,18 @@
</div>
{% endif %}
<hr>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="regex" name="regex" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }}>
<div class="form-check" title="{% trans %}search.regexmatching.help{% endtrans %}">
<input type="checkbox" class="form-check-input" id="regex" name="regex" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }} {{ stimulus_target('elements/search_config', 'regex') }}>
<label for="regex" class="form-check-label justify-content-start">{% trans %}search.regexmatching{% endtrans %}</label>
</div>
<div class="form-check" title="{% trans %}search.extensive_matching.help{% endtrans %}">
<input type="checkbox" class="form-check-input" id="extensive" name="extensive" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }} {{ stimulus_target('elements/search_config', 'extensive') }}>
<label for="extensive" class="form-check-label justify-content-start">{% trans %}search.extensive_matching{% endtrans %}</label>
</div>
<div class="form-check" title="{% trans %}search.permit_wildcards.help{% endtrans %}">
<input type="checkbox" class="form-check-input" id="wildcard" name="wildcard" value="1" {{ stimulus_controller('elements/localStorage_checkbox') }} {{ stimulus_target('elements/search_config', 'wildcard') }}>
<label for="wildcard" class="form-check-label justify-content-start">{% trans %}search.permit_wildcards{% endtrans %}</label>
</div>
</div>
</div>
</div>
@ -85,7 +93,7 @@
{# Show the options left in navbar #}
{% if is_navbar %}
{{ _self.settings_drodown(is_navbar) }}
{{ _self.settings_dropdown(is_navbar) }}
{% endif %}
<div {{ stimulus_controller('elements/part_search') }}
@ -103,7 +111,7 @@
{# And right in the standalone mode #}
{% if not is_navbar %}
{{ _self.settings_drodown(is_navbar) }}
{{ _self.settings_dropdown(is_navbar) }}
{% endif %}
</form>
{% endmacro %}

View file

@ -69,6 +69,14 @@
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.regex %}checked{% endif %}>
<label for="regex" class="form-check-label justify-content-start">{% trans %}search.regexmatching{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.extensive %}checked{% endif %}>
<label for="extensive" class="form-check-label justify-content-start">{% trans %}search.extensive_matching{% endtrans %}</label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" disabled {% if searchFilter.extensive %}checked{% endif %}>
<label for="wildcard" class="form-check-label justify-content-start">{% trans %}search.permit_wildcards{% endtrans %}</label>
</div>
</div>
</div>
</div>