Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ impl Component for ConfigSelector {
false
},
ConfigSelectorMsg::Close(index, DragTarget::Filter) => {
self.filter_dropdown.hide().unwrap();
self.filter_dropdown.reset();
let mut filter = ctx.props().view_config.filter.clone();
filter.remove(index);
let config = ViewConfigUpdate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,13 @@ impl Component for FilterColumn {
false
},
FilterColumnMsg::Close => {
ctx.props().filter_dropdown.hide().unwrap();
ctx.props().filter_dropdown.reset();
false
},
FilterColumnMsg::FilterKeyDown(13) => {
if ctx.props().is_suggestable() {
ctx.props().filter_dropdown.item_select();
ctx.props().filter_dropdown.hide().unwrap();
ctx.props().filter_dropdown.dismiss();
}
false
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub fn row_selector(props: &RowSelectorProps) -> Html {
let on_select = {
clone!(props.dropdown, props.index, props.set_focused_index);
props.on_select.reform(move |key| {
dropdown.hide().unwrap();
dropdown.dismiss();
set_focused_index.emit(Some(index + 1));
key
})
Expand Down
6 changes: 3 additions & 3 deletions rust/perspective-viewer/src/rust/components/empty_row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ impl Component for EmptyRow {
}

fn destroy(&mut self, ctx: &Context<Self>) {
ctx.props().dropdown.hide().unwrap();
ctx.props().dropdown.reset();
}

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
Expand All @@ -121,7 +121,7 @@ impl Component for EmptyRow {
false
},
Blur => {
p.dropdown.hide().unwrap();
p.dropdown.reset();
if let Some(elem) = self.input_ref.cast::<HtmlInputElement>() {
elem.set_value("");
}
Expand All @@ -139,7 +139,7 @@ impl Component for EmptyRow {
},
KeyDown(13) => {
p.dropdown.item_select();
p.dropdown.hide().unwrap();
p.dropdown.dismiss();
p.set_focused_index.emit(Some(p.index + 1));
true
},
Expand Down
201 changes: 100 additions & 101 deletions rust/perspective-viewer/src/rust/components/filter_dropdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;

use perspective_client::clone;
use web_sys::*;
use yew::html::ImplicitClone;
use yew::prelude::*;
Expand All @@ -25,12 +24,19 @@ use crate::ui::PortalModal;
use crate::utils::*;
use crate::*;

#[derive(Default)]
struct FilterDropDownState {
/// The suggestion list while it is on screen, the only state a key may act on.
struct OpenState {
values: Vec<String>,
selected: usize,
on_select: Option<Callback<String>>,
target: Option<HtmlElement>,
on_select: Callback<String>,
target: HtmlElement,
}

#[derive(Default)]
enum FilterDropDownState {
#[default]
Hidden,
Open(OpenState),
}

#[derive(Clone)]
Expand Down Expand Up @@ -62,7 +68,6 @@ impl FilterDropDownElement {
}

pub fn reautocomplete(&self) {
// Re-open portal with current target
self.notify.emit(());
}

Expand All @@ -78,97 +83,94 @@ impl FilterDropDownElement {
match current_column {
Some(filter_col) if filter_col == column => {
let values = filter_values(&input, &self.all_values, &exclude);
if values.len() == 1 && values[0] == input {
let _ = self.hide();
} else {
let mut s = self.state.borrow_mut();
s.values = values;
s.selected = 0;
s.on_select = Some(callback);
if s.target.is_none() {
s.target = Some(target);
}

drop(s);
self.notify.emit(());
}
self.show(values, &input, target, callback);
},
_ => {
clone!(
self.state,
self.session,
self.all_values,
self.notify,
old_column = self.column
);
let this = self.clone();
ApiFuture::spawn(async move {
let fetched =
crate::queries::get_column_values(&session, column.1.clone()).await?;
*all_values.borrow_mut() = Some(fetched);
let values = filter_values(&input, &all_values, &exclude);
let should_hide = values.len() == 1 && values[0] == input;

*old_column.borrow_mut() = Some(column);
{
let mut s = state.borrow_mut();
s.on_select = Some(callback);
if should_hide {
let fv = self::filter_values("", &all_values, &exclude);
s.values = fv;
s.target = Some(target);
} else {
s.values = values;
s.target = Some(target);
}
s.selected = 0;
}
if should_hide {
state.borrow_mut().target = None;
}

notify.emit(());
crate::queries::get_column_values(&this.session, column.1.clone()).await?;
*this.all_values.borrow_mut() = Some(fetched);
*this.column.borrow_mut() = Some(column);
let values = filter_values(&input, &this.all_values, &exclude);
this.show(values, &input, target, callback);
Ok(())
});
},
}
}

pub fn item_select(&self) {
let state = self.state.borrow();
if let Some(value) = state.values.get(state.selected)
&& let Some(ref cb) = state.on_select
{
cb.emit(value.clone());
/// Open the list, or dismiss it when `input` is its one exact match.
fn show(
&self,
values: Vec<String>,
input: &str,
target: HtmlElement,
on_select: Callback<String>,
) {
if values.len() == 1 && values[0] == input {
self.dismiss();
} else {
*self.state.borrow_mut() = FilterDropDownState::Open(OpenState {
values,
selected: 0,
on_select,
target,
});

self.notify.emit(());
}
}

pub fn item_down(&self) {
let mut state = self.state.borrow_mut();
state.selected += 1;
if state.selected >= state.values.len() {
state.selected = 0;
/// Accept a visible list's highlighted suggestion; a hidden list has none.
pub fn item_select(&self) {
let selected = match &*self.state.borrow() {
FilterDropDownState::Open(open) => open
.values
.get(open.selected)
.map(|value| (value.clone(), open.on_select.clone())),
FilterDropDownState::Hidden => None,
};

if let Some((value, on_select)) = selected {
on_select.emit(value);
}
}

drop(state);
self.notify.emit(());
pub fn item_down(&self) {
self.step(1);
}

pub fn item_up(&self) {
let mut state = self.state.borrow_mut();
if state.selected < 1 {
state.selected = state.values.len();
self.step(-1);
}

fn step(&self, delta: isize) {
let moved = match &mut *self.state.borrow_mut() {
FilterDropDownState::Open(open) if !open.values.is_empty() => {
let len = open.values.len() as isize;
open.selected = (open.selected as isize + delta).rem_euclid(len) as usize;
true
},
_ => false,
};

if moved {
self.notify.emit(());
}
}

state.selected -= 1;
drop(state);
/// Close the list, keeping the column's values for the next keystroke.
pub fn dismiss(&self) {
*self.state.borrow_mut() = FilterDropDownState::Hidden;
self.notify.emit(());
}

pub fn hide(&self) -> ApiResult<()> {
self.state.borrow_mut().target = None;
/// Close the list and forget the column so the next focus refetches it.
pub fn reset(&self) {
self.column.borrow_mut().take();
self.notify.emit(());
Ok(())
self.all_values.borrow_mut().take();
self.dismiss();
}
}

Expand Down Expand Up @@ -201,35 +203,32 @@ impl Component for FilterDropDownPortal {
}

fn view(&self, ctx: &Context<Self>) -> Html {
let state = ctx.props().element.state.borrow();
let target = state.target.clone();
let element = &ctx.props().element;
let state = element.state.borrow();
let FilterDropDownState::Open(open) = &*state else {
return html! {};
};

let on_close = {
let element = ctx.props().element.clone();
Callback::from(move |()| {
let _ = element.hide();
})
let element = element.clone();
Callback::from(move |()| element.dismiss())
};

if target.is_some() {
let values = state.values.clone();
let selected = state.selected;
let on_select = state.on_select.clone();
drop(state);

html! {
<PortalModal
tag_name="perspective-dropdown"
sheet={StyleSurface::FilterDropdown.sheet()}
{target}
own_focus=false
{on_close}
theme={ctx.props().theme.clone()}
>
<FilterDropDownView {values} {selected} {on_select} />
</PortalModal>
}
} else {
html! {}
html! {
<PortalModal
tag_name="perspective-dropdown"
sheet={StyleSurface::FilterDropdown.sheet()}
target={Some(open.target.clone())}
own_focus=false
{on_close}
theme={ctx.props().theme.clone()}
>
<FilterDropDownView
values={open.values.clone()}
selected={open.selected}
on_select={open.on_select.clone()}
/>
</PortalModal>
}
}
}
Expand All @@ -238,7 +237,7 @@ impl Component for FilterDropDownPortal {
struct FilterDropDownViewProps {
values: Vec<String>,
selected: usize,
on_select: Option<Callback<String>>,
on_select: Callback<String>,
}

#[function_component]
Expand All @@ -249,7 +248,7 @@ fn FilterDropDownView(props: &FilterDropDownViewProps) -> Html {
.iter()
.enumerate()
.map(|(idx, value)| {
let click = props.on_select.as_ref().unwrap().reform({
let click = props.on_select.reform({
let value = value.clone();
move |_: MouseEvent| value.clone()
});
Expand Down
Loading
Loading