Skip to content

Commit 9f9da99

Browse files
roed314claude
andcommitted
Highlight active search constraints on results pages (LMFDB#2770)
On search results pages, inputs that constrain the displayed query are now visually distinguished with a subtle yellow tint so users can see at a glance why results are filtered. What/why: constraint-eligible search boxes render a `search_constraint` class on results pages, plus `search_active` when their value in `info` is non-empty; CSS tints active text/select backgrounds and rings active checkboxes. Display controls (count, sort/column selectors, DynStats variable boxes, and diagram axis/color selectors) set `is_constraint = False` so they are never highlighted. Hidden inputs are untouched. A small JS hook keeps the highlight live as the user edits, pairing with the existing stale/fresh button signal. How verified: flask test client across elliptic curves, number fields, abstract groups, abvar/Fq (checkbox), modular-curve/groups diagram search, and DynStats; browse pages render unchanged; direct unit checks of every box subclass; all 19 color schemes resolve the new `search_active_background` key; /style.css renders; ECQ diagram-search and CMF dynamic-stats tests pass; pyflakes clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 5ef81bd commit 9f9da99

6 files changed

Lines changed: 75 additions & 6 deletions

File tree

lmfdb/templates/diagram_search_form.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,14 @@ <h2 id='search_h2'>{{KNOWL('intro.search', search_header)}}
5252

5353
$("input").on("input", set_fresh);
5454
$("select").on("change", set_fresh);
55+
56+
function update_search_active() {
57+
var box = $(this);
58+
if (box.is("[type=checkbox]")) {
59+
box.toggleClass("search_active", box.prop("checked"));
60+
} else {
61+
box.toggleClass("search_active", Boolean(box.val()));
62+
}
63+
}
64+
$(".search_constraint").on("input change", update_search_active);
5565
</script>

lmfdb/templates/refine_search_form.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,15 @@ <h2 id='search_h2'>{{KNOWL('intro.search', search_header)}}
4141

4242
$("input").on("input", set_fresh);
4343
$("select").on("change", set_fresh);
44+
45+
function update_search_active() {
46+
var box = $(this);
47+
if (box.is("[type=checkbox]")) {
48+
box.toggleClass("search_active", box.prop("checked"));
49+
} else {
50+
box.toggleClass("search_active", Boolean(box.val()));
51+
}
52+
}
53+
$(".search_constraint").on("input change", update_search_active);
4454
</script>
4555

lmfdb/templates/style.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,3 +2333,13 @@ button.search_stale, button.search_stale:hover {
23332333
button.search_fresh {
23342334
background: {{color.button_background}};
23352335
}
2336+
2337+
/* Highlight inputs that are actively constraining the displayed search results */
2338+
input.search_active, select.search_active {
2339+
background: {{color.search_active_background}};
2340+
}
2341+
/* Checkboxes are rendered natively, so mark them with a ring instead */
2342+
input[type=checkbox].search_active {
2343+
outline: 3px solid {{color.search_active_background}};
2344+
outline-offset: 1px;
2345+
}

lmfdb/utils/color.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ class ColorScheme():
196196
# Buttons
197197
'button_background': 'col_main_b',
198198
'select_background': 'col_main_ll',
199+
# Background for search inputs actively constraining the results shown
200+
'search_active_background': '#FFF9C4', # P2-100
199201
'button_border': 'col_main_lg',
200202
'button_background_hover': 'col_main_l',
201203
'button_border_hover': 'col_main_lg',

lmfdb/utils/search_boxes.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,27 @@ class SearchBox(TdElt):
9090
Class abstracting the input boxes used for LMFDB searches.
9191
"""
9292
_default_width = 160
93+
# Whether filling this box constrains the displayed search results
94+
# (set to False for boxes that only affect the display, like the
95+
# number of results to show or the sort order)
96+
is_constraint = True
97+
98+
def _classes(self, info):
99+
"""
100+
The CSS classes for this box's input element: ``advanced`` for inputs
101+
only shown when advanced options are toggled, ``search_constraint``
102+
for inputs that constrain the search results when filled in, and
103+
``search_active`` when such an input is currently filled in, so that
104+
it can be highlighted on search results pages.
105+
"""
106+
classes = []
107+
if self.advanced:
108+
classes.append("advanced")
109+
if info is not None and self.is_constraint:
110+
classes.append("search_constraint")
111+
if info.get(self.name) not in (None, ""):
112+
classes.append("search_active")
113+
return classes
93114

94115
def __init__(
95116
self,
@@ -223,8 +244,9 @@ def _input(self, info):
223244
keys = self.extra + ['type="text"', 'name="%s"' % self.name]
224245
if self.id is not None:
225246
keys.append('id="%s"' % self.id)
226-
if self.advanced:
227-
keys.append('class="advanced"')
247+
classes = self._classes(info)
248+
if classes:
249+
keys.append('class="%s"' % " ".join(classes))
228250
if self.example is not None:
229251
if self.example_value and info is None:
230252
keys.append('value="%s"' % self.example)
@@ -312,8 +334,9 @@ def _input(self, info):
312334
keys = self.extra + ['name="%s"' % self.name]
313335
if self.id is not None:
314336
keys.append('id="%s"' % self.id)
315-
if self.advanced:
316-
keys.append('class="advanced"')
337+
classes = self._classes(info)
338+
if classes:
339+
keys.append('class="%s"' % " ".join(classes))
317340
if self.example_value and info is None:
318341
info = {self.name:self.example}
319342
if info is None:
@@ -368,8 +391,9 @@ def _input(self, info=None):
368391
class CheckBox(SearchBox):
369392
def _input(self, info=None):
370393
keys = ['name="%s"' % self.name, 'value="yes"']
371-
if self.advanced:
372-
keys.append('class="advanced"')
394+
classes = self._classes(info)
395+
if classes:
396+
keys.append('class="%s"' % " ".join(classes))
373397
if info is not None and info.get(self.name, False):
374398
keys.append("checked")
375399
return '<input type="checkbox" %s>' % (" ".join(keys),)
@@ -471,6 +495,8 @@ class SubsetNoExcludeBox(SelectBox):
471495
('subset', 'subset')]
472496

473497
class CountBox(TextBox):
498+
is_constraint = False
499+
474500
def __init__(self):
475501
TextBox.__init__(
476502
self,
@@ -483,6 +509,7 @@ def __init__(self):
483509

484510
class ColumnController(SelectBox):
485511
wrap_mixins = {'width': '170px'}
512+
is_constraint = False
486513

487514
def __init__(self):
488515
super().__init__(
@@ -559,6 +586,7 @@ def _input(self, info):
559586

560587
class SortController(SelectBox):
561588
wrap_mixins = {'width': '170px'}
589+
is_constraint = False
562590

563591
def __init__(self, options, knowl):
564592
extra = [
@@ -801,6 +829,10 @@ def dynstats_array(self, info):
801829
label="Proportions" if i == 1 else "",
802830
rowspan=(1, 2),
803831
knowl="stats.proportions" if i == 1 else None)
832+
for box in (cols, buckets, totals, proportions):
833+
# These select the variables and format for the statistics
834+
# display rather than constraining the underlying results
835+
box.is_constraint = False
804836
if i == 1:
805837
array.append([cols, buckets, totals, proportions])
806838
else:

lmfdb/utils/search_wrapper.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,11 @@ def flatten(L):
574574
SelectBox(name="color", label="Color", options=color_fields),
575575
CountBox(),
576576
]
577+
for box in info["diagram_boxes"]:
578+
# These choose how the diagram is drawn (which fields go on each
579+
# axis and how many points to show) rather than constraining the
580+
# underlying results, so they are not highlighted as active.
581+
box.is_constraint = False
577582

578583
# Build query using the same parsing function
579584
data = self.make_query(info, False)

0 commit comments

Comments
 (0)