button: Add dropdown_caret option to show a caret icon to end of button. (#1637)
<img width="658" height="162" alt="image" src="https://github.com/user-attachments/assets/7c233982-04c3-408c-8ae4-f672f8b135a3" />
This commit is contained in:
parent
44dc2c7f38
commit
01082f11d2
6 changed files with 89 additions and 128 deletions
|
|
@ -1,12 +1,11 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, App, AppContext as _, Axis, ClickEvent, Context, Corner, Entity, Focusable,
|
Action, App, AppContext as _, Axis, ClickEvent, Context, Entity, Focusable, InteractiveElement,
|
||||||
InteractiveElement, IntoElement, ParentElement as _, Render, Styled as _, Window,
|
IntoElement, ParentElement as _, Render, Styled as _, Window, prelude::FluentBuilder, px,
|
||||||
prelude::FluentBuilder, px,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
ActiveTheme, Disableable as _, Icon, IconName, Selectable as _, Sizable as _, Theme,
|
ActiveTheme, Disableable as _, Icon, IconName, Selectable as _, Sizable as _, Theme,
|
||||||
button::{Button, ButtonCustomVariant, ButtonGroup, ButtonVariants as _, DropdownButton},
|
button::{Button, ButtonCustomVariant, ButtonGroup, ButtonVariants as _},
|
||||||
checkbox::Checkbox,
|
checkbox::Checkbox,
|
||||||
h_flex, v_flex,
|
h_flex, v_flex,
|
||||||
};
|
};
|
||||||
|
|
@ -441,6 +440,66 @@ impl Render for ButtonStory {
|
||||||
.on_click(Self::on_click),
|
.on_click(Self::on_click),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
section("With Dropdown Caret")
|
||||||
|
.max_w_lg()
|
||||||
|
.child(
|
||||||
|
Button::new("button-outline-1")
|
||||||
|
.primary()
|
||||||
|
.dropdown_caret(true)
|
||||||
|
.label("Primary Button")
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("button-outline-2")
|
||||||
|
.outline()
|
||||||
|
.label("Secondary Button")
|
||||||
|
.dropdown_caret(true)
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("button-outline-5-ghost")
|
||||||
|
.ghost()
|
||||||
|
.dropdown_caret(true)
|
||||||
|
.label("Ghost Button")
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("button-outline-5-link")
|
||||||
|
.link()
|
||||||
|
.dropdown_caret(true)
|
||||||
|
.label("Link Button")
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("button-outline-5-text")
|
||||||
|
.outline()
|
||||||
|
.small()
|
||||||
|
.dropdown_caret(true)
|
||||||
|
.label("Small Button")
|
||||||
|
.disabled(disabled)
|
||||||
|
.selected(selected)
|
||||||
|
.loading(loading)
|
||||||
|
.when(compact, |this| this.compact())
|
||||||
|
.on_click(Self::on_click),
|
||||||
|
),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
section("Small Size")
|
section("Small Size")
|
||||||
.child(
|
.child(
|
||||||
|
|
@ -690,120 +749,6 @@ impl Render for ButtonStory {
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
|
||||||
section("Dropdown Button")
|
|
||||||
.child(
|
|
||||||
DropdownButton::new("dropdown-button1")
|
|
||||||
.small()
|
|
||||||
.button(Button::new("btn").label("Click Me"))
|
|
||||||
.selected(selected)
|
|
||||||
.dropdown_menu(move |this, _, _| {
|
|
||||||
this.menu_with_check(
|
|
||||||
"Disabled",
|
|
||||||
disabled,
|
|
||||||
Box::new(ButtonAction::Disabled),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Loading",
|
|
||||||
loading,
|
|
||||||
Box::new(ButtonAction::Loading),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Selected",
|
|
||||||
selected,
|
|
||||||
Box::new(ButtonAction::Selected),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Compact",
|
|
||||||
compact,
|
|
||||||
Box::new(ButtonAction::Compact),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
DropdownButton::new("dropdown-button2")
|
|
||||||
.button(Button::new("btn").label("Click Me"))
|
|
||||||
.selected(selected)
|
|
||||||
.dropdown_menu_with_anchor(Corner::BottomRight, move |this, _, _| {
|
|
||||||
this.menu_with_check(
|
|
||||||
"Disabled",
|
|
||||||
disabled,
|
|
||||||
Box::new(ButtonAction::Disabled),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Loading",
|
|
||||||
loading,
|
|
||||||
Box::new(ButtonAction::Loading),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Selected",
|
|
||||||
selected,
|
|
||||||
Box::new(ButtonAction::Selected),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Compact",
|
|
||||||
compact,
|
|
||||||
Box::new(ButtonAction::Compact),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
DropdownButton::new("dropdown-button3")
|
|
||||||
.outline()
|
|
||||||
.button(Button::new("btn").label("Outline Dropdown"))
|
|
||||||
.selected(selected)
|
|
||||||
.dropdown_menu(move |this, _, _| {
|
|
||||||
this.menu_with_check(
|
|
||||||
"Disabled",
|
|
||||||
disabled,
|
|
||||||
Box::new(ButtonAction::Disabled),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Loading",
|
|
||||||
loading,
|
|
||||||
Box::new(ButtonAction::Loading),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Selected",
|
|
||||||
selected,
|
|
||||||
Box::new(ButtonAction::Selected),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Compact",
|
|
||||||
compact,
|
|
||||||
Box::new(ButtonAction::Compact),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
DropdownButton::new("dropdown-button4")
|
|
||||||
.ghost()
|
|
||||||
.button(Button::new("btn").label("Ghost Dropdown"))
|
|
||||||
.selected(selected)
|
|
||||||
.dropdown_menu(move |this, _, _| {
|
|
||||||
this.menu_with_check(
|
|
||||||
"Disabled",
|
|
||||||
disabled,
|
|
||||||
Box::new(ButtonAction::Disabled),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Loading",
|
|
||||||
loading,
|
|
||||||
Box::new(ButtonAction::Loading),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Selected",
|
|
||||||
selected,
|
|
||||||
Box::new(ButtonAction::Selected),
|
|
||||||
)
|
|
||||||
.menu_with_check(
|
|
||||||
"Compact",
|
|
||||||
compact,
|
|
||||||
Box::new(ButtonAction::Compact),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
.child(
|
||||||
section("Icon Button")
|
section("Icon Button")
|
||||||
.child(
|
.child(
|
||||||
|
|
|
||||||
|
|
@ -416,6 +416,7 @@ impl Styled for StorySection {
|
||||||
impl RenderOnce for StorySection {
|
impl RenderOnce for StorySection {
|
||||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
GroupBox::new()
|
GroupBox::new()
|
||||||
|
.id(self.title.clone())
|
||||||
.outline()
|
.outline()
|
||||||
.title(
|
.title(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
h_flex, spinner::Spinner, tooltip::Tooltip, ActiveTheme, Colorize as _, Disableable,
|
h_flex, spinner::Spinner, tooltip::Tooltip, ActiveTheme, Colorize as _, Disableable,
|
||||||
FocusableExt as _, Icon, Selectable, Sizable, Size, StyleSized, StyledExt,
|
FocusableExt as _, Icon, IconName, Selectable, Sizable, Size, StyleSized, StyledExt,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, px, relative, Action, AnyElement, App, ClickEvent, Corners,
|
div, prelude::FluentBuilder as _, px, relative, Action, AnyElement, App, ClickEvent, Corners,
|
||||||
|
|
@ -189,6 +189,7 @@ pub struct Button {
|
||||||
outline: bool,
|
outline: bool,
|
||||||
border_corners: Corners<bool>,
|
border_corners: Corners<bool>,
|
||||||
border_edges: Edges<bool>,
|
border_edges: Edges<bool>,
|
||||||
|
dropdown_caret: bool,
|
||||||
size: Size,
|
size: Size,
|
||||||
compact: bool,
|
compact: bool,
|
||||||
tooltip: Option<(
|
tooltip: Option<(
|
||||||
|
|
@ -237,6 +238,7 @@ impl Button {
|
||||||
outline: false,
|
outline: false,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
loading_icon: None,
|
loading_icon: None,
|
||||||
|
dropdown_caret: false,
|
||||||
tab_index: 0,
|
tab_index: 0,
|
||||||
tab_stop: true,
|
tab_stop: true,
|
||||||
}
|
}
|
||||||
|
|
@ -352,6 +354,12 @@ impl Button {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set to show a dropdown caret icon at the end of the button.
|
||||||
|
pub fn dropdown_caret(mut self, dropdown_caret: bool) -> Self {
|
||||||
|
self.dropdown_caret = dropdown_caret;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clickable(&self) -> bool {
|
fn clickable(&self) -> bool {
|
||||||
!(self.disabled || self.loading) && self.on_click.is_some()
|
!(self.disabled || self.loading) && self.on_click.is_some()
|
||||||
|
|
@ -579,6 +587,9 @@ impl RenderOnce for Button {
|
||||||
this.child(div().flex_none().line_height(relative(1.)).child(label))
|
this.child(div().flex_none().line_height(relative(1.)).child(label))
|
||||||
})
|
})
|
||||||
.children(self.children)
|
.children(self.children)
|
||||||
|
.when(self.dropdown_caret, |this| {
|
||||||
|
this.child(Icon::new(IconName::ChevronDown).with_size(icon_size))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.when(self.loading && !self.disabled, |this| {
|
.when(self.loading && !self.disabled, |this| {
|
||||||
this.bg(normal_style.bg.opacity(0.8))
|
this.bg(normal_style.bg.opacity(0.8))
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use gpui::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
menu::{DropdownMenu, PopupMenu},
|
menu::{DropdownMenu, PopupMenu},
|
||||||
Disableable, IconName, Selectable, Sizable, Size, StyledExt as _,
|
Disableable, Selectable, Sizable, Size, StyledExt as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Button, ButtonRounded, ButtonVariant, ButtonVariants};
|
use super::{Button, ButtonRounded, ButtonVariant, ButtonVariants};
|
||||||
|
|
@ -178,7 +178,7 @@ impl RenderOnce for DropdownButton {
|
||||||
.when_some(self.menu, |this, menu| {
|
.when_some(self.menu, |this, menu| {
|
||||||
this.child(
|
this.child(
|
||||||
Button::new("popup")
|
Button::new("popup")
|
||||||
.icon(IconName::ChevronDown)
|
.dropdown_caret(true)
|
||||||
.rounded(self.rounded)
|
.rounded(self.rounded)
|
||||||
.border_edges(Edges {
|
.border_edges(Edges {
|
||||||
left: rounded,
|
left: rounded,
|
||||||
|
|
|
||||||
|
|
@ -845,13 +845,7 @@ where
|
||||||
.when(!show_clean, |this| {
|
.when(!show_clean, |this| {
|
||||||
let icon = match self.options.icon.clone() {
|
let icon = match self.options.icon.clone() {
|
||||||
Some(icon) => icon,
|
Some(icon) => icon,
|
||||||
None => {
|
None => Icon::new(IconName::ChevronDown),
|
||||||
if self.open {
|
|
||||||
Icon::new(IconName::ChevronUp)
|
|
||||||
} else {
|
|
||||||
Icon::new(IconName::ChevronDown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.child(icon.xsmall().text_color(match self.options.disabled {
|
this.child(icon.xsmall().text_color(match self.options.disabled {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,16 @@ Button::new("btn")
|
||||||
.label("Like")
|
.label("Like")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### With a dropdown caret icon
|
||||||
|
|
||||||
|
The `.dropdown_caret` method can allows adding a dropdown caret icon to end of the button.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Button::new("btn")
|
||||||
|
.label("Options")
|
||||||
|
.dropdown_caret(true)
|
||||||
|
```
|
||||||
|
|
||||||
### Button States
|
### Button States
|
||||||
|
|
||||||
There have `disabled`, `loading`, `selected` state for buttons to indicate different statuses.
|
There have `disabled`, `loading`, `selected` state for buttons to indicate different statuses.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue