use crate::{ h_flex, theme::{scale::ColorScaleStep, ActiveTheme}, IconName, }; use gpui::{ div, prelude::FluentBuilder, relative, svg, App, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, }; type OnClick = Option>; /// A Radio element. /// /// This is not included the Radio group implementation, you can manage the group by yourself. #[derive(IntoElement)] pub struct Radio { id: ElementId, label: Option, checked: bool, disabled: bool, on_click: OnClick, } impl Radio { pub fn new(id: impl Into) -> Self { Self { id: id.into(), label: None, checked: false, disabled: false, on_click: None, } } pub fn label(mut self, label: impl Into) -> Self { self.label = Some(label.into()); self } pub fn checked(mut self, checked: bool) -> Self { self.checked = checked; self } pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } pub fn on_click(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self { self.on_click = Some(Box::new(handler)); self } } impl RenderOnce for Radio { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { let color = if self.disabled { cx.theme().accent.step(cx, ColorScaleStep::FIVE) } else { cx.theme().accent.step(cx, ColorScaleStep::NINE) }; h_flex() .id(self.id) .gap_x_2() .items_center() .line_height(relative(1.)) .child( div() .relative() .size_4() .flex_shrink_0() .rounded_full() .border_1() .border_color(color) .when(self.checked, |this| this.bg(color)) .child( svg() .absolute() .top_px() .left_px() .size_3() .text_color(color) .map(|this| match self.checked { true => this.path(IconName::Check.path()), false => this, }), ), ) .when_some(self.label, |this, label| { this.child( div() .size_full() .overflow_x_hidden() .text_ellipsis() .line_height(relative(1.)) .child(label), ) }) .when_some( self.on_click.filter(|_| !self.disabled), |this, on_click| { this.on_click(move |_event, window, cx| { on_click(&!self.checked, window, cx); }) }, ) } }