Merge pull request #263 from alexanderalber/fix/201-filter-header-row
AutoFilter: a selected header row filters the data below it; search box in the filter menu (#201)
This commit is contained in:
commit
f6628954fc
|
|
@ -431,7 +431,16 @@ fn toggle_filter(s: &mut Session, p: &Json) -> Result<Json> {
|
|||
Ok(json!({"filter": false}))
|
||||
});
|
||||
}
|
||||
let r = data_range(s, p)?;
|
||||
let mut r = data_range(s, p)?;
|
||||
// A selected header row filters the data below it (#201).
|
||||
if p.get("range").is_none() && r.height() == 1 {
|
||||
let d = s.doc()?;
|
||||
let sh = d.wb.active().ok_or(EngineError::NoDocument)?;
|
||||
let below = current_region(sh, r.start).end.row;
|
||||
if below > r.end.row {
|
||||
r = RangeRef::new(r.start, CellRef::new(below, r.end.col));
|
||||
}
|
||||
}
|
||||
edit(s, |cx| {
|
||||
cx.sheet_mut(sheet)?.autofilter = Some(AutoFilter { range: r, criteria: vec![] });
|
||||
Ok(json!({"filter": true, "range": r.a1()}))
|
||||
|
|
|
|||
|
|
@ -512,6 +512,26 @@ fn sort_and_filter() {
|
|||
assert_eq!(r["hiddenRows"], 1);
|
||||
}
|
||||
|
||||
/// Turning the filter on with only the header row selected filters the data below it (#201).
|
||||
#[test]
|
||||
fn filter_on_a_selected_header_row_covers_the_data_below() {
|
||||
let mut s = s();
|
||||
s.execute(
|
||||
"range.setValues",
|
||||
json!({"range": "A1", "values": [["Name", "City"], ["Ana", "SP"], ["Bia", "RJ"], ["Caio", "SP"], ["Duda", "BH"], ["Eva", "RJ"]]}),
|
||||
)
|
||||
.unwrap();
|
||||
s.execute("selection.set", json!({"range": "A1:B1"})).unwrap();
|
||||
let r = s.execute("data.filter", json!({})).unwrap();
|
||||
assert_eq!(r["range"], "A1:B6");
|
||||
let r = s.execute("data.filterBy", json!({"column": "B", "values": ["SP"]})).unwrap();
|
||||
assert_eq!(r["hiddenRows"], 3);
|
||||
// An explicit range is taken as given.
|
||||
s.execute("data.filter", json!({})).unwrap();
|
||||
let r = s.execute("data.filter", json!({"range": "A1:B1"})).unwrap();
|
||||
assert_eq!(r["range"], "A1:B1");
|
||||
}
|
||||
|
||||
/// A sheet with locked data in A1:B3, unlocked cells in D1:D3 and row 10, then protected.
|
||||
fn protected(extra: serde_json::Value) -> Session {
|
||||
let mut s = s();
|
||||
|
|
|
|||
|
|
@ -1803,6 +1803,7 @@ fn filter_menu(app: &mut SheetApp, ui: &mut egui::Ui, geo: &Geo, was_open: bool)
|
|||
let wb = app.session.active().map(|d| d.wb.clone()).unwrap_or(wb);
|
||||
let values = gridcraft_engine::cmd::data::filter_values(&wb, si, col);
|
||||
let key = egui::Id::new(("filter_sel", col));
|
||||
let search_key = egui::Id::new(("filter_search", col));
|
||||
let mut close = false;
|
||||
let area = egui::Area::new(egui::Id::new("filter_menu")).fixed_pos(at + vec2(-180.0, 10.0)).order(egui::Order::Foreground);
|
||||
let response = area.show(ui.ctx(), |ui| {
|
||||
|
|
@ -1819,19 +1820,32 @@ fn filter_menu(app: &mut SheetApp, ui: &mut egui::Ui, geo: &Geo, was_open: bool)
|
|||
ui.separator();
|
||||
ui.label(egui::RichText::new("Filter").strong());
|
||||
let mut checks: Vec<(String, bool)> = ui.ctx().data_mut(|d| d.get_temp::<Vec<(String, bool)>>(key)).unwrap_or(values.clone());
|
||||
let all = checks.iter().all(|(_, b)| *b);
|
||||
// A search narrows the list; Select All and Apply then only cover the matches.
|
||||
let mut search: String = ui.ctx().data_mut(|d| d.get_temp(search_key)).unwrap_or_default();
|
||||
let field = ui.add(egui::TextEdit::singleline(&mut search).hint_text("Search").desired_width(f32::INFINITY));
|
||||
if !was_open {
|
||||
field.request_focus();
|
||||
}
|
||||
let needle = search.trim().to_lowercase();
|
||||
let matches = |v: &str| needle.is_empty() || v.to_lowercase().contains(&needle);
|
||||
let all = checks.iter().filter(|(v, _)| matches(v)).all(|(_, b)| *b);
|
||||
let mut all_new = all;
|
||||
if ui.checkbox(&mut all_new, "(Select All)").changed() {
|
||||
for c in checks.iter_mut() {
|
||||
let label = if needle.is_empty() { "(Select All)" } else { "(Select All Search Results)" };
|
||||
if ui.checkbox(&mut all_new, label).changed() {
|
||||
for c in checks.iter_mut().filter(|(v, _)| matches(v)) {
|
||||
c.1 = all_new;
|
||||
}
|
||||
}
|
||||
egui::ScrollArea::vertical().max_height(220.0).show(ui, |ui| {
|
||||
for (v, on) in checks.iter_mut() {
|
||||
for (v, on) in checks.iter_mut().filter(|(v, _)| matches(v)) {
|
||||
ui.checkbox(on, v.as_str());
|
||||
}
|
||||
});
|
||||
ui.ctx().data_mut(|d| d.insert_temp(key, checks.clone()));
|
||||
ui.ctx().data_mut(|d| {
|
||||
d.insert_temp(key, checks.clone());
|
||||
d.insert_temp(search_key, search.clone());
|
||||
});
|
||||
let checks: Vec<(String, bool)> = checks.iter().map(|(v, on)| (v.clone(), *on && matches(v))).collect();
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Clear Filter").clicked() {
|
||||
|
|
@ -1853,7 +1867,10 @@ fn filter_menu(app: &mut SheetApp, ui: &mut egui::Ui, geo: &Geo, was_open: bool)
|
|||
});
|
||||
});
|
||||
if close || popup_dismissed(&response.response, was_open) {
|
||||
ui.ctx().data_mut(|d| d.remove::<Vec<(String, bool)>>(key));
|
||||
ui.ctx().data_mut(|d| {
|
||||
d.remove::<Vec<(String, bool)>>(key);
|
||||
d.remove::<String>(search_key);
|
||||
});
|
||||
app.grid.filter_menu = None;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue