commit 303f18a4d971485b9111c43d5ac3ceeaa83ea5d7
parent 46722d35de8b7e59dd38558773cec7aa5f5ff639
Author: triesap <tyson@radroots.org>
Date: Sun, 19 Apr 2026 19:08:16 +0000
ui: move startup home and products controls into shared ui
- widen shared button primitives to accept generic element ids
- add shared text, choice, and selectable row controls in radroots_app_ui
- replace startup and products launcher-local helpers with shared ui primitives
- route home and product form fields through shared form inputs and verify cargo test -p radroots_app
Diffstat:
3 files changed, 224 insertions(+), 261 deletions(-)
diff --git a/crates/launchers/desktop/src/window.rs b/crates/launchers/desktop/src/window.rs
@@ -34,19 +34,20 @@ use radroots_app_state::{
use radroots_app_ui::{
APP_UI_THEME, AppCheckboxFieldSpec, AppFormFieldSpec,
AppSegmentButtonIconSpec as IconSegmentButtonSpec, LabelValueRow,
- app_button_compact as action_button_compact, app_button_icon as action_icon_button,
+ app_button_choice as choice_button, app_button_compact as action_button_compact,
+ app_button_icon as action_icon_button, app_button_list_row as list_row_button,
app_button_primary as action_button_primary,
app_button_primary_disabled as action_button_primary_disabled,
- app_button_secondary as action_button, app_checkbox_field, app_cluster,
- app_divider as section_divider, app_form_field, app_form_input_text, app_form_section,
- app_heading_section, app_heading_view, app_input_text as app_text_input, app_scroll_panel,
- app_segment_button_icon as icon_segment_button, app_shared_label_text, app_shared_text,
- app_split_shell, app_stack_h, app_stack_v, app_status_indicator as status_indicator,
- app_surface_card, app_surface_card_section as home_card, app_surface_panel,
- app_surface_sidebar, app_surface_window as app_window_shell,
- app_text_badge as settings_badge_text, app_text_body_subtle as home_body_text,
- app_text_label as home_farm_setup_field_label, app_text_value, label_value_list,
- utility_title_row,
+ app_button_secondary as action_button, app_button_text as text_button, app_checkbox_field,
+ app_cluster, app_divider as section_divider, app_form_field, app_form_input_text,
+ app_form_section, app_heading_section, app_heading_view, app_input_text as app_text_input,
+ app_scroll_panel, app_segment_button_icon as icon_segment_button, app_shared_label_text,
+ app_shared_text, app_split_shell, app_stack_h, app_stack_v,
+ app_status_indicator as status_indicator, app_surface_card,
+ app_surface_card_section as home_card, app_surface_panel, app_surface_sidebar,
+ app_surface_window as app_window_shell, app_text_badge as settings_badge_text,
+ app_text_body_subtle as home_body_text, app_text_label as home_farm_setup_field_label,
+ app_text_value, label_value_list, utility_title_row,
};
use radroots_nostr::prelude::RadrootsNostrClient;
use std::time::Duration;
@@ -1452,9 +1453,10 @@ impl HomeView {
.as_ref()
.map(|editor| editor.product_id == row.product_id)
.unwrap_or(false);
- let product = products_row_open_button(
+ let product = list_row_button(
("products-row-open", index),
- row,
+ product_display_title(row.title.as_str()),
+ row.subtitle.clone().map(SharedString::from),
is_open,
cx.listener({
let product_id = row.product_id;
@@ -1472,7 +1474,7 @@ impl HomeView {
)
.into_any_element()
} else {
- products_row_action_button(
+ action_button_compact(
("products-row-stock-action", index),
app_shared_text(AppTextKey::ProductsUpdateStockAction),
cx.listener({
@@ -4339,9 +4341,9 @@ fn startup_signer_entry_surface(
)
.into_any_element()
})
- .child(startup_text_button(
+ .child(text_button(
"home-signer-back",
- AppTextKey::HomeSetupBackAction,
+ app_shared_text(AppTextKey::HomeSetupBackAction),
on_back,
cx,
))
@@ -4468,32 +4470,6 @@ fn startup_signer_transport_failure_requires_notice(message: &str) -> bool {
message != "remote signer did not respond yet"
}
-fn startup_text_button(
- id: &'static str,
- key: AppTextKey,
- on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- cx: &App,
-) -> impl IntoElement {
- Button::new(id)
- .custom(
- ButtonCustomVariant::new(cx)
- .color(transparent_black().into())
- .foreground(rgb(APP_UI_THEME.foundation.text.secondary).into())
- .border(transparent_black())
- .hover(transparent_black().into())
- .active(transparent_black().into()),
- )
- .rounded(ButtonRounded::Size(px(0.0)))
- .on_click(on_click)
- .child(
- div()
- .text_size(px(APP_UI_THEME.foundation.typography.body_text_px))
- .font_weight(gpui::FontWeight::MEDIUM)
- .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
- .child(app_shared_text(key)),
- )
-}
-
fn startup_home_body(runtime: &DesktopAppRuntimeSummary) -> impl IntoElement {
let body = runtime
.startup_issue
@@ -4929,44 +4905,44 @@ fn products_controls_card(
.flex()
.items_center()
.gap(px(8.0))
- .child(products_filter_button(
+ .child(choice_button(
"products-filter-all",
- AppTextKey::ProductsFilterAll,
+ app_shared_text(AppTextKey::ProductsFilterAll),
selected_filter == ProductsFilter::All,
on_select_all_products,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-filter-live",
- AppTextKey::ProductsFilterLive,
+ app_shared_text(AppTextKey::ProductsFilterLive),
selected_filter == ProductsFilter::Live,
on_select_live_products,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-filter-drafts",
- AppTextKey::ProductsFilterDrafts,
+ app_shared_text(AppTextKey::ProductsFilterDrafts),
selected_filter == ProductsFilter::Drafts,
on_select_draft_products,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-filter-need-attention",
- AppTextKey::ProductsFilterNeedAttention,
+ app_shared_text(AppTextKey::ProductsFilterNeedAttention),
selected_filter == ProductsFilter::NeedAttention,
on_select_products_needing_attention,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-filter-paused",
- AppTextKey::ProductsFilterPaused,
+ app_shared_text(AppTextKey::ProductsFilterPaused),
selected_filter == ProductsFilter::Paused,
on_select_paused_products,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-filter-archived",
- AppTextKey::ProductsFilterArchived,
+ app_shared_text(AppTextKey::ProductsFilterArchived),
selected_filter == ProductsFilter::Archived,
on_select_archived_products,
cx,
@@ -4991,37 +4967,37 @@ fn products_controls_card(
.flex()
.items_center()
.gap(px(8.0))
- .child(products_filter_button(
+ .child(choice_button(
"products-sort-updated",
- AppTextKey::ProductsSortUpdated,
+ app_shared_text(AppTextKey::ProductsSortUpdated),
selected_sort == ProductsSort::Updated,
on_sort_products_by_updated,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-sort-name",
- AppTextKey::ProductsSortName,
+ app_shared_text(AppTextKey::ProductsSortName),
selected_sort == ProductsSort::Name,
on_sort_products_by_name,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-sort-availability",
- AppTextKey::ProductsSortAvailability,
+ app_shared_text(AppTextKey::ProductsSortAvailability),
selected_sort == ProductsSort::Availability,
on_sort_products_by_availability,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-sort-stock",
- AppTextKey::ProductsSortStock,
+ app_shared_text(AppTextKey::ProductsSortStock),
selected_sort == ProductsSort::Stock,
on_sort_products_by_stock,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-sort-price",
- AppTextKey::ProductsSortPrice,
+ app_shared_text(AppTextKey::ProductsSortPrice),
selected_sort == ProductsSort::Price,
on_sort_products_by_price,
cx,
@@ -5031,20 +5007,6 @@ fn products_controls_card(
)
}
-fn products_filter_button(
- id: &'static str,
- key: AppTextKey,
- is_active: bool,
- on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- cx: &App,
-) -> impl IntoElement {
- if is_active {
- action_button_primary(id, app_shared_text(key), on_click, cx).into_any_element()
- } else {
- action_button_compact(id, app_shared_text(key), on_click, cx).into_any_element()
- }
-}
-
fn products_table_header() -> impl IntoElement {
div()
.w_full()
@@ -5162,63 +5124,6 @@ fn products_table_row(
.child(div().w(px(120.0)).flex().justify_end().child(action))
}
-fn products_row_open_button(
- id: (&'static str, usize),
- row: &ProductsListRow,
- is_open: bool,
- on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- cx: &App,
-) -> impl IntoElement {
- let selected_background = rgb(APP_UI_THEME.foundation.surfaces.window_background);
-
- Button::new(id)
- .custom(
- ButtonCustomVariant::new(cx)
- .color(if is_open {
- selected_background.into()
- } else {
- transparent_black().into()
- })
- .foreground(rgb(APP_UI_THEME.foundation.text.primary).into())
- .border(transparent_black())
- .hover(selected_background.into())
- .active(selected_background.into()),
- )
- .rounded(ButtonRounded::Size(px(APP_UI_THEME
- .foundation
- .radii
- .medium_px)))
- .flex_1()
- .min_w_0()
- .on_click(on_click)
- .child(
- div()
- .w_full()
- .flex()
- .flex_col()
- .items_start()
- .gap(px(4.0))
- .px(px(8.0))
- .py(px(6.0))
- .child(
- div()
- .text_size(px(APP_UI_THEME.foundation.typography.body_text_px))
- .font_weight(gpui::FontWeight::MEDIUM)
- .text_color(rgb(APP_UI_THEME.foundation.text.primary))
- .child(product_display_title(row.title.as_str())),
- )
- .when_some(row.subtitle.as_ref(), |this, subtitle| {
- this.child(
- div()
- .text_size(px(APP_UI_THEME.foundation.typography.utility_title_text_px))
- .line_height(relative(1.2))
- .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
- .child(subtitle.clone()),
- )
- }),
- )
-}
-
fn products_empty_state_card(filter: ProductsFilter) -> impl IntoElement {
let (title_key, body_key) = if filter == ProductsFilter::NeedAttention {
(
@@ -5277,40 +5182,6 @@ fn products_price_text(row: &ProductsListRow) -> String {
format!("${dollars}.{cents:02} / {}", price.unit_label)
}
-fn products_row_action_button(
- id: (&'static str, usize),
- label: impl Into<SharedString>,
- on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- cx: &App,
-) -> impl IntoElement {
- let sizing = APP_UI_THEME.components.app_button.sizing;
- let colors = APP_UI_THEME.components.app_button.secondary_colors;
-
- Button::new(id)
- .custom(
- ButtonCustomVariant::new(cx)
- .color(rgb(colors.background).into())
- .foreground(rgb(colors.foreground).into())
- .border(transparent_black())
- .hover(rgb(colors.background).into())
- .active(rgb(colors.active_background).into()),
- )
- .rounded(ButtonRounded::Size(px(sizing.corner_radius_px)))
- .h(px(sizing.height_px))
- .on_click(on_click)
- .child(
- div()
- .h_full()
- .flex()
- .items_center()
- .justify_center()
- .px(px(sizing.compact_horizontal_padding_px))
- .text_size(px(sizing.label_size_px))
- .text_color(rgb(colors.foreground))
- .child(label.into()),
- )
-}
-
fn products_stock_editor_card(
row: &ProductsListRow,
editor: &ProductsStockEditorState,
@@ -5468,30 +5339,45 @@ fn products_editor_surface(
.child(home_body_text(app_shared_text(
AppTextKey::ProductsEditorBody,
)))
- .child(products_editor_text_field(
- AppTextKey::ProductsEditorFieldTitle,
+ .child(app_form_input_text(
+ AppFormFieldSpec::new(
+ app_shared_text(AppTextKey::ProductsEditorFieldTitle),
+ Option::<SharedString>::None,
+ ),
&form.title_input,
- None,
+ false,
))
- .child(products_editor_text_field(
- AppTextKey::ProductsEditorFieldSubtitle,
+ .child(app_form_input_text(
+ AppFormFieldSpec::new(
+ app_shared_text(AppTextKey::ProductsEditorFieldSubtitle),
+ Option::<SharedString>::None,
+ ),
&form.subtitle_input,
- None,
+ false,
))
- .child(products_editor_text_field(
- AppTextKey::ProductsEditorFieldUnit,
+ .child(app_form_input_text(
+ AppFormFieldSpec::new(
+ app_shared_text(AppTextKey::ProductsEditorFieldUnit),
+ Option::<SharedString>::None,
+ ),
&form.unit_input,
- None,
+ false,
))
- .child(products_editor_text_field(
- AppTextKey::ProductsEditorFieldPrice,
+ .child(app_form_input_text(
+ AppFormFieldSpec::new(
+ app_shared_text(AppTextKey::ProductsEditorFieldPrice),
+ products_editor_invalid_price_key(form, cx).map(app_shared_text),
+ ),
&form.price_input,
- products_editor_invalid_price_key(form, cx),
+ false,
))
- .child(products_editor_text_field(
- AppTextKey::ProductsEditorFieldStock,
+ .child(app_form_input_text(
+ AppFormFieldSpec::new(
+ app_shared_text(AppTextKey::ProductsEditorFieldStock),
+ products_editor_invalid_stock_key(form, cx).map(app_shared_text),
+ ),
&form.stock_input,
- products_editor_invalid_stock_key(form, cx),
+ false,
))
.child(products_editor_status_section(
form.status,
@@ -5558,26 +5444,6 @@ fn products_editor_surface(
)
}
-fn products_editor_text_field(
- field_label_key: AppTextKey,
- input: &Entity<InputState>,
- validation_key: Option<AppTextKey>,
-) -> impl IntoElement {
- div()
- .w_full()
- .flex()
- .flex_col()
- .items_start()
- .gap(px(8.0))
- .child(home_farm_setup_field_label(app_shared_text(
- field_label_key,
- )))
- .child(app_text_input(input, false).w_full())
- .when_some(validation_key, |this, validation_key| {
- this.child(home_body_text(app_shared_text(validation_key)))
- })
-}
-
fn products_editor_status_section(
selected_status: ProductStatus,
on_select_draft: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
@@ -5601,30 +5467,30 @@ fn products_editor_status_section(
.flex()
.items_center()
.gap(px(8.0))
- .child(products_filter_button(
+ .child(choice_button(
"products-editor-status-draft",
- AppTextKey::ProductsStatusDraft,
+ app_shared_text(AppTextKey::ProductsStatusDraft),
selected_status == ProductStatus::Draft,
on_select_draft,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-editor-status-live",
- AppTextKey::ProductsStatusLive,
+ app_shared_text(AppTextKey::ProductsStatusLive),
selected_status == ProductStatus::Published,
on_select_live,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-editor-status-paused",
- AppTextKey::ProductsStatusPaused,
+ app_shared_text(AppTextKey::ProductsStatusPaused),
selected_status == ProductStatus::Paused,
on_select_paused,
cx,
))
- .child(products_filter_button(
+ .child(choice_button(
"products-editor-status-archived",
- AppTextKey::ProductsStatusArchived,
+ app_shared_text(AppTextKey::ProductsStatusArchived),
selected_status == ProductStatus::Archived,
on_select_archived,
cx,
@@ -5859,21 +5725,33 @@ fn home_farm_setup_form_card(
.child(home_body_text(app_shared_text(
AppTextKey::HomeFarmSetupOnboardingBody,
)))
- .child(home_farm_setup_text_field(
- AppTextKey::HomeFarmSetupSectionFarm,
- AppTextKey::HomeFarmSetupFieldFarmName,
- &form.farm_name_input,
- blockers
- .contains(&FarmSetupBlocker::AddFarmName)
- .then_some(AppTextKey::HomeFarmSetupBlockerAddFarmName),
+ .child(app_form_section(
+ app_shared_text(AppTextKey::HomeFarmSetupSectionFarm),
+ app_form_input_text(
+ AppFormFieldSpec::new(
+ app_shared_text(AppTextKey::HomeFarmSetupFieldFarmName),
+ blockers
+ .contains(&FarmSetupBlocker::AddFarmName)
+ .then_some(AppTextKey::HomeFarmSetupBlockerAddFarmName)
+ .map(app_shared_text),
+ ),
+ &form.farm_name_input,
+ false,
+ ),
))
- .child(home_farm_setup_text_field(
- AppTextKey::HomeFarmSetupSectionLocation,
- AppTextKey::HomeFarmSetupFieldLocationOrServiceArea,
- &form.location_input,
- blockers
- .contains(&FarmSetupBlocker::AddLocationOrServiceArea)
- .then_some(AppTextKey::HomeFarmSetupBlockerAddLocationOrServiceArea),
+ .child(app_form_section(
+ app_shared_text(AppTextKey::HomeFarmSetupSectionLocation),
+ app_form_input_text(
+ AppFormFieldSpec::new(
+ app_shared_text(AppTextKey::HomeFarmSetupFieldLocationOrServiceArea),
+ blockers
+ .contains(&FarmSetupBlocker::AddLocationOrServiceArea)
+ .then_some(AppTextKey::HomeFarmSetupBlockerAddLocationOrServiceArea)
+ .map(app_shared_text),
+ ),
+ &form.location_input,
+ false,
+ ),
))
.child(home_farm_setup_order_method_section(
form,
@@ -5915,24 +5793,6 @@ fn home_farm_setup_form_card(
)
}
-fn home_farm_setup_text_field(
- section_key: AppTextKey,
- field_label_key: AppTextKey,
- input: &Entity<InputState>,
- blocker_key: Option<AppTextKey>,
-) -> impl IntoElement {
- app_form_section(
- app_shared_text(section_key),
- app_form_field(
- AppFormFieldSpec::new(
- app_shared_text(field_label_key),
- blocker_key.map(app_shared_text),
- ),
- app_text_input(input, false).w_full(),
- ),
- )
-}
-
fn home_farm_setup_order_method_section(
form: &FarmSetupFormState,
blocker_key: Option<AppTextKey>,
diff --git a/crates/shared/ui/src/lib.rs b/crates/shared/ui/src/lib.rs
@@ -6,13 +6,14 @@ mod theme;
pub use primitives::{
AppCheckboxFieldSpec, AppFormFieldSpec, AppSegmentButtonIconSpec, LabelValueRow,
- app_button_compact, app_button_icon, app_button_primary, app_button_primary_disabled,
- app_button_secondary, app_checkbox_field, app_cluster, app_divider, app_form_field,
- app_form_input_text, app_form_section, app_heading_section, app_heading_view, app_input_text,
- app_scroll_panel, app_segment_button_icon, app_split_shell, app_stack_h, app_stack_v,
- app_status_indicator, app_surface_card, app_surface_card_section, app_surface_panel,
- app_surface_sidebar, app_surface_window, app_text_badge, app_text_body, app_text_body_subtle,
- app_text_label, app_text_value, label_value_list, utility_title_row,
+ app_button_choice, app_button_compact, app_button_icon, app_button_list_row,
+ app_button_primary, app_button_primary_disabled, app_button_secondary, app_button_text,
+ app_checkbox_field, app_cluster, app_divider, app_form_field, app_form_input_text,
+ app_form_section, app_heading_section, app_heading_view, app_input_text, app_scroll_panel,
+ app_segment_button_icon, app_split_shell, app_stack_h, app_stack_v, app_status_indicator,
+ app_surface_card, app_surface_card_section, app_surface_panel, app_surface_sidebar,
+ app_surface_window, app_text_badge, app_text_body, app_text_body_subtle, app_text_label,
+ app_text_value, label_value_list, utility_title_row,
};
pub use text::{
app_shared_label_text, app_shared_text, runtime_metadata_rows, settings_about_status_rows,
diff --git a/crates/shared/ui/src/primitives.rs b/crates/shared/ui/src/primitives.rs
@@ -1,7 +1,7 @@
use gpui::{
- AnyElement, App, ClickEvent, Div, Entity, InteractiveElement, IntoElement, ParentElement,
- SharedString, StatefulInteractiveElement, Styled, Window, div, prelude::FluentBuilder, px,
- relative, rgb, transparent_black,
+ AnyElement, App, ClickEvent, Div, ElementId, Entity, InteractiveElement, IntoElement,
+ ParentElement, SharedString, StatefulInteractiveElement, Styled, Window, div,
+ prelude::FluentBuilder, px, relative, rgb, transparent_black,
};
use gpui_component::{
Icon, IconName, Sizable, Size,
@@ -535,7 +535,7 @@ pub fn app_input_text(input: &Entity<InputState>, disabled: bool) -> Input {
}
pub fn app_button_secondary(
- id: &'static str,
+ id: impl Into<ElementId>,
label: impl Into<SharedString>,
on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
cx: &App,
@@ -553,7 +553,7 @@ pub fn app_button_secondary(
}
pub fn app_button_primary(
- id: &'static str,
+ id: impl Into<ElementId>,
label: impl Into<SharedString>,
on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
cx: &App,
@@ -571,7 +571,7 @@ pub fn app_button_primary(
}
pub fn app_button_primary_disabled(
- id: &'static str,
+ id: impl Into<ElementId>,
label: impl Into<SharedString>,
cx: &App,
) -> impl IntoElement {
@@ -588,7 +588,7 @@ pub fn app_button_primary_disabled(
}
pub fn app_button_compact(
- id: &'static str,
+ id: impl Into<ElementId>,
label: impl Into<SharedString>,
on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
cx: &App,
@@ -628,7 +628,7 @@ fn app_button_label(
}
pub fn app_button_icon(
- id: &'static str,
+ id: impl Into<ElementId>,
icon: IconName,
on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
cx: &App,
@@ -654,8 +654,106 @@ pub fn app_status_indicator(color: u32) -> impl IntoElement {
.rounded(px(sizing.size_px / 2.0))
}
+pub fn app_button_text(
+ id: impl Into<ElementId>,
+ label: impl Into<SharedString>,
+ on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ cx: &App,
+) -> impl IntoElement {
+ Button::new(id)
+ .custom(
+ ButtonCustomVariant::new(cx)
+ .color(transparent_black().into())
+ .foreground(rgb(APP_UI_THEME.foundation.text.secondary).into())
+ .border(transparent_black())
+ .hover(transparent_black().into())
+ .active(transparent_black().into()),
+ )
+ .rounded(ButtonRounded::Size(px(0.0)))
+ .on_click(on_click)
+ .child(
+ div()
+ .text_size(px(APP_UI_THEME.foundation.typography.body_text_px))
+ .font_weight(gpui::FontWeight::MEDIUM)
+ .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
+ .child(label.into()),
+ )
+}
+
+pub fn app_button_choice(
+ id: impl Into<ElementId>,
+ label: impl Into<SharedString>,
+ is_active: bool,
+ on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ cx: &App,
+) -> AnyElement {
+ if is_active {
+ app_button_primary(id, label, on_click, cx).into_any_element()
+ } else {
+ app_button_compact(id, label, on_click, cx).into_any_element()
+ }
+}
+
+pub fn app_button_list_row(
+ id: impl Into<ElementId>,
+ title: impl Into<SharedString>,
+ subtitle: Option<SharedString>,
+ is_selected: bool,
+ on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ cx: &App,
+) -> impl IntoElement {
+ let selected_background = rgb(APP_UI_THEME.foundation.surfaces.window_background);
+
+ Button::new(id)
+ .custom(
+ ButtonCustomVariant::new(cx)
+ .color(if is_selected {
+ selected_background.into()
+ } else {
+ transparent_black().into()
+ })
+ .foreground(rgb(APP_UI_THEME.foundation.text.primary).into())
+ .border(transparent_black())
+ .hover(selected_background.into())
+ .active(selected_background.into()),
+ )
+ .rounded(ButtonRounded::Size(px(APP_UI_THEME
+ .foundation
+ .radii
+ .medium_px)))
+ .flex_1()
+ .min_w_0()
+ .on_click(on_click)
+ .child(
+ div()
+ .w_full()
+ .flex()
+ .flex_col()
+ .items_start()
+ .gap(px(4.0))
+ .px(px(8.0))
+ .py(px(6.0))
+ .child(
+ div()
+ .text_size(px(APP_UI_THEME.foundation.typography.body_text_px))
+ .font_weight(gpui::FontWeight::MEDIUM)
+ .text_color(rgb(APP_UI_THEME.foundation.text.primary))
+ .child(title.into()),
+ )
+ .when_some(subtitle, |this, subtitle| {
+ this.child(
+ div()
+ .text_size(px(APP_UI_THEME.foundation.typography.utility_title_text_px))
+ .line_height(relative(1.2))
+ .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
+ .child(subtitle),
+ )
+ }),
+ )
+}
+
fn app_button_base(
- id: &'static str,
+ id: impl Into<ElementId>,
variant: AppButtonVariant,
on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
cx: &App,
@@ -682,7 +780,11 @@ fn app_button_base(
.on_click(on_click)
}
-fn app_button_base_disabled(id: &'static str, variant: AppButtonVariant, cx: &App) -> Button {
+fn app_button_base_disabled(
+ id: impl Into<ElementId>,
+ variant: AppButtonVariant,
+ cx: &App,
+) -> Button {
let sizing = APP_UI_THEME.components.app_button.sizing;
let colors = app_button_disabled_colors(variant);