feat: added MB4/MB5 and Esc navigation globally (#742)
This commit is contained in:
parent
b2c2289853
commit
12783fe59a
20 changed files with 229 additions and 29 deletions
|
|
@ -327,7 +327,7 @@ impl Component for Button {
|
|||
})
|
||||
.cursor(cursor_icon)
|
||||
.map(on_press.clone(), |rect, handler| {
|
||||
rect.on_all_press(move |event: Event<PressEventData>| {
|
||||
rect.on_press(move |event: Event<PressEventData>| {
|
||||
handler.call(event);
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use freya::prelude::*;
|
||||
|
||||
use crate::components::{Icon, IconType, ScrollArea};
|
||||
use crate::hooks::use_overlay_claim_when;
|
||||
use crate::theme::colors;
|
||||
use crate::ui::border_all_color;
|
||||
|
||||
|
|
@ -70,6 +71,8 @@ impl Component for Dropdown {
|
|||
let on_select = self.on_select.clone();
|
||||
let is_open = open();
|
||||
|
||||
use_overlay_claim_when(is_open);
|
||||
|
||||
if !is_open && list_size().is_some() {
|
||||
let _ = list_size.take();
|
||||
}
|
||||
|
|
@ -108,6 +111,11 @@ impl Component for Dropdown {
|
|||
|
||||
rect()
|
||||
.width(self.width.clone())
|
||||
.on_global_key_down(move |e: Event<KeyboardEventData>| {
|
||||
if e.key == Key::Named(NamedKey::Escape) {
|
||||
open.set_if_modified(false);
|
||||
}
|
||||
})
|
||||
.child(
|
||||
rect()
|
||||
.width(Size::fill())
|
||||
|
|
@ -130,7 +138,7 @@ impl Component for Dropdown {
|
|||
.on_sized(move |e: Event<SizedEventData>| {
|
||||
button_area.set_if_modified(Some(e.area));
|
||||
})
|
||||
.on_all_press(move |e: Event<PressEventData>| {
|
||||
.on_press(move |e: Event<PressEventData>| {
|
||||
e.prevent_default();
|
||||
e.stop_propagation();
|
||||
open.toggle();
|
||||
|
|
@ -237,7 +245,7 @@ impl Component for DropdownOption {
|
|||
})
|
||||
.on_pointer_enter(move |_| hovering.set(true))
|
||||
.on_pointer_leave(move |_| hovering.set(false))
|
||||
.on_all_press(self.on_press.clone())
|
||||
.on_press(self.on_press.clone())
|
||||
.child(
|
||||
label()
|
||||
.text(self.text.clone())
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ impl Component for NavLink {
|
|||
.a11y_id(a11y_id)
|
||||
.a11y_focusable(true)
|
||||
.a11y_role(AccessibilityRole::Button)
|
||||
.on_all_press(move |e: Event<PressEventData>| {
|
||||
.on_press(move |e: Event<PressEventData>| {
|
||||
e.prevent_default();
|
||||
match &target {
|
||||
NavTarget::Route(route) => {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ use freya::{
|
|||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::hooks::use_overlay_claim;
|
||||
|
||||
const SCRIM_ALPHA: f32 = 90.;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
|
|
@ -61,6 +63,8 @@ impl KeyExt for OverlayPopup {
|
|||
|
||||
impl Component for OverlayPopup {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
use_overlay_claim();
|
||||
|
||||
let a11y_id = use_a11y();
|
||||
let scrim_close = self.on_close.clone();
|
||||
let key_close = self.on_close.clone();
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ impl Component for ClusterCard {
|
|||
.y(0.)
|
||||
.color(Color::from_af32rgb(0.3, 0, 0, 0)),
|
||||
)
|
||||
.on_all_press(on_press)
|
||||
.on_press(on_press)
|
||||
.child(
|
||||
rect()
|
||||
.width(Size::fill())
|
||||
|
|
@ -281,7 +281,7 @@ impl Component for OtherVersionsTile {
|
|||
.y(0.)
|
||||
.color(Color::from_af32rgb(0.3, 0, 0, 0)),
|
||||
)
|
||||
.on_all_press(|_| {
|
||||
.on_press(|_| {
|
||||
let _ = RouterContext::get().push(Route::Clusters {});
|
||||
})
|
||||
.child(
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ impl<T: Copy + PartialEq + 'static> Component for SegmentButton<T> {
|
|||
})
|
||||
.maybe(!disabled, |el| {
|
||||
el.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(move |_| *selected.write() = value)
|
||||
.on_press(move |_| *selected.write() = value)
|
||||
})
|
||||
.maybe_child(self.icon.map(|icon| {
|
||||
let mut ic = Icon::new(icon);
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ impl Component for TabButton {
|
|||
.cursor(CursorIcon::Pointer);
|
||||
|
||||
if let Some(handler) = self.on_press.clone() {
|
||||
el = el.on_all_press(move |e| handler.call(e));
|
||||
el = el.on_press(move |e| handler.call(e));
|
||||
}
|
||||
|
||||
el.child(
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ impl Component for Switch {
|
|||
.a11y_focusable(!disabled)
|
||||
.a11y_role(AccessibilityRole::Button)
|
||||
.maybe(!disabled, |el| {
|
||||
el.cursor(CursorIcon::Pointer).on_all_press(move |_| {
|
||||
el.cursor(CursorIcon::Pointer).on_press(move |_| {
|
||||
on_press.call(());
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl Component for VersionCard {
|
|||
.a11y_id(a11y_id)
|
||||
.a11y_focusable(true)
|
||||
.a11y_role(AccessibilityRole::Button)
|
||||
.on_all_press(move |e| on_press.call(e))
|
||||
.on_press(move |e| on_press.call(e))
|
||||
.on_pointer_enter(move |_| {
|
||||
*hovering.write() = true;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
mod active_cluster;
|
||||
mod debounce;
|
||||
mod actions;
|
||||
mod overlay_claims;
|
||||
mod queries;
|
||||
mod view_state;
|
||||
|
||||
pub use debounce::use_debounced;
|
||||
pub use overlay_claims::{
|
||||
OverlayClaims, use_overlay_claim, use_overlay_claim_when, use_overlay_claims,
|
||||
use_provide_overlay_claims,
|
||||
};
|
||||
pub use view_state::{PersistedView, use_view_state};
|
||||
|
||||
pub use active_cluster::{
|
||||
|
|
|
|||
64
packages/oneclient_app/src/hooks/overlay_claims.rs
Normal file
64
packages/oneclient_app/src/hooks/overlay_claims.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use freya::prelude::*;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct OverlayClaims(Rc<Cell<usize>>);
|
||||
|
||||
impl OverlayClaims {
|
||||
/// Whether anything on screen currently owns Escape and mouse-back.
|
||||
pub fn any(&self) -> bool {
|
||||
self.0.get() > 0
|
||||
}
|
||||
|
||||
fn acquire(&self) {
|
||||
self.0.set(self.0.get() + 1);
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
let held = self.0.get();
|
||||
debug_assert!(held > 0, "overlay claim released without a matching acquire");
|
||||
self.0.set(held.saturating_sub(1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_provide_overlay_claims() {
|
||||
use_provide_root_context(OverlayClaims::default);
|
||||
}
|
||||
|
||||
/// Falls back to a detached counter outside `RootLayout` so a claim raised there
|
||||
/// degrades to "nothing claimed" instead of taking the app down.
|
||||
pub fn use_overlay_claims() -> OverlayClaims {
|
||||
try_consume_root_context::<OverlayClaims>().unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn use_overlay_claim() {
|
||||
let claims = use_hook(use_overlay_claims);
|
||||
let released = claims.clone();
|
||||
|
||||
use_hook(move || claims.acquire());
|
||||
use_drop(move || released.release());
|
||||
}
|
||||
|
||||
pub fn use_overlay_claim_when(active: bool) {
|
||||
let claims = use_hook(use_overlay_claims);
|
||||
let held = use_hook(|| Rc::new(Cell::new(false)));
|
||||
|
||||
if held.get() != active {
|
||||
held.set(active);
|
||||
if active {
|
||||
claims.acquire();
|
||||
} else {
|
||||
claims.release();
|
||||
}
|
||||
}
|
||||
|
||||
let released = claims;
|
||||
let held_at_drop = held;
|
||||
use_drop(move || {
|
||||
if held_at_drop.get() {
|
||||
released.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -2,8 +2,9 @@ use freya::animation::*;
|
|||
use freya::prelude::*;
|
||||
use freya::router::*;
|
||||
|
||||
use super::app_shell::{appshell_overlay, back_button, hides_overlay};
|
||||
use super::app_shell::{appshell_overlay, back_button, hides_overlay, navigate_back};
|
||||
use crate::Route;
|
||||
use crate::hooks::use_overlay_claims;
|
||||
use crate::theme;
|
||||
use crate::ui::entrance_motion_layer;
|
||||
|
||||
|
|
@ -46,6 +47,41 @@ fn is_cluster_route(route: &Route) -> bool {
|
|||
)
|
||||
}
|
||||
|
||||
/// Checks if it should go back to home or somewhere else
|
||||
fn escape_exits_section(route: &Route) -> bool {
|
||||
is_cluster_route(route) || is_sidebar_route(route)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
enum EscapeAction {
|
||||
/// The root screen has nothing above it so Escape must not walk history
|
||||
Ignore,
|
||||
ExitSection,
|
||||
Back,
|
||||
}
|
||||
|
||||
fn escape_action(route: &Route) -> EscapeAction {
|
||||
if matches!(route, Route::Home {}) {
|
||||
EscapeAction::Ignore
|
||||
} else if escape_exits_section(route) {
|
||||
EscapeAction::ExitSection
|
||||
} else {
|
||||
EscapeAction::Back
|
||||
}
|
||||
}
|
||||
|
||||
/// Escape belongs to whatever the user is typing in before it belongs to the
|
||||
/// router so a search field does not throw the page away mid-word
|
||||
fn is_text_entry(role: AccessibilityRole) -> bool {
|
||||
matches!(
|
||||
role,
|
||||
AccessibilityRole::TextInput
|
||||
| AccessibilityRole::MultilineTextInput
|
||||
| AccessibilityRole::SearchInput
|
||||
| AccessibilityRole::PasswordInput
|
||||
)
|
||||
}
|
||||
|
||||
fn enter_kind(from: &Route, to: &Route) -> Enter {
|
||||
if (is_sidebar_route(from) && is_sidebar_route(to))
|
||||
|| (is_cluster_route(from) && is_cluster_route(to))
|
||||
|
|
@ -66,6 +102,9 @@ impl Component for AnimatedAppOutlet {
|
|||
let mut router = use_animated_router::<Route>();
|
||||
|
||||
let route = use_route::<Route>();
|
||||
let at_home = matches!(&route, Route::Home {});
|
||||
let escape = escape_action(&route);
|
||||
|
||||
let mut origin_route = use_state(|| route.clone());
|
||||
let mut last_route = use_state(|| route.clone());
|
||||
|
||||
|
|
@ -79,6 +118,48 @@ impl Component for AnimatedAppOutlet {
|
|||
|
||||
let back_title = origin_route.read().title();
|
||||
|
||||
let platform = Platform::get();
|
||||
let focused_node = platform.focused_accessibility_node;
|
||||
let focused_id = platform.focused_accessibility_id;
|
||||
|
||||
let key_claims = use_overlay_claims();
|
||||
let pointer_claims = key_claims.clone();
|
||||
|
||||
let on_global_key = move |e: Event<KeyboardEventData>| {
|
||||
if e.key != Key::Named(NamedKey::Escape) || key_claims.any() {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_text_entry(focused_node.peek().role()) {
|
||||
focused_id.peek().request_unfocus();
|
||||
return;
|
||||
}
|
||||
|
||||
match escape {
|
||||
EscapeAction::Ignore => {}
|
||||
EscapeAction::ExitSection => {
|
||||
let _ = RouterContext::get().push(Route::Home {});
|
||||
}
|
||||
EscapeAction::Back => navigate_back(at_home),
|
||||
}
|
||||
};
|
||||
|
||||
let on_global_pointer = move |e: Event<PointerEventData>| {
|
||||
if pointer_claims.any() {
|
||||
return;
|
||||
}
|
||||
match e.button() {
|
||||
Some(MouseButton::Back) => navigate_back(at_home),
|
||||
Some(MouseButton::Forward) => {
|
||||
let router = RouterContext::get();
|
||||
if router.can_go_forward() {
|
||||
router.go_forward();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
let anim = use_animation(|_conf| {
|
||||
AnimNum::new(0., 1.)
|
||||
.time(430)
|
||||
|
|
@ -170,7 +251,28 @@ impl Component for AnimatedAppOutlet {
|
|||
.width(Size::fill())
|
||||
.height(Size::fill())
|
||||
.overflow(Overflow::Clip)
|
||||
.on_global_key_down(on_global_key)
|
||||
.on_global_pointer_press(on_global_pointer)
|
||||
.maybe_child(overlay)
|
||||
.child(column)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn escape_action_per_route() {
|
||||
assert_eq!(escape_action(&Route::Home {}), EscapeAction::Ignore);
|
||||
assert_eq!(
|
||||
escape_action(&Route::ClusterSettings { cluster_id: 1 }),
|
||||
EscapeAction::ExitSection
|
||||
);
|
||||
assert_eq!(
|
||||
escape_action(&Route::SettingsJava {}),
|
||||
EscapeAction::ExitSection
|
||||
);
|
||||
assert_eq!(escape_action(&Route::Clusters {}), EscapeAction::Back);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -600,12 +600,22 @@ pub(crate) fn gradient_overlay_radial() -> impl IntoElement {
|
|||
))
|
||||
}
|
||||
|
||||
pub(crate) fn navigate_back(at_home: bool) {
|
||||
let router = RouterContext::get();
|
||||
if router.can_go_back() {
|
||||
router.go_back();
|
||||
} else if !at_home {
|
||||
let _ = router.push(Route::Home {});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn back_button(destination: &str) -> impl IntoElement {
|
||||
Button::new()
|
||||
.ghost()
|
||||
.small()
|
||||
.on_press(|_| {
|
||||
RouterContext::get().go_back();
|
||||
// Only rendered off home so the history-empty fallback is reachable
|
||||
navigate_back(false);
|
||||
})
|
||||
.margin(Gaps::new(0., 0., 0., 32.))
|
||||
.child(Icon::new(IconType::ArrowLeft).size(12.))
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::components::{
|
|||
NotificationCenter, PackageUpdatePopup, SplashCurtain, StatusBar, Toasts,
|
||||
UpdatePromptOverlay,
|
||||
};
|
||||
use crate::hooks::{SplashState, use_provide_splash};
|
||||
use crate::hooks::{SplashState, use_provide_overlay_claims, use_provide_splash};
|
||||
use crate::layout::HomeArtPrefetch;
|
||||
use crate::motion::AnimationClockDriver;
|
||||
use crate::routes::Route;
|
||||
|
|
@ -21,6 +21,7 @@ impl Component for RootLayout {
|
|||
let active = use_state(|| false);
|
||||
let home_ready = use_state(|| false);
|
||||
use_provide_splash(SplashState { active, home_ready });
|
||||
use_provide_overlay_claims();
|
||||
|
||||
// macOS rounds the window natively so Freya must not round on top of it
|
||||
#[cfg(target_os = "macos")]
|
||||
|
|
|
|||
|
|
@ -797,7 +797,7 @@ impl Component for SidebarItem {
|
|||
.on_pointer_enter(move |_| *hovering.write() = true)
|
||||
.on_pointer_leave(move |_| *hovering.write() = false)
|
||||
.map(route, |el, route| {
|
||||
el.on_all_press(move |_| {
|
||||
el.on_press(move |_| {
|
||||
let _ = RouterContext::get().replace(route.clone());
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -125,14 +125,13 @@ impl Component for PackageCard {
|
|||
.a11y_focusable(true)
|
||||
.a11y_role(AccessibilityRole::Button)
|
||||
.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(move |_| open_package(cluster_id, &package_type, provider, &id))
|
||||
.on_press(move |_| open_package(cluster_id, &package_type, provider, &id))
|
||||
.child(
|
||||
rect()
|
||||
.margin(Gaps::new_all(1.))
|
||||
.corner_radius(CornerRadius::new(10., 10., 0., 0.))
|
||||
.overflow(Overflow::Clip)
|
||||
.child(PackageBanner::new(icon_url, BANNER_H))
|
||||
// Top-left of the banner the first thing worth knowing about a result and the opposite corner is the install button's
|
||||
.maybe_child(self.installed.map(|installed| {
|
||||
rect()
|
||||
.position(Position::new_absolute().top(8.).left(8.))
|
||||
|
|
@ -258,7 +257,7 @@ impl Component for ListRow {
|
|||
.a11y_focusable(true)
|
||||
.a11y_role(AccessibilityRole::Button)
|
||||
.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(move |_| open_package(cluster_id, &package_type, provider, &id))
|
||||
.on_press(move |_| open_package(cluster_id, &package_type, provider, &id))
|
||||
.child(Thumbnail::new(item.icon_url.clone(), 48.).radius(8.))
|
||||
.child(
|
||||
rect()
|
||||
|
|
@ -303,7 +302,6 @@ impl Component for ListRow {
|
|||
),
|
||||
)
|
||||
.child(downloads_row(item.downloads))
|
||||
// Kept once installed rather than dropped a row that loses its button leaves its neighbours misaligned
|
||||
.child(InstallButton::new(
|
||||
&self.item,
|
||||
self.cluster_id,
|
||||
|
|
@ -313,7 +311,6 @@ impl Component for ListRow {
|
|||
}
|
||||
}
|
||||
|
||||
/// Same version pick and dispatch as the install button on the package page
|
||||
#[derive(PartialEq)]
|
||||
struct InstallButton {
|
||||
provider: ProviderId,
|
||||
|
|
@ -372,7 +369,7 @@ impl Component for InstallButton {
|
|||
|
||||
rect()
|
||||
// The card behind is one big press target stop here so the package page doesn't open on top of the install
|
||||
.on_all_press(|e: Event<PressEventData>| e.stop_propagation())
|
||||
.on_press(|e: Event<PressEventData>| e.stop_propagation())
|
||||
.child(
|
||||
Button::new()
|
||||
.primary()
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ impl Component for CategoryRow {
|
|||
.cursor(CursorIcon::Pointer)
|
||||
.on_pointer_enter(move |_| hovering.set(true))
|
||||
.on_pointer_leave(move |_| hovering.set(false))
|
||||
.on_all_press(move |_| on_toggle.call(()))
|
||||
.on_press(move |_| on_toggle.call(()))
|
||||
.child(
|
||||
label()
|
||||
.text(capitalize(&self.name))
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ impl Component for ProviderTag {
|
|||
el.border(border_all_color(1., colors::brand()))
|
||||
})
|
||||
.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(move |_| confirm.set(Some(url.clone())))
|
||||
.on_press(move |_| confirm.set(Some(url.clone())))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -302,7 +302,7 @@ impl Component for AuthorRow {
|
|||
.a11y_focusable(true)
|
||||
.a11y_role(AccessibilityRole::Button)
|
||||
.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(move |_| confirm.set(Some(url.clone())))
|
||||
.on_press(move |_| confirm.set(Some(url.clone())))
|
||||
})
|
||||
.maybe(interactive && focused, |el| {
|
||||
el.border(border_all_color(1., colors::brand()))
|
||||
|
|
@ -439,7 +439,7 @@ impl Component for LinkRow {
|
|||
el.border(border_all_color(1., colors::brand()))
|
||||
})
|
||||
.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(move |_| confirm.set(Some(url.clone())))
|
||||
.on_press(move |_| confirm.set(Some(url.clone())))
|
||||
.child(
|
||||
Icon::new(IconType::Link03)
|
||||
.size(14.)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use crate::components::{
|
|||
use crate::hooks::{
|
||||
ClusterLogsQuery, LogAction, UploadLogKeys, UseLogAction, UseUploadLog,
|
||||
invalidate_logs_queries, try_cluster_logs, try_log_content, use_cluster_logs, use_log_action,
|
||||
use_log_content, use_upload_log,
|
||||
use_log_content, use_overlay_claim_when, use_upload_log,
|
||||
};
|
||||
use crate::layout::cluster_content;
|
||||
use crate::theme::colors;
|
||||
|
|
@ -377,6 +377,8 @@ impl Component for LogPicker {
|
|||
// Stays usable with an empty list the category selector lives inside the panel
|
||||
let is_open = open();
|
||||
|
||||
use_overlay_claim_when(is_open);
|
||||
|
||||
let query = SearchQuery::new(&filter.read());
|
||||
let no_files = files.is_empty();
|
||||
let rows: Vec<Element> = files
|
||||
|
|
@ -402,6 +404,11 @@ impl Component for LogPicker {
|
|||
|
||||
rect()
|
||||
.width(Size::px(260.))
|
||||
.on_global_key_down(move |e: Event<KeyboardEventData>| {
|
||||
if e.key == Key::Named(NamedKey::Escape) {
|
||||
open.set_if_modified(false);
|
||||
}
|
||||
})
|
||||
.child(
|
||||
rect()
|
||||
.width(Size::fill())
|
||||
|
|
@ -425,7 +432,7 @@ impl Component for LogPicker {
|
|||
},
|
||||
))
|
||||
.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(move |e: Event<PressEventData>| {
|
||||
.on_press(move |e: Event<PressEventData>| {
|
||||
e.stop_propagation();
|
||||
open.toggle();
|
||||
})
|
||||
|
|
@ -555,7 +562,7 @@ impl Component for PickerRow {
|
|||
.cursor(CursorIcon::Pointer)
|
||||
.on_pointer_enter(move |_| hovering.set(true))
|
||||
.on_pointer_leave(move |_| hovering.set(false))
|
||||
.on_all_press(self.on_press.clone())
|
||||
.on_press(self.on_press.clone())
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::components::{
|
|||
Button, CardLayout, Icon, IconType, PackageEntry, PackageRow, ScrollArea, ScrollAreaCtx,
|
||||
Segment, SegmentedControl, TabBar, TabItem, TextInput,
|
||||
};
|
||||
use crate::hooks::use_dispatch;
|
||||
use crate::hooks::{use_dispatch, use_overlay_claim};
|
||||
use crate::routes::Route;
|
||||
use crate::theme::colors;
|
||||
use crate::{Actions, utils};
|
||||
|
|
@ -311,6 +311,8 @@ struct FilterPopover {
|
|||
|
||||
impl Component for FilterPopover {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
use_overlay_claim();
|
||||
|
||||
let mut sort = self.sort;
|
||||
let current_sort = self.current_sort;
|
||||
let mut enabled_filter = self.enabled_filter;
|
||||
|
|
@ -473,7 +475,7 @@ impl Component for ChoiceRow {
|
|||
el.border(crate::ui::border_all_color(1., colors::brand()))
|
||||
})
|
||||
.cursor(CursorIcon::Pointer)
|
||||
.on_all_press(self.on_press.clone())
|
||||
.on_press(self.on_press.clone())
|
||||
.child(
|
||||
label()
|
||||
.text(text)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue