ui: Add the SwitchField component (#34713)

This will be useful for both the current agent panel and some other
onboarding stuff we're working on. Also ended up removing the
`SwitchWithLabel` as it was unused.

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-07-18 15:03:14 -03:00 committed by GitHub
parent 87555d3f0b
commit 64ce696aae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -543,28 +543,48 @@ impl RenderOnce for Switch {
}
}
/// A [`Switch`] that has a [`Label`].
#[derive(IntoElement)]
pub struct SwitchWithLabel {
/// # SwitchField
///
/// A field component that combines a label, description, and switch into one reusable component.
///
/// # Examples
///
/// ```
/// use ui::prelude::*;
///
/// SwitchField::new(
/// "feature-toggle",
/// "Enable feature",
/// "This feature adds new functionality to the app.",
/// ToggleState::Unselected,
/// |state, window, cx| {
/// // Logic here
/// }
/// );
/// ```
#[derive(IntoElement, RegisterComponent)]
pub struct SwitchField {
id: ElementId,
label: Label,
label: SharedString,
description: SharedString,
toggle_state: ToggleState,
on_click: Arc<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>,
disabled: bool,
color: SwitchColor,
}
impl SwitchWithLabel {
/// Creates a switch with an attached label.
impl SwitchField {
pub fn new(
id: impl Into<ElementId>,
label: Label,
label: impl Into<SharedString>,
description: impl Into<SharedString>,
toggle_state: impl Into<ToggleState>,
on_click: impl Fn(&ToggleState, &mut Window, &mut App) + 'static,
) -> Self {
Self {
id: id.into(),
label,
label: label.into(),
description: description.into(),
toggle_state: toggle_state.into(),
on_click: Arc::new(on_click),
disabled: false,
@ -572,43 +592,141 @@ impl SwitchWithLabel {
}
}
/// Sets the disabled state of the [`SwitchWithLabel`].
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
/// Sets the color of the switch using the specified [`SwitchColor`].
/// This changes the color scheme of the switch when it's in the "on" state.
pub fn color(mut self, color: SwitchColor) -> Self {
self.color = color;
self
}
}
impl RenderOnce for SwitchWithLabel {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
impl RenderOnce for SwitchField {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
h_flex()
.id(SharedString::from(format!("{}-container", self.id)))
.gap(DynamicSpacing::Base08.rems(cx))
.w_full()
.gap_4()
.justify_between()
.flex_wrap()
.child(
Switch::new(self.id.clone(), self.toggle_state)
.disabled(self.disabled)
.color(self.color)
.on_click({
let on_click = self.on_click.clone();
move |checked, window, cx| {
(on_click)(checked, window, cx);
}
}),
v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new(self.label))
.child(Label::new(self.description).color(Color::Muted)),
)
.child(
div()
.id(SharedString::from(format!("{}-label", self.id)))
.child(self.label),
Switch::new(
SharedString::from(format!("{}-switch", self.id)),
self.toggle_state,
)
.color(self.color)
.disabled(self.disabled)
.on_click({
let on_click = self.on_click.clone();
move |state, window, cx| {
(on_click)(state, window, cx);
}
}),
)
}
}
impl Component for SwitchField {
fn scope() -> ComponentScope {
ComponentScope::Input
}
fn description() -> Option<&'static str> {
Some("A field component that combines a label, description, and switch")
}
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
Some(
v_flex()
.gap_6()
.children(vec![
example_group_with_title(
"States",
vec![
single_example(
"Unselected",
SwitchField::new(
"switch_field_unselected",
"Enable notifications",
"Receive notifications when new messages arrive.",
ToggleState::Unselected,
|_, _, _| {},
)
.into_any_element(),
),
single_example(
"Selected",
SwitchField::new(
"switch_field_selected",
"Enable notifications",
"Receive notifications when new messages arrive.",
ToggleState::Selected,
|_, _, _| {},
)
.into_any_element(),
),
],
),
example_group_with_title(
"Colors",
vec![
single_example(
"Default",
SwitchField::new(
"switch_field_default",
"Default color",
"This uses the default switch color.",
ToggleState::Selected,
|_, _, _| {},
)
.into_any_element(),
),
single_example(
"Accent",
SwitchField::new(
"switch_field_accent",
"Accent color",
"This uses the accent color scheme.",
ToggleState::Selected,
|_, _, _| {},
)
.color(SwitchColor::Accent)
.into_any_element(),
),
],
),
example_group_with_title(
"Disabled",
vec![single_example(
"Disabled",
SwitchField::new(
"switch_field_disabled",
"Disabled field",
"This field is disabled and cannot be toggled.",
ToggleState::Selected,
|_, _, _| {},
)
.disabled(true)
.into_any_element(),
)],
),
])
.into_any_element(),
)
}
}
impl Component for Checkbox {
fn scope() -> ComponentScope {
ComponentScope::Input