gpui-component/crates/story/src/alert_story.rs
Jason Lee 5e504d10bb
chore: Refactor tab key to switch focus. (#991)
Wait https://github.com/zed-industries/zed/pull/33008,
https://github.com/zed-industries/zed/pull/34804 to merge.


https://github.com/user-attachments/assets/3c21ebc9-44a0-4311-bb0f-5f63fe8d4bb3

- Improved `focus_ring` to render like an outline.
- Added to support press `Enter`, `Space` key to trigger Checkbox,
Button, Radio.

## Break Changes

- The `FocusableExt` has been changed to only for crate internal.
- The `FocusableCycle` has been removed, now GPUI has it own tab stop
implementation.
2025-09-29 16:48:24 +08:00

240 lines
8.8 KiB
Rust

use gpui::{
App, AppContext, Context, Entity, FocusHandle, Focusable, IntoElement, ParentElement, Render,
Styled, Window,
};
use gpui_component::{
alert::Alert,
button::{Button, ButtonGroup},
dock::PanelControl,
text::TextView,
v_flex, IconName, Selectable as _, Sizable as _, Size,
};
use crate::section;
pub struct AlertStory {
size: Size,
banner_visible: bool,
focus_handle: gpui::FocusHandle,
}
impl AlertStory {
fn new(_: &mut Window, cx: &mut Context<Self>) -> Self {
Self {
size: Size::default(),
banner_visible: true,
focus_handle: cx.focus_handle(),
}
}
pub fn view(window: &mut Window, cx: &mut App) -> Entity<Self> {
cx.new(|cx| Self::new(window, cx))
}
fn set_size(&mut self, size: Size, _: &mut Window, cx: &mut Context<Self>) {
self.size = size;
cx.notify();
}
}
impl super::Story for AlertStory {
fn title() -> &'static str {
"Alert"
}
fn description() -> &'static str {
"Displays a callout for user attention."
}
fn new_view(window: &mut Window, cx: &mut App) -> Entity<impl Render> {
Self::view(window, cx)
}
fn zoomable() -> Option<PanelControl> {
None
}
}
impl Focusable for AlertStory {
fn focus_handle(&self, _: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Render for AlertStory {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_4()
.child(
ButtonGroup::new("toggle-size")
.outline()
.compact()
.child(
Button::new("xsmall")
.label("XSmall")
.selected(self.size == Size::XSmall),
)
.child(
Button::new("small")
.label("Small")
.selected(self.size == Size::Small),
)
.child(
Button::new("medium")
.label("Medium")
.selected(self.size == Size::Medium),
)
.child(
Button::new("large")
.label("Large")
.selected(self.size == Size::Large),
)
.on_click(cx.listener(|this, selecteds: &Vec<usize>, window, cx| {
let size = match selecteds[0] {
0 => Size::XSmall,
1 => Size::Small,
2 => Size::Medium,
3 => Size::Large,
_ => unreachable!(),
};
this.set_size(size, window, cx);
})),
)
.child(
section("Default").w_2_3().child(
Alert::new(
"alert-default",
TextView::markdown(
"md",
"This is an alert with icon, title and description (in Markdown).\n\
- This is a **list** item.\n\
- This is another list item.",
window,
cx,
),
)
.with_size(self.size)
.title("Success! Your changes have been saved"),
),
)
.child(
section("With variant").w_2_3().child(
v_flex()
.w_full()
.gap_3()
.child(
Alert::info("info1", "This is an info alert.")
.with_size(self.size)
.title("Info message")
.on_close(cx.listener(|_, _, _, _| {
println!("Info alert closed");
})),
)
.child(
Alert::success(
"success-1",
"You have successfully submitted your form.\n\
Thank you for your submission!",
)
.with_size(self.size)
.title("Submit Successful"),
)
.child(
Alert::warning(
"warning-1",
"This is a warning alert with icon, but no title.\n\
This is second line of text to test is the line-height is correct.",
)
.with_size(self.size),
)
.child(
Alert::error(
"error-1",
TextView::markdown(
"error-message",
"Please verify your billing information and try again.\n\
- Check your card details\n\
- Ensure sufficient funds\n\
- Verify billing address",
window,
cx,
),
)
.with_size(self.size)
.title("Unable to process your payment."),
),
),
)
.child(
section("Banner").w_2_3().child(
v_flex()
.w_full()
.gap_2()
.child(
Alert::new(
"banner-1",
"This is a banner alert, it will take \
the full width of the container.",
)
.banner()
.on_close(cx.listener(|this, _, _, cx| {
this.banner_visible = !this.banner_visible;
cx.notify();
}))
.visible(self.banner_visible)
.with_size(self.size),
)
.child(
Alert::info(
"banner-info",
"This is a banner alert, it will take the full width of the\
container.",
)
.banner()
.with_size(self.size),
)
.child(
Alert::success(
"banner-success",
"This is a banner alert, it will take the full width of the\
container.",
)
.banner()
.with_size(self.size),
)
.child(
Alert::warning(
"banner-warning",
"This is a banner alert, it will take the full width of the\
container.",
)
.banner()
.with_size(self.size),
)
.child(
Alert::error(
"banner-error",
"This is a banner alert, it will take the full width of the\
container.",
)
.banner()
.with_size(self.size),
),
),
)
.child(
section("Custom Icon").w_2_3().child(
Alert::new(
"other-1",
"Custom icon with info alert with long \
long long long long long long long long \
long long long long long long long long long \
long long messageeeeeeeee.",
)
.title("Custom Icon")
.with_size(self.size)
.icon(IconName::Calendar),
),
)
}
}