Onboarding · Flows and highlights: focus that holds as the flow turns, a tip that closes
navigation::Wizard keeps one focus handle for Next and Finish, so a second press on the step before last finishes; Back that reaches the first step, and a finished step picked by key, hand focus to Next. Popover::with_opener hands its content a close that gives focus back: Got it closes the hotspot's tip before the owner hides it, and the event details close before Edit and Delete run, so focus never lands on a panel that left the tree. The wizard's count and the setup checklist's tally name their selectors after their words, and the bar takes the count's own value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
996daed6e4
commit
973f10a6dc
15 files changed
+180
-54
No files matched your search
+14
-7
@@ -12,7 +12,7 @@ use crate::{
|
||||
theme::ControlSize,
|
||||
};
|
||||
|
||||
type Content = Box<dyn FnOnce(&mut Window, &mut App) -> AnyElement>;
|
||||
type Content = Box<dyn FnOnce(Run, &mut Window, &mut App) -> AnyElement>;
|
||||
type Own = Box<dyn FnOnce(Run) -> AnyElement>;
|
||||
|
||||
/// A popover's own button: its label and look.
|
||||
@@ -73,20 +73,22 @@ impl Popover {
|
||||
variant: ButtonVariant::Secondary,
|
||||
size: ControlSize::default(),
|
||||
}),
|
||||
content: Box::new(move |window, cx| content(window, cx).into_any_element()),
|
||||
content: Box::new(move |_, window, cx| content(window, cx).into_any_element()),
|
||||
}
|
||||
}
|
||||
|
||||
/// A panel that opens from an element of the owner's: `opener` builds it with the run that opens and closes the panel.
|
||||
/// A panel that opens from an element of the owner's: `opener` builds it with the run that opens and closes the panel, and `content` gets the run that closes it and hands focus back, for a press that ends the panel's work.
|
||||
pub fn with_opener<T: IntoElement, E: IntoElement>(
|
||||
id: impl Into<ElementId>,
|
||||
opener: impl FnOnce(Run) -> T + 'static,
|
||||
content: impl FnOnce(&mut Window, &mut App) -> E + 'static,
|
||||
content: impl FnOnce(Run, &mut Window, &mut App) -> E + 'static,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
opener: Opener::Own(Box::new(move |toggle| opener(toggle).into_any_element())),
|
||||
content: Box::new(move |window, cx| content(window, cx).into_any_element()),
|
||||
content: Box::new(move |close, window, cx| {
|
||||
content(close, window, cx).into_any_element()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,9 +181,14 @@ impl RenderOnce for Popover {
|
||||
close(&state, window, cx);
|
||||
return host;
|
||||
}
|
||||
let (escape, out, tall) = (state.clone(), state.clone(), state.clone());
|
||||
let (escape, out, tall, done) =
|
||||
(state.clone(), state.clone(), state.clone(), state.clone());
|
||||
let (anchor, height) = (state.read(cx).host, state.read(cx).height);
|
||||
let body = (self.content)(window, cx);
|
||||
let body = (self.content)(
|
||||
Rc::new(move |window, cx| close(&done, window, cx)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
let panel = div()
|
||||
.id((self.id.clone(), "panel"))
|
||||
.relative()
|
||||
|
||||
@@ -430,5 +430,5 @@ fn a_tall_dialog_body_keeps_its_height_and_scrolls(cx: &mut TestAppContext) {
|
||||
#[test]
|
||||
#[should_panic(expected = "an icon is for its own button")]
|
||||
fn a_popover_opened_by_its_owner_takes_no_icon() {
|
||||
let _ = Popover::with_opener("own", |_| div(), |_, _| div()).icon(IconName::Clock);
|
||||
let _ = Popover::with_opener("own", |_| div(), |_, _, _| div()).icon(IconName::Clock);
|
||||
}
|
||||
Reference in new issue
Block a user