diff --git a/crates/storybook/src/components/icon_button.rs b/crates/storybook/src/components/icon_button.rs index cecb6ccfc3..32ef1d0ce2 100644 --- a/crates/storybook/src/components/icon_button.rs +++ b/crates/storybook/src/components/icon_button.rs @@ -1,4 +1,4 @@ -use crate::prelude::{ButtonVariant, UIState}; +use crate::prelude::{ButtonVariant, InteractionState}; use crate::theme::theme; use gpui2::elements::svg; use gpui2::style::{StyleHelpers, Styleable}; @@ -6,31 +6,37 @@ use gpui2::{elements::div, IntoElement}; use gpui2::{Element, ParentElement, ViewContext}; #[derive(Element)] -pub(crate) struct IconButton { +pub struct IconButton { path: &'static str, variant: ButtonVariant, - state: UIState, + state: InteractionState, } -pub fn icon_button( - path: &'static str, - variant: ButtonVariant, - state: UIState, -) -> impl Element { +pub fn icon_button(path: &'static str) -> IconButton { IconButton { path, - variant, - state, + variant: ButtonVariant::default(), + state: InteractionState::default(), } } impl IconButton { + pub fn variant(mut self, variant: ButtonVariant) -> Self { + self.variant = variant; + self + } + + pub fn state(mut self, state: InteractionState) -> Self { + self.state = state; + self + } + fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); let icon_color; - if self.state == UIState::Disabled { + if self.state == InteractionState::Disabled { icon_color = theme.highest.base.disabled.foreground; } else { icon_color = theme.highest.base.default.foreground; diff --git a/crates/storybook/src/modules/chat_panel.rs b/crates/storybook/src/modules/chat_panel.rs index 772bb908e0..25bd9debe3 100644 --- a/crates/storybook/src/modules/chat_panel.rs +++ b/crates/storybook/src/modules/chat_panel.rs @@ -1,12 +1,12 @@ use std::marker::PhantomData; use crate::components::icon_button; -use crate::prelude::{ButtonVariant, UIState}; use crate::theme::theme; use gpui2::elements::div::ScrollState; use gpui2::style::StyleHelpers; use gpui2::{elements::div, IntoElement}; use gpui2::{Element, ParentElement, ViewContext}; +use theme::IconButton; #[derive(Element)] pub struct ChatPanel { @@ -58,16 +58,8 @@ impl ChatPanel { .flex() .items_center() .gap_px() - .child(icon_button( - "icons/plus.svg", - ButtonVariant::Ghost, - UIState::Default, - )) - .child(icon_button( - "icons/split.svg", - ButtonVariant::Ghost, - UIState::Default, - )), + .child(icon_button::("icons/plus.svg")) + .child(icon_button::("icons/split.svg")), ), ) } diff --git a/crates/storybook/src/modules/tab_bar.rs b/crates/storybook/src/modules/tab_bar.rs index 5a3358588e..8cc7ab9433 100644 --- a/crates/storybook/src/modules/tab_bar.rs +++ b/crates/storybook/src/modules/tab_bar.rs @@ -1,12 +1,13 @@ use std::marker::PhantomData; use crate::components::{icon_button, tab}; -use crate::prelude::{ButtonVariant, UIState}; +use crate::prelude::InteractionState; use crate::theme::theme; use gpui2::elements::div::ScrollState; use gpui2::style::StyleHelpers; use gpui2::{elements::div, IntoElement}; use gpui2::{Element, ParentElement, ViewContext}; +use theme::IconButton; #[derive(Element)] pub struct TabBar { @@ -24,7 +25,8 @@ pub fn tab_bar(scroll_state: ScrollState) -> TabBar { impl TabBar { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); - + let can_navigate_back = true; + let can_navigate_forward = false; div() .w_full() .flex() @@ -41,16 +43,15 @@ impl TabBar { .flex() .items_center() .gap_px() - .child(icon_button( - "icons/arrow_left.svg", - ButtonVariant::Ghost, - UIState::Default, - )) - .child(icon_button( - "icons/arrow_right.svg", - ButtonVariant::Ghost, - UIState::Disabled, - )), + .child( + icon_button::("icons/arrow_left.svg") + .state(InteractionState::Enabled.if_enabled(can_navigate_back)), + ) + .child( + icon_button::("icons/arrow_right.svg").state( + InteractionState::Enabled.if_enabled(can_navigate_forward), + ), + ), ), ) .child( @@ -83,16 +84,8 @@ impl TabBar { .flex() .items_center() .gap_px() - .child(icon_button( - "icons/plus.svg", - ButtonVariant::Ghost, - UIState::Default, - )) - .child(icon_button( - "icons/split.svg", - ButtonVariant::Ghost, - UIState::Default, - )), + .child(icon_button::("icons/plus.svg")) + .child(icon_button::("icons/split.svg")), ), ) } diff --git a/crates/storybook/src/modules/title_bar.rs b/crates/storybook/src/modules/title_bar.rs index 9e1e0b3533..ead95c2009 100644 --- a/crates/storybook/src/modules/title_bar.rs +++ b/crates/storybook/src/modules/title_bar.rs @@ -1,11 +1,12 @@ use std::marker::PhantomData; use crate::components::{avatar, icon_button, tool_divider}; -use crate::prelude::{ButtonVariant, Shape, UIState}; +use crate::prelude::Shape; use crate::theme::theme; use gpui2::style::{StyleHelpers, Styleable}; use gpui2::{elements::div, IntoElement}; use gpui2::{Element, ParentElement, ViewContext}; +use theme::IconButton; #[derive(Element)] pub struct TitleBar { @@ -111,16 +112,8 @@ impl TitleBar { .flex() .items_center() .gap_1() - .child(icon_button( - "icons/stop_sharing.svg", - ButtonVariant::Ghost, - UIState::Default, - )) - .child(icon_button( - "icons/exit.svg", - ButtonVariant::Ghost, - UIState::Default, - )), + .child(icon_button::("icons/stop_sharing.svg")) + .child(icon_button::("icons/exit.svg")), ) .child(tool_divider()) .child( @@ -129,21 +122,9 @@ impl TitleBar { .flex() .items_center() .gap_1() - .child(icon_button( - "icons/radix/mic.svg", - ButtonVariant::Ghost, - UIState::Default, - )) - .child(icon_button( - "icons/radix/speaker-loud.svg", - ButtonVariant::Ghost, - UIState::Default, - )) - .child(icon_button( - "icons/radix/desktop.svg", - ButtonVariant::Ghost, - UIState::Default, - )), + .child(icon_button::("icons/radix/mic.svg")) + .child(icon_button::("icons/radix/speaker-loud.svg")) + .child(icon_button::("icons/radix/desktop.svg")), ) .child(div().px_2().flex().items_center().child(avatar( "https://avatars.githubusercontent.com/u/1714999?v=4", diff --git a/crates/storybook/src/prelude.rs b/crates/storybook/src/prelude.rs index c925c64620..50ab042cfe 100644 --- a/crates/storybook/src/prelude.rs +++ b/crates/storybook/src/prelude.rs @@ -1,26 +1,42 @@ -#[derive(PartialEq)] +#[derive(Default, PartialEq)] pub enum ButtonVariant { + #[default] Ghost, Filled, } -#[derive(PartialEq)] +#[derive(Default, PartialEq)] pub enum Shape { + #[default] Circle, RoundedRectangle, } -#[derive(PartialEq)] -pub enum UIState { - Default, +#[derive(Default, PartialEq, Clone, Copy)] +pub enum InteractionState { + #[default] + Enabled, Hovered, Active, Focused, + Dragged, Disabled, } -#[derive(PartialEq)] -pub enum UIToggleState { - Default, - Enabled, +impl InteractionState { + pub fn if_enabled(&self, enabled: bool) -> Self { + if enabled { + *self + } else { + InteractionState::Disabled + } + } +} + +#[derive(Default, PartialEq)] +pub enum SelectedState { + #[default] + Unselected, + PartiallySelected, + Selected, } diff --git a/docs/ui/states.md b/docs/ui/states.md new file mode 100644 index 0000000000..7dc3110ced --- /dev/null +++ b/docs/ui/states.md @@ -0,0 +1,43 @@ +## Interaction State + +**Enabled** + +An enabled state communicates an interactive component or element. + +**Disabled** + +A disabled state communicates a inoperable component or element. + +**Hover** + +A hover state communicates when a user has placed a cursor above an interactive element. + +**Focused** + +A focused state communicates when a user has highlighted an element, using an input method such as a keyboard or voice. + +**Activated** + +An activated state communicates a highlighted destination, whether initiated by the user or by default. + +**Pressed** + +A pressed state communicates a user tap. + +**Dragged** + +A dragged state communicates when a user presses and moves an element. + +## Selected State + +**Unselected** + +dfa + +**Partially Selected** + +daf + +**Selected** + +dfa