crates/ui2/Cargo.toml 🔗
@@ -4,6 +4,10 @@ version = "0.1.0"
edition = "2021"
publish = false
+[lib]
+name = "ui2"
+path = "src/ui2.rs"
+
[dependencies]
anyhow.workspace = true
chrono = "0.4"
Nate Butler created
crates/ui2/Cargo.toml | 4
crates/ui2/src/components.rs | 28 -
crates/ui2/src/components/avatar.rs | 28 -
crates/ui2/src/components/button.rs | 168 ----------
crates/ui2/src/components/checkbox.rs | 60 ---
crates/ui2/src/components/context_menu.rs | 113 ------
crates/ui2/src/components/details.rs | 83 -----
crates/ui2/src/components/elevated_surface.rs | 28 -
crates/ui2/src/components/facepile.rs | 33 --
crates/ui2/src/components/icon.rs | 28 -
crates/ui2/src/components/indicator.rs | 37 --
crates/ui2/src/components/input.rs | 23 -
crates/ui2/src/components/keybinding.rs | 67 ----
crates/ui2/src/components/label.rs | 32 -
crates/ui2/src/components/modal.rs | 85 -----
crates/ui2/src/components/notification_toast.rs | 40 --
crates/ui2/src/components/palette.rs | 212 -------------
crates/ui2/src/components/panel.rs | 152 ---------
crates/ui2/src/components/player.rs | 174 ----------
crates/ui2/src/components/player_stack.rs | 67 ----
crates/ui2/src/components/stories/avatar.rs | 27 +
crates/ui2/src/components/stories/button.rs | 167 ++++++++++
crates/ui2/src/components/stories/checkbox.rs | 59 +++
crates/ui2/src/components/stories/context_menu.rs | 112 ++++++
crates/ui2/src/components/stories/icon.rs | 27 +
crates/ui2/src/components/stories/input.rs | 22 +
crates/ui2/src/components/stories/keybinding.rs | 66 ++++
crates/ui2/src/components/stories/label.rs | 31 +
crates/ui2/src/components/stories/mod.rs | 8
crates/ui2/src/components/tab.rs | 276 -----------------
crates/ui2/src/components/toast.rs | 117 -------
crates/ui2/src/components/tool_divider.rs | 23 -
crates/ui2/src/prelude.rs | 1
crates/ui2/src/styles.rs | 2
crates/ui2/src/styles/docs/elevation.md | 0
crates/ui2/src/styles/elevation.rs | 2
crates/ui2/src/ui2.rs | 4
37 files changed, 530 insertions(+), 1,876 deletions(-)
@@ -4,6 +4,10 @@ version = "0.1.0"
edition = "2021"
publish = false
+[lib]
+name = "ui2"
+path = "src/ui2.rs"
+
[dependencies]
anyhow.workspace = true
chrono = "0.4"
@@ -2,56 +2,32 @@ mod avatar;
mod button;
mod checkbox;
mod context_menu;
-mod details;
mod divider;
-mod elevated_surface;
-mod facepile;
mod icon;
mod icon_button;
-mod indicator;
mod input;
mod keybinding;
mod label;
mod list;
-mod modal;
-mod notification_toast;
-mod palette;
-mod panel;
-mod player;
-mod player_stack;
mod slot;
mod stack;
-mod tab;
-mod toast;
+mod stories;
mod toggle;
-mod tool_divider;
mod tooltip;
pub use avatar::*;
pub use button::*;
pub use checkbox::*;
pub use context_menu::*;
-pub use details::*;
pub use divider::*;
-pub use elevated_surface::*;
-pub use facepile::*;
pub use icon::*;
pub use icon_button::*;
-pub use indicator::*;
pub use input::*;
pub use keybinding::*;
pub use label::*;
pub use list::*;
-pub use modal::*;
-pub use notification_toast::*;
-pub use palette::*;
-pub use panel::*;
-pub use player::*;
-pub use player_stack::*;
pub use slot::*;
pub use stack::*;
-pub use tab::*;
-pub use toast::*;
+pub use stories::*;
pub use toggle::*;
-pub use tool_divider::*;
pub use tooltip::*;
@@ -39,31 +39,3 @@ impl Avatar {
self
}
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::Story;
- use gpui::{Div, Render};
-
- pub struct AvatarStory;
-
- impl Render for AvatarStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .child(Story::title_for::<Avatar>(cx))
- .child(Story::label(cx, "Default"))
- .child(Avatar::new(
- "https://avatars.githubusercontent.com/u/1714999?v=4",
- ))
- .child(Avatar::new(
- "https://avatars.githubusercontent.com/u/326587?v=4",
- ))
- }
- }
-}
@@ -231,171 +231,3 @@ impl ButtonGroup {
Self { buttons }
}
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::{h_stack, v_stack, Story, TextColor};
- use gpui::{rems, Div, Render};
- use strum::IntoEnumIterator;
-
- pub struct ButtonStory;
-
- impl Render for ButtonStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- let states = InteractionState::iter();
-
- Story::container(cx)
- .child(Story::title_for::<Button>(cx))
- .child(
- div()
- .flex()
- .gap_8()
- .child(
- div()
- .child(Story::label(cx, "Ghost (Default)"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label").variant(ButtonVariant::Ghost), // .state(state),
- )
- })))
- .child(Story::label(cx, "Ghost – Left Icon"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Ghost)
- .icon(Icon::Plus)
- .icon_position(IconPosition::Left), // .state(state),
- )
- })))
- .child(Story::label(cx, "Ghost – Right Icon"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Ghost)
- .icon(Icon::Plus)
- .icon_position(IconPosition::Right), // .state(state),
- )
- }))),
- )
- .child(
- div()
- .child(Story::label(cx, "Filled"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label").variant(ButtonVariant::Filled), // .state(state),
- )
- })))
- .child(Story::label(cx, "Filled – Left Button"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Filled)
- .icon(Icon::Plus)
- .icon_position(IconPosition::Left), // .state(state),
- )
- })))
- .child(Story::label(cx, "Filled – Right Button"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Filled)
- .icon(Icon::Plus)
- .icon_position(IconPosition::Right), // .state(state),
- )
- }))),
- )
- .child(
- div()
- .child(Story::label(cx, "Fixed With"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Filled)
- // .state(state)
- .width(Some(rems(6.).into())),
- )
- })))
- .child(Story::label(cx, "Fixed With – Left Icon"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Filled)
- // .state(state)
- .icon(Icon::Plus)
- .icon_position(IconPosition::Left)
- .width(Some(rems(6.).into())),
- )
- })))
- .child(Story::label(cx, "Fixed With – Right Icon"))
- .child(h_stack().gap_2().children(states.clone().map(|state| {
- v_stack()
- .gap_1()
- .child(
- Label::new(state.to_string()).color(TextColor::Muted),
- )
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Filled)
- // .state(state)
- .icon(Icon::Plus)
- .icon_position(IconPosition::Right)
- .width(Some(rems(6.).into())),
- )
- }))),
- ),
- )
- .child(Story::label(cx, "Button with `on_click`"))
- .child(
- Button::new("Label")
- .variant(ButtonVariant::Ghost)
- .on_click(|_, cx| println!("Button clicked.")),
- )
- }
- }
-}
@@ -283,63 +283,3 @@ impl Checkbox {
)
}
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::{h_stack, Story};
- use gpui::{Div, Render, ViewContext};
-
- pub struct CheckboxStory;
-
- impl Render for CheckboxStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .child(Story::title_for::<Checkbox>(cx))
- .child(Story::label(cx, "Default"))
- .child(
- h_stack()
- .p_2()
- .gap_2()
- .rounded_md()
- .border()
- .border_color(cx.theme().colors().border)
- .child(Checkbox::new("checkbox-enabled", Selection::Unselected))
- .child(Checkbox::new(
- "checkbox-intermediate",
- Selection::Indeterminate,
- ))
- .child(Checkbox::new("checkbox-selected", Selection::Selected)),
- )
- .child(Story::label(cx, "Disabled"))
- .child(
- h_stack()
- .p_2()
- .gap_2()
- .rounded_md()
- .border()
- .border_color(cx.theme().colors().border)
- .child(
- Checkbox::new("checkbox-disabled", Selection::Unselected)
- .disabled(true),
- )
- .child(
- Checkbox::new(
- "checkbox-disabled-intermediate",
- Selection::Indeterminate,
- )
- .disabled(true),
- )
- .child(
- Checkbox::new("checkbox-disabled-selected", Selection::Selected)
- .disabled(true),
- ),
- )
- }
- }
-}
@@ -297,116 +297,3 @@ impl<M: ManagedView> RenderOnce for MenuHandle<M> {
self
}
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::{story::Story, Label};
- use gpui::{actions, Div, Render};
-
- actions!(PrintCurrentDate, PrintBestFood);
-
- fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<ContextMenu> {
- ContextMenu::build(cx, |menu, _| {
- menu.header(header)
- .separator()
- .entry(
- ListItem::new("Print current time", Label::new("Print current time")),
- |v, cx| {
- println!("dispatching PrintCurrentTime action");
- cx.dispatch_action(PrintCurrentDate.boxed_clone())
- },
- )
- .entry(
- ListItem::new("Print best food", Label::new("Print best food")),
- |v, cx| cx.dispatch_action(PrintBestFood.boxed_clone()),
- )
- })
- }
-
- pub struct ContextMenuStory;
-
- impl Render for ContextMenuStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .on_action(|_: &PrintCurrentDate, _| {
- println!("printing unix time!");
- if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
- println!("Current Unix time is {:?}", unix_time.as_secs());
- }
- })
- .on_action(|_: &PrintBestFood, _| {
- println!("burrito");
- })
- .flex()
- .flex_row()
- .justify_between()
- .child(
- div()
- .flex()
- .flex_col()
- .justify_between()
- .child(
- menu_handle("test2")
- .child(|is_open| {
- Label::new(if is_open {
- "TOP LEFT"
- } else {
- "RIGHT CLICK ME"
- })
- })
- .menu(move |cx| build_menu(cx, "top left")),
- )
- .child(
- menu_handle("test1")
- .child(|is_open| {
- Label::new(if is_open {
- "BOTTOM LEFT"
- } else {
- "RIGHT CLICK ME"
- })
- })
- .anchor(AnchorCorner::BottomLeft)
- .attach(AnchorCorner::TopLeft)
- .menu(move |cx| build_menu(cx, "bottom left")),
- ),
- )
- .child(
- div()
- .flex()
- .flex_col()
- .justify_between()
- .child(
- menu_handle("test3")
- .child(|is_open| {
- Label::new(if is_open {
- "TOP RIGHT"
- } else {
- "RIGHT CLICK ME"
- })
- })
- .anchor(AnchorCorner::TopRight)
- .menu(move |cx| build_menu(cx, "top right")),
- )
- .child(
- menu_handle("test4")
- .child(|is_open| {
- Label::new(if is_open {
- "BOTTOM RIGHT"
- } else {
- "RIGHT CLICK ME"
- })
- })
- .anchor(AnchorCorner::BottomRight)
- .attach(AnchorCorner::TopRight)
- .menu(move |cx| build_menu(cx, "bottom right")),
- ),
- )
- }
- }
-}
@@ -1,83 +0,0 @@
-use crate::prelude::*;
-use crate::{v_stack, ButtonGroup};
-
-#[derive(RenderOnce)]
-pub struct Details {
- text: &'static str,
- meta: Option<&'static str>,
- actions: Option<ButtonGroup>,
-}
-
-impl Component for Details {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- v_stack()
- .p_1()
- .gap_0p5()
- .text_ui_sm()
- .text_color(cx.theme().colors().text)
- .size_full()
- .child(self.text)
- .children(self.meta.map(|m| m))
- .children(self.actions.map(|a| a))
- }
-}
-
-impl Details {
- pub fn new(text: &'static str) -> Self {
- Self {
- text,
- meta: None,
- actions: None,
- }
- }
-
- pub fn meta_text(mut self, meta: &'static str) -> Self {
- self.meta = Some(meta);
- self
- }
-
- pub fn actions(mut self, actions: ButtonGroup) -> Self {
- self.actions = Some(actions);
- self
- }
-}
-
-use gpui::{Div, RenderOnce};
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::{Button, Story};
- use gpui::{Div, Render};
-
- pub struct DetailsStory;
-
- impl Render for DetailsStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .child(Story::title_for::<Details>(cx))
- .child(Story::label(cx, "Default"))
- .child(Details::new("The quick brown fox jumps over the lazy dog"))
- .child(Story::label(cx, "With meta"))
- .child(
- Details::new("The quick brown fox jumps over the lazy dog")
- .meta_text("Sphinx of black quartz, judge my vow."),
- )
- .child(Story::label(cx, "With meta and actions"))
- .child(
- Details::new("The quick brown fox jumps over the lazy dog")
- .meta_text("Sphinx of black quartz, judge my vow.")
- .actions(ButtonGroup::new(vec![
- Button::new("Decline"),
- Button::new("Accept").variant(crate::ButtonVariant::Filled),
- ])),
- )
- }
- }
-}
@@ -1,28 +0,0 @@
-use gpui::Div;
-
-use crate::{prelude::*, v_stack};
-
-/// Create an elevated surface.
-///
-/// Must be used inside of a relative parent element
-pub fn elevated_surface(level: ElevationIndex, cx: &mut WindowContext) -> Div {
- let colors = cx.theme().colors();
-
- // let shadow = BoxShadow {
- // color: hsla(0., 0., 0., 0.1),
- // offset: point(px(0.), px(1.)),
- // blur_radius: px(3.),
- // spread_radius: px(0.),
- // };
-
- v_stack()
- .rounded_lg()
- .bg(colors.elevated_surface_background)
- .border()
- .border_color(colors.border)
- .shadow(level.shadow())
-}
-
-pub fn modal(cx: &mut WindowContext) -> Div {
- elevated_surface(ElevationIndex::ModalSurface, cx)
-}
@@ -1,33 +0,0 @@
-use crate::prelude::*;
-use crate::{Avatar, Player};
-
-#[derive(RenderOnce)]
-pub struct Facepile {
- players: Vec<Player>,
-}
-
-impl Component for Facepile {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let player_count = self.players.len();
- let player_list = self.players.iter().enumerate().map(|(ix, player)| {
- let isnt_last = ix < player_count - 1;
-
- div()
- .when(isnt_last, |div| div.neg_mr_1())
- .child(Avatar::new(player.avatar_src().to_string()))
- });
- div().p_1().flex().items_center().children(player_list)
- }
-}
-
-impl Facepile {
- pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
- Self {
- players: players.collect(),
- }
- }
-}
-
-use gpui::{Div, RenderOnce};
@@ -197,31 +197,3 @@ impl IconElement {
.text_color(self.color.color(cx))
}
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use gpui::{Div, Render};
- use strum::IntoEnumIterator;
-
- use crate::Story;
-
- use super::*;
-
- pub struct IconStory;
-
- impl Render for IconStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- let icons = Icon::iter();
-
- Story::container(cx)
- .child(Story::title_for::<IconElement>(cx))
- .child(Story::label(cx, "All Icons"))
- .child(div().flex().gap_3().children(icons.map(IconElement::new)))
- }
- }
-}
@@ -1,37 +0,0 @@
-use crate::prelude::*;
-use gpui::{px, Div, RenderOnce};
-
-#[derive(RenderOnce)]
-pub struct UnreadIndicator;
-
-impl Component for UnreadIndicator {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- div()
- .rounded_full()
- .border_2()
- .border_color(cx.theme().colors().surface_background)
- .w(px(9.0))
- .h(px(9.0))
- .z_index(2)
- .bg(cx.theme().status().info)
- }
-}
-
-impl UnreadIndicator {
- pub fn new() -> Self {
- Self
- }
-
- fn render(self, cx: &mut WindowContext) -> impl Element {
- div()
- .rounded_full()
- .border_2()
- .border_color(cx.theme().colors().surface_background)
- .w(px(9.0))
- .h(px(9.0))
- .z_index(2)
- .bg(cx.theme().status().info)
- }
-}
@@ -106,26 +106,3 @@ impl Input {
self
}
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::Story;
- use gpui::{Div, Render};
-
- pub struct InputStory;
-
- impl Render for InputStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .child(Story::title_for::<Input>(cx))
- .child(Story::label(cx, "Default"))
- .child(div().flex().child(Input::new("Search")))
- }
- }
-}
@@ -69,70 +69,3 @@ impl Key {
Self { key: key.into() }
}
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- pub use crate::KeyBinding;
- use crate::Story;
- use gpui::{actions, Div, Render};
- use itertools::Itertools;
- pub struct KeybindingStory;
-
- actions!(NoAction);
-
- pub fn binding(key: &str) -> gpui::KeyBinding {
- gpui::KeyBinding::new(key, NoAction {}, None)
- }
-
- impl Render for KeybindingStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- let all_modifier_permutations =
- ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
-
- Story::container(cx)
- .child(Story::title_for::<KeyBinding>(cx))
- .child(Story::label(cx, "Single Key"))
- .child(KeyBinding::new(binding("Z")))
- .child(Story::label(cx, "Single Key with Modifier"))
- .child(
- div()
- .flex()
- .gap_3()
- .child(KeyBinding::new(binding("ctrl-c")))
- .child(KeyBinding::new(binding("alt-c")))
- .child(KeyBinding::new(binding("cmd-c")))
- .child(KeyBinding::new(binding("shift-c"))),
- )
- .child(Story::label(cx, "Single Key with Modifier (Permuted)"))
- .child(
- div().flex().flex_col().children(
- all_modifier_permutations
- .chunks(4)
- .into_iter()
- .map(|chunk| {
- div()
- .flex()
- .gap_4()
- .py_3()
- .children(chunk.map(|permutation| {
- KeyBinding::new(binding(&*(permutation.join("-") + "-x")))
- }))
- }),
- ),
- )
- .child(Story::label(cx, "Single Key with All Modifiers"))
- .child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
- .child(Story::label(cx, "Chord"))
- .child(KeyBinding::new(binding("a z")))
- .child(Story::label(cx, "Chord with Modifier"))
- .child(KeyBinding::new(binding("ctrl-a shift-z")))
- .child(KeyBinding::new(binding("fn-s")))
- }
- }
-}
@@ -230,35 +230,3 @@ struct Run {
pub text: String,
pub color: Hsla,
}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::Story;
- use gpui::{Div, Render};
-
- pub struct LabelStory;
-
- impl Render for LabelStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .child(Story::title_for::<Label>(cx))
- .child(Story::label(cx, "Default"))
- .child(Label::new("Hello, world!"))
- .child(Story::label(cx, "Highlighted"))
- .child(HighlightedLabel::new(
- "Hello, world!",
- vec![0, 1, 2, 7, 8, 12],
- ))
- .child(HighlightedLabel::new(
- "Héllo, world!",
- vec![0, 1, 3, 8, 9, 13],
- ))
- }
- }
-}
@@ -1,85 +0,0 @@
-use gpui::{AnyElement, Div, RenderOnce};
-use smallvec::SmallVec;
-
-use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label};
-
-#[derive(RenderOnce)]
-pub struct Modal {
- id: ElementId,
- title: Option<SharedString>,
- primary_action: Option<Button>,
- secondary_action: Option<Button>,
- children: SmallVec<[AnyElement; 2]>,
-}
-
-impl Component for Modal {
- type Rendered = gpui::Stateful<Div>;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- v_stack()
- .id(self.id.clone())
- .w_96()
- // .rounded_xl()
- .bg(cx.theme().colors().background)
- .border()
- .border_color(cx.theme().colors().border)
- .shadow_2xl()
- .child(
- h_stack()
- .justify_between()
- .p_1()
- .border_b()
- .border_color(cx.theme().colors().border)
- .child(div().children(self.title.clone().map(|t| Label::new(t))))
- .child(IconButton::new("close", Icon::Close)),
- )
- .child(v_stack().p_1().children(self.children))
- .when(
- self.primary_action.is_some() || self.secondary_action.is_some(),
- |this| {
- this.child(
- h_stack()
- .border_t()
- .border_color(cx.theme().colors().border)
- .p_1()
- .justify_end()
- .children(self.secondary_action)
- .children(self.primary_action),
- )
- },
- )
- }
-}
-
-impl Modal {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self {
- id: id.into(),
- title: None,
- primary_action: None,
- secondary_action: None,
- children: SmallVec::new(),
- }
- }
-
- pub fn title(mut self, title: impl Into<SharedString>) -> Self {
- self.title = Some(title.into());
- self
- }
-
- pub fn primary_action(mut self, action: Button) -> Self {
- self.primary_action = Some(action);
- self
- }
-
- pub fn secondary_action(mut self, action: Button) -> Self {
- self.secondary_action = Some(action);
- self
- }
-}
-
-impl ParentElement for Modal {
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
- &mut self.children
- }
-}
@@ -1,40 +0,0 @@
-use gpui::rems;
-
-use crate::prelude::*;
-use crate::{h_stack, Icon};
-
-// #[derive(RenderOnce)]
-pub struct NotificationToast {
- label: SharedString,
- icon: Option<Icon>,
-}
-
-impl NotificationToast {
- pub fn new(label: SharedString) -> Self {
- Self { label, icon: None }
- }
-
- pub fn icon<I>(mut self, icon: I) -> Self
- where
- I: Into<Option<Icon>>,
- {
- self.icon = icon.into();
- self
- }
-
- fn render(self, cx: &mut WindowContext) -> impl Element {
- h_stack()
- .z_index(5)
- .absolute()
- .top_1()
- .right_1()
- .w(rems(9999.))
- .max_w_56()
- .py_1()
- .px_1p5()
- .rounded_lg()
- .shadow_md()
- .bg(cx.theme().colors().elevated_surface_background)
- .child(div().size_full().child(self.label.clone()))
- }
-}
@@ -1,212 +0,0 @@
-use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
-use gpui::prelude::*;
-use gpui::Div;
-
-#[derive(RenderOnce)]
-pub struct Palette {
- id: ElementId,
- input_placeholder: SharedString,
- empty_string: SharedString,
- items: Vec<PaletteItem>,
- default_order: OrderMethod,
-}
-
-impl Component for Palette {
- type Rendered = gpui::Stateful<Div>;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- v_stack()
- .id(self.id)
- .w_96()
- .rounded_lg()
- .bg(cx.theme().colors().elevated_surface_background)
- .border()
- .border_color(cx.theme().colors().border)
- .child(
- v_stack()
- .gap_px()
- .child(v_stack().py_0p5().px_1().child(
- div().px_2().py_0p5().child(
- Label::new(self.input_placeholder).color(TextColor::Placeholder),
- ),
- ))
- .child(
- div()
- .h_px()
- .w_full()
- .bg(cx.theme().colors().element_background),
- )
- .child(
- v_stack()
- .id("items")
- .py_0p5()
- .px_1()
- .grow()
- .max_h_96()
- .overflow_y_scroll()
- .children(
- vec![if self.items.is_empty() {
- Some(h_stack().justify_between().px_2().py_1().child(
- Label::new(self.empty_string).color(TextColor::Muted),
- ))
- } else {
- None
- }]
- .into_iter()
- .flatten(),
- )
- .children(self.items.into_iter().enumerate().map(|(index, item)| {
- h_stack()
- .id(index)
- .justify_between()
- .px_2()
- .py_0p5()
- .rounded_lg()
- .hover(|style| {
- style.bg(cx.theme().colors().ghost_element_hover)
- })
- .active(|style| {
- style.bg(cx.theme().colors().ghost_element_active)
- })
- .child(item)
- })),
- ),
- )
- }
-}
-
-impl Palette {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self {
- id: id.into(),
- input_placeholder: "Find something...".into(),
- empty_string: "No items found.".into(),
- items: vec![],
- default_order: OrderMethod::default(),
- }
- }
-
- pub fn items(mut self, items: Vec<PaletteItem>) -> Self {
- self.items = items;
- self
- }
-
- pub fn placeholder(mut self, input_placeholder: impl Into<SharedString>) -> Self {
- self.input_placeholder = input_placeholder.into();
- self
- }
-
- pub fn empty_string(mut self, empty_string: impl Into<SharedString>) -> Self {
- self.empty_string = empty_string.into();
- self
- }
-
- // TODO: Hook up sort order
- pub fn default_order(mut self, default_order: OrderMethod) -> Self {
- self.default_order = default_order;
- self
- }
-}
-
-#[derive(RenderOnce)]
-pub struct PaletteItem {
- pub label: SharedString,
- pub sublabel: Option<SharedString>,
- pub key_binding: Option<KeyBinding>,
-}
-
-impl Component for PaletteItem {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- div()
- .flex()
- .flex_row()
- .grow()
- .justify_between()
- .child(
- v_stack()
- .child(Label::new(self.label))
- .children(self.sublabel.map(|sublabel| Label::new(sublabel))),
- )
- .children(self.key_binding)
- }
-}
-
-impl PaletteItem {
- pub fn new(label: impl Into<SharedString>) -> Self {
- Self {
- label: label.into(),
- sublabel: None,
- key_binding: None,
- }
- }
-
- pub fn label(mut self, label: impl Into<SharedString>) -> Self {
- self.label = label.into();
- self
- }
-
- pub fn sublabel(mut self, sublabel: impl Into<Option<SharedString>>) -> Self {
- self.sublabel = sublabel.into();
- self
- }
-
- pub fn key_binding(mut self, key_binding: impl Into<Option<KeyBinding>>) -> Self {
- self.key_binding = key_binding.into();
- self
- }
-}
-
-use gpui::ElementId;
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use gpui::{Div, Render};
-
- use crate::{binding, Story};
-
- use super::*;
-
- pub struct PaletteStory;
-
- impl Render for PaletteStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- {
- Story::container(cx)
- .child(Story::title_for::<Palette>(cx))
- .child(Story::label(cx, "Default"))
- .child(Palette::new("palette-1"))
- .child(Story::label(cx, "With Items"))
- .child(
- Palette::new("palette-2")
- .placeholder("Execute a command...")
- .items(vec![
- PaletteItem::new("theme selector: toggle")
- .key_binding(KeyBinding::new(binding("cmd-k cmd-t"))),
- PaletteItem::new("assistant: inline assist")
- .key_binding(KeyBinding::new(binding("cmd-enter"))),
- PaletteItem::new("assistant: quote selection")
- .key_binding(KeyBinding::new(binding("cmd-<"))),
- PaletteItem::new("assistant: toggle focus")
- .key_binding(KeyBinding::new(binding("cmd-?"))),
- PaletteItem::new("auto update: check"),
- PaletteItem::new("auto update: view release notes"),
- PaletteItem::new("branches: open recent")
- .key_binding(KeyBinding::new(binding("cmd-alt-b"))),
- PaletteItem::new("chat panel: toggle focus"),
- PaletteItem::new("cli: install"),
- PaletteItem::new("client: sign in"),
- PaletteItem::new("client: sign out"),
- PaletteItem::new("editor: cancel")
- .key_binding(KeyBinding::new(binding("escape"))),
- ]),
- )
- }
- }
- }
-}
@@ -1,152 +0,0 @@
-use gpui::px;
-use gpui::{prelude::*, AbsoluteLength, AnyElement, Div, RenderOnce};
-use smallvec::SmallVec;
-
-use crate::prelude::*;
-use crate::v_stack;
-
-#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
-pub enum PanelAllowedSides {
- LeftOnly,
- RightOnly,
- BottomOnly,
- #[default]
- LeftAndRight,
- All,
-}
-
-impl PanelAllowedSides {
- /// Return a `HashSet` that contains the allowable `PanelSide`s.
- pub fn allowed_sides(&self) -> HashSet<PanelSide> {
- match self {
- Self::LeftOnly => HashSet::from_iter([PanelSide::Left]),
- Self::RightOnly => HashSet::from_iter([PanelSide::Right]),
- Self::BottomOnly => HashSet::from_iter([PanelSide::Bottom]),
- Self::LeftAndRight => HashSet::from_iter([PanelSide::Left, PanelSide::Right]),
- Self::All => HashSet::from_iter([PanelSide::Left, PanelSide::Right, PanelSide::Bottom]),
- }
- }
-}
-
-#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)]
-pub enum PanelSide {
- #[default]
- Left,
- Right,
- Bottom,
-}
-
-use std::collections::HashSet;
-
-#[derive(RenderOnce)]
-pub struct Panel {
- id: ElementId,
- current_side: PanelSide,
- /// Defaults to PanelAllowedSides::LeftAndRight
- allowed_sides: PanelAllowedSides,
- initial_width: AbsoluteLength,
- width: Option<AbsoluteLength>,
- children: SmallVec<[AnyElement; 2]>,
-}
-
-impl Component for Panel {
- type Rendered = gpui::Stateful<Div>;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let current_size = self.width.unwrap_or(self.initial_width);
-
- v_stack()
- .id(self.id.clone())
- .flex_initial()
- .map(|this| match self.current_side {
- PanelSide::Left | PanelSide::Right => this.h_full().w(current_size),
- PanelSide::Bottom => this,
- })
- .map(|this| match self.current_side {
- PanelSide::Left => this.border_r(),
- PanelSide::Right => this.border_l(),
- PanelSide::Bottom => this.border_b().w_full().h(current_size),
- })
- .bg(cx.theme().colors().surface_background)
- .border_color(cx.theme().colors().border)
- .children(self.children)
- }
-}
-
-impl Panel {
- pub fn new(id: impl Into<ElementId>, cx: &mut WindowContext) -> Self {
- Self {
- id: id.into(),
- current_side: PanelSide::default(),
- allowed_sides: PanelAllowedSides::default(),
- initial_width: px(320.).into(),
- width: None,
- children: SmallVec::new(),
- }
- }
-
- pub fn initial_width(mut self, initial_width: AbsoluteLength) -> Self {
- self.initial_width = initial_width;
- self
- }
-
- pub fn width(mut self, width: AbsoluteLength) -> Self {
- self.width = Some(width);
- self
- }
-
- pub fn allowed_sides(mut self, allowed_sides: PanelAllowedSides) -> Self {
- self.allowed_sides = allowed_sides;
- self
- }
-
- pub fn side(mut self, side: PanelSide) -> Self {
- let allowed_sides = self.allowed_sides.allowed_sides();
-
- if allowed_sides.contains(&side) {
- self.current_side = side;
- } else {
- panic!(
- "The panel side {:?} was not added as allowed before it was set.",
- side
- );
- }
- self
- }
-}
-
-impl ParentElement for Panel {
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
- &mut self.children
- }
-}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use super::*;
- use crate::{Label, Story};
- use gpui::{Div, InteractiveElement, Render};
-
- pub struct PanelStory;
-
- impl Render for PanelStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .child(Story::title_for::<Panel>(cx))
- .child(Story::label(cx, "Default"))
- .child(
- Panel::new("panel", cx).child(
- div()
- .id("panel-contents")
- .overflow_y_scroll()
- .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1)))),
- ),
- )
- }
- }
-}
@@ -1,174 +0,0 @@
-use gpui::Hsla;
-
-use crate::prelude::*;
-
-/// Represents a person with a Zed account's public profile.
-/// All data in this struct should be considered public.
-pub struct PublicPlayer {
- pub username: SharedString,
- pub avatar: SharedString,
- pub is_contact: bool,
-}
-
-impl PublicPlayer {
- pub fn new(username: impl Into<SharedString>, avatar: impl Into<SharedString>) -> Self {
- Self {
- username: username.into(),
- avatar: avatar.into(),
- is_contact: false,
- }
- }
-}
-
-#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
-pub enum PlayerStatus {
- #[default]
- Offline,
- Online,
- InCall,
- Away,
- DoNotDisturb,
- Invisible,
-}
-
-#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
-pub enum MicStatus {
- Muted,
- #[default]
- Unmuted,
-}
-
-impl MicStatus {
- pub fn inverse(&self) -> Self {
- match self {
- Self::Muted => Self::Unmuted,
- Self::Unmuted => Self::Muted,
- }
- }
-}
-
-#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
-pub enum VideoStatus {
- On,
- #[default]
- Off,
-}
-
-impl VideoStatus {
- pub fn inverse(&self) -> Self {
- match self {
- Self::On => Self::Off,
- Self::Off => Self::On,
- }
- }
-}
-
-#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
-pub enum ScreenShareStatus {
- Shared,
- #[default]
- NotShared,
-}
-
-impl ScreenShareStatus {
- pub fn inverse(&self) -> Self {
- match self {
- Self::Shared => Self::NotShared,
- Self::NotShared => Self::Shared,
- }
- }
-}
-
-#[derive(Clone)]
-pub struct PlayerCallStatus {
- pub mic_status: MicStatus,
- /// Indicates if the player is currently speaking
- /// And the intensity of the volume coming through
- ///
- /// 0.0 - 1.0
- pub voice_activity: f32,
- pub video_status: VideoStatus,
- pub screen_share_status: ScreenShareStatus,
- pub in_current_project: bool,
- pub disconnected: bool,
- pub following: Option<Vec<Player>>,
- pub followers: Option<Vec<Player>>,
-}
-
-impl PlayerCallStatus {
- pub fn new() -> Self {
- Self {
- mic_status: MicStatus::default(),
- voice_activity: 0.,
- video_status: VideoStatus::default(),
- screen_share_status: ScreenShareStatus::default(),
- in_current_project: true,
- disconnected: false,
- following: None,
- followers: None,
- }
- }
-}
-
-#[derive(PartialEq, Clone)]
-pub struct Player {
- index: usize,
- avatar_src: String,
- username: String,
- status: PlayerStatus,
-}
-
-#[derive(Clone)]
-pub struct PlayerWithCallStatus {
- player: Player,
- call_status: PlayerCallStatus,
-}
-
-impl PlayerWithCallStatus {
- pub fn new(player: Player, call_status: PlayerCallStatus) -> Self {
- Self {
- player,
- call_status,
- }
- }
-
- pub fn get_player(&self) -> &Player {
- &self.player
- }
-
- pub fn get_call_status(&self) -> &PlayerCallStatus {
- &self.call_status
- }
-}
-
-impl Player {
- pub fn new(index: usize, avatar_src: String, username: String) -> Self {
- Self {
- index,
- avatar_src,
- username,
- status: Default::default(),
- }
- }
-
- pub fn set_status(mut self, status: PlayerStatus) -> Self {
- self.status = status;
- self
- }
-
- pub fn cursor_color(&self, cx: &mut WindowContext) -> Hsla {
- cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].cursor
- }
-
- pub fn selection_color(&self, cx: &mut WindowContext) -> Hsla {
- cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].selection
- }
-
- pub fn avatar_src(&self) -> &str {
- &self.avatar_src
- }
-
- pub fn index(&self) -> usize {
- self.index
- }
-}
@@ -1,67 +0,0 @@
-use gpui::{Div, RenderOnce};
-
-use crate::prelude::*;
-use crate::{Avatar, Facepile, PlayerWithCallStatus};
-
-#[derive(RenderOnce)]
-pub struct PlayerStack {
- player_with_call_status: PlayerWithCallStatus,
-}
-
-impl Component for PlayerStack {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let player = self.player_with_call_status.get_player();
-
- let followers = self
- .player_with_call_status
- .get_call_status()
- .followers
- .as_ref()
- .map(|followers| followers.clone());
-
- // if we have no followers return a slightly different element
- // if mic_status == muted add a red ring to avatar
-
- div()
- .h_full()
- .flex()
- .flex_col()
- .gap_px()
- .justify_center()
- .child(
- div()
- .flex()
- .justify_center()
- .w_full()
- .child(div().w_4().h_0p5().rounded_sm().bg(player.cursor_color(cx))),
- )
- .child(
- div()
- .flex()
- .items_center()
- .justify_center()
- .h_6()
- .pl_1()
- .rounded_lg()
- .bg(if followers.is_none() {
- cx.theme().styles.system.transparent
- } else {
- player.selection_color(cx)
- })
- .child(Avatar::new(player.avatar_src().to_string()))
- .children(followers.map(|followers| {
- div().neg_ml_2().child(Facepile::new(followers.into_iter()))
- })),
- )
- }
-}
-
-impl PlayerStack {
- pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
- Self {
- player_with_call_status,
- }
- }
-}
@@ -0,0 +1,27 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ use crate::Story;
+ use gpui::{Div, Render};
+
+ pub struct AvatarStory;
+
+ impl Render for AvatarStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ Story::container(cx)
+ .child(Story::title_for::<Avatar>(cx))
+ .child(Story::label(cx, "Default"))
+ .child(Avatar::new(
+ "https://avatars.githubusercontent.com/u/1714999?v=4",
+ ))
+ .child(Avatar::new(
+ "https://avatars.githubusercontent.com/u/326587?v=4",
+ ))
+ }
+ }
+}
@@ -0,0 +1,167 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ use crate::{h_stack, v_stack, Story, TextColor};
+ use gpui::{rems, Div, Render};
+ use strum::IntoEnumIterator;
+
+ pub struct ButtonStory;
+
+ impl Render for ButtonStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let states = InteractionState::iter();
+
+ Story::container(cx)
+ .child(Story::title_for::<Button>(cx))
+ .child(
+ div()
+ .flex()
+ .gap_8()
+ .child(
+ div()
+ .child(Story::label(cx, "Ghost (Default)"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label").variant(ButtonVariant::Ghost), // .state(state),
+ )
+ })))
+ .child(Story::label(cx, "Ghost – Left Icon"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Ghost)
+ .icon(Icon::Plus)
+ .icon_position(IconPosition::Left), // .state(state),
+ )
+ })))
+ .child(Story::label(cx, "Ghost – Right Icon"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Ghost)
+ .icon(Icon::Plus)
+ .icon_position(IconPosition::Right), // .state(state),
+ )
+ }))),
+ )
+ .child(
+ div()
+ .child(Story::label(cx, "Filled"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label").variant(ButtonVariant::Filled), // .state(state),
+ )
+ })))
+ .child(Story::label(cx, "Filled – Left Button"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Filled)
+ .icon(Icon::Plus)
+ .icon_position(IconPosition::Left), // .state(state),
+ )
+ })))
+ .child(Story::label(cx, "Filled – Right Button"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Filled)
+ .icon(Icon::Plus)
+ .icon_position(IconPosition::Right), // .state(state),
+ )
+ }))),
+ )
+ .child(
+ div()
+ .child(Story::label(cx, "Fixed With"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Filled)
+ // .state(state)
+ .width(Some(rems(6.).into())),
+ )
+ })))
+ .child(Story::label(cx, "Fixed With – Left Icon"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Filled)
+ // .state(state)
+ .icon(Icon::Plus)
+ .icon_position(IconPosition::Left)
+ .width(Some(rems(6.).into())),
+ )
+ })))
+ .child(Story::label(cx, "Fixed With – Right Icon"))
+ .child(h_stack().gap_2().children(states.clone().map(|state| {
+ v_stack()
+ .gap_1()
+ .child(
+ Label::new(state.to_string()).color(TextColor::Muted),
+ )
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Filled)
+ // .state(state)
+ .icon(Icon::Plus)
+ .icon_position(IconPosition::Right)
+ .width(Some(rems(6.).into())),
+ )
+ }))),
+ ),
+ )
+ .child(Story::label(cx, "Button with `on_click`"))
+ .child(
+ Button::new("Label")
+ .variant(ButtonVariant::Ghost)
+ .on_click(|_, cx| println!("Button clicked.")),
+ )
+ }
+ }
+}
@@ -0,0 +1,59 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ use crate::{h_stack, Story};
+ use gpui::{Div, Render, ViewContext};
+
+ pub struct CheckboxStory;
+
+ impl Render for CheckboxStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ Story::container(cx)
+ .child(Story::title_for::<Checkbox>(cx))
+ .child(Story::label(cx, "Default"))
+ .child(
+ h_stack()
+ .p_2()
+ .gap_2()
+ .rounded_md()
+ .border()
+ .border_color(cx.theme().colors().border)
+ .child(Checkbox::new("checkbox-enabled", Selection::Unselected))
+ .child(Checkbox::new(
+ "checkbox-intermediate",
+ Selection::Indeterminate,
+ ))
+ .child(Checkbox::new("checkbox-selected", Selection::Selected)),
+ )
+ .child(Story::label(cx, "Disabled"))
+ .child(
+ h_stack()
+ .p_2()
+ .gap_2()
+ .rounded_md()
+ .border()
+ .border_color(cx.theme().colors().border)
+ .child(
+ Checkbox::new("checkbox-disabled", Selection::Unselected)
+ .disabled(true),
+ )
+ .child(
+ Checkbox::new(
+ "checkbox-disabled-intermediate",
+ Selection::Indeterminate,
+ )
+ .disabled(true),
+ )
+ .child(
+ Checkbox::new("checkbox-disabled-selected", Selection::Selected)
+ .disabled(true),
+ ),
+ )
+ }
+ }
+}
@@ -0,0 +1,112 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ use crate::{story::Story, Label};
+ use gpui::{actions, Div, Render};
+
+ actions!(PrintCurrentDate, PrintBestFood);
+
+ fn build_menu(cx: &mut WindowContext, header: impl Into<SharedString>) -> View<ContextMenu> {
+ ContextMenu::build(cx, |menu, _| {
+ menu.header(header)
+ .separator()
+ .entry(
+ ListItem::new("Print current time", Label::new("Print current time")),
+ |v, cx| {
+ println!("dispatching PrintCurrentTime action");
+ cx.dispatch_action(PrintCurrentDate.boxed_clone())
+ },
+ )
+ .entry(
+ ListItem::new("Print best food", Label::new("Print best food")),
+ |v, cx| cx.dispatch_action(PrintBestFood.boxed_clone()),
+ )
+ })
+ }
+
+ pub struct ContextMenuStory;
+
+ impl Render for ContextMenuStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ Story::container(cx)
+ .on_action(|_: &PrintCurrentDate, _| {
+ println!("printing unix time!");
+ if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() {
+ println!("Current Unix time is {:?}", unix_time.as_secs());
+ }
+ })
+ .on_action(|_: &PrintBestFood, _| {
+ println!("burrito");
+ })
+ .flex()
+ .flex_row()
+ .justify_between()
+ .child(
+ div()
+ .flex()
+ .flex_col()
+ .justify_between()
+ .child(
+ menu_handle("test2")
+ .child(|is_open| {
+ Label::new(if is_open {
+ "TOP LEFT"
+ } else {
+ "RIGHT CLICK ME"
+ })
+ })
+ .menu(move |cx| build_menu(cx, "top left")),
+ )
+ .child(
+ menu_handle("test1")
+ .child(|is_open| {
+ Label::new(if is_open {
+ "BOTTOM LEFT"
+ } else {
+ "RIGHT CLICK ME"
+ })
+ })
+ .anchor(AnchorCorner::BottomLeft)
+ .attach(AnchorCorner::TopLeft)
+ .menu(move |cx| build_menu(cx, "bottom left")),
+ ),
+ )
+ .child(
+ div()
+ .flex()
+ .flex_col()
+ .justify_between()
+ .child(
+ menu_handle("test3")
+ .child(|is_open| {
+ Label::new(if is_open {
+ "TOP RIGHT"
+ } else {
+ "RIGHT CLICK ME"
+ })
+ })
+ .anchor(AnchorCorner::TopRight)
+ .menu(move |cx| build_menu(cx, "top right")),
+ )
+ .child(
+ menu_handle("test4")
+ .child(|is_open| {
+ Label::new(if is_open {
+ "BOTTOM RIGHT"
+ } else {
+ "RIGHT CLICK ME"
+ })
+ })
+ .anchor(AnchorCorner::BottomRight)
+ .attach(AnchorCorner::TopRight)
+ .menu(move |cx| build_menu(cx, "bottom right")),
+ ),
+ )
+ }
+ }
+}
@@ -0,0 +1,27 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use gpui::{Div, Render};
+ use strum::IntoEnumIterator;
+
+ use crate::Story;
+
+ use super::*;
+
+ pub struct IconStory;
+
+ impl Render for IconStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let icons = Icon::iter();
+
+ Story::container(cx)
+ .child(Story::title_for::<IconElement>(cx))
+ .child(Story::label(cx, "All Icons"))
+ .child(div().flex().gap_3().children(icons.map(IconElement::new)))
+ }
+ }
+}
@@ -0,0 +1,22 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ use crate::Story;
+ use gpui::{Div, Render};
+
+ pub struct InputStory;
+
+ impl Render for InputStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ Story::container(cx)
+ .child(Story::title_for::<Input>(cx))
+ .child(Story::label(cx, "Default"))
+ .child(div().flex().child(Input::new("Search")))
+ }
+ }
+}
@@ -0,0 +1,66 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ pub use crate::KeyBinding;
+ use crate::Story;
+ use gpui::{actions, Div, Render};
+ use itertools::Itertools;
+ pub struct KeybindingStory;
+
+ actions!(NoAction);
+
+ pub fn binding(key: &str) -> gpui::KeyBinding {
+ gpui::KeyBinding::new(key, NoAction {}, None)
+ }
+
+ impl Render for KeybindingStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ let all_modifier_permutations =
+ ["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
+
+ Story::container(cx)
+ .child(Story::title_for::<KeyBinding>(cx))
+ .child(Story::label(cx, "Single Key"))
+ .child(KeyBinding::new(binding("Z")))
+ .child(Story::label(cx, "Single Key with Modifier"))
+ .child(
+ div()
+ .flex()
+ .gap_3()
+ .child(KeyBinding::new(binding("ctrl-c")))
+ .child(KeyBinding::new(binding("alt-c")))
+ .child(KeyBinding::new(binding("cmd-c")))
+ .child(KeyBinding::new(binding("shift-c"))),
+ )
+ .child(Story::label(cx, "Single Key with Modifier (Permuted)"))
+ .child(
+ div().flex().flex_col().children(
+ all_modifier_permutations
+ .chunks(4)
+ .into_iter()
+ .map(|chunk| {
+ div()
+ .flex()
+ .gap_4()
+ .py_3()
+ .children(chunk.map(|permutation| {
+ KeyBinding::new(binding(&*(permutation.join("-") + "-x")))
+ }))
+ }),
+ ),
+ )
+ .child(Story::label(cx, "Single Key with All Modifiers"))
+ .child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
+ .child(Story::label(cx, "Chord"))
+ .child(KeyBinding::new(binding("a z")))
+ .child(Story::label(cx, "Chord with Modifier"))
+ .child(KeyBinding::new(binding("ctrl-a shift-z")))
+ .child(KeyBinding::new(binding("fn-s")))
+ }
+ }
+}
@@ -0,0 +1,31 @@
+#[cfg(feature = "stories")]
+pub use stories::*;
+
+#[cfg(feature = "stories")]
+mod stories {
+ use super::*;
+ use crate::Story;
+ use gpui::{Div, Render};
+
+ pub struct LabelStory;
+
+ impl Render for LabelStory {
+ type Element = Div;
+
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ Story::container(cx)
+ .child(Story::title_for::<Label>(cx))
+ .child(Story::label(cx, "Default"))
+ .child(Label::new("Hello, world!"))
+ .child(Story::label(cx, "Highlighted"))
+ .child(HighlightedLabel::new(
+ "Hello, world!",
+ vec![0, 1, 2, 7, 8, 12],
+ ))
+ .child(HighlightedLabel::new(
+ "Héllo, world!",
+ vec![0, 1, 3, 8, 9, 13],
+ ))
+ }
+ }
+}
@@ -0,0 +1,8 @@
+mod avatar;
+mod button;
+mod checkbox;
+mod context_menu;
+mod icon;
+mod input;
+mod keybinding;
+mod label;
@@ -1,276 +0,0 @@
-// use crate::prelude::*;
-// use crate::{Icon, IconElement, Label, TextColor};
-// use gpui::{prelude::*, red, Div, ElementId, Render, RenderOnce, View};
-
-// #[derive(RenderOnce, Clone)]
-// pub struct Tab {
-// id: ElementId,
-// title: String,
-// icon: Option<Icon>,
-// current: bool,
-// dirty: bool,
-// fs_status: FileSystemStatus,
-// git_status: GitStatus,
-// diagnostic_status: DiagnosticStatus,
-// close_side: IconSide,
-// }
-
-// #[derive(Clone, Debug)]
-// struct TabDragState {
-// title: String,
-// }
-
-// impl Render for TabDragState {
-// type Element = Div;
-
-// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
-// div().w_8().h_4().bg(red())
-// }
-// }
-
-// impl Component for Tab {
-// type Rendered = gpui::Stateful<Div>;
-
-// fn render(self, cx: &mut WindowContext) -> Self::Rendered {
-// let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
-// let is_deleted = self.fs_status == FileSystemStatus::Deleted;
-
-// let label = match (self.git_status, is_deleted) {
-// (_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone())
-// .color(TextColor::Hidden)
-// .set_strikethrough(true),
-// (GitStatus::None, false) => Label::new(self.title.clone()),
-// (GitStatus::Created, false) => Label::new(self.title.clone()).color(TextColor::Created),
-// (GitStatus::Modified, false) => {
-// Label::new(self.title.clone()).color(TextColor::Modified)
-// }
-// (GitStatus::Renamed, false) => Label::new(self.title.clone()).color(TextColor::Accent),
-// (GitStatus::Conflict, false) => Label::new(self.title.clone()),
-// };
-
-// let close_icon = || IconElement::new(Icon::Close).color(TextColor::Muted);
-
-// let (tab_bg, tab_hover_bg, tab_active_bg) = match self.current {
-// false => (
-// cx.theme().colors().tab_inactive_background,
-// cx.theme().colors().ghost_element_hover,
-// cx.theme().colors().ghost_element_active,
-// ),
-// true => (
-// cx.theme().colors().tab_active_background,
-// cx.theme().colors().element_hover,
-// cx.theme().colors().element_active,
-// ),
-// };
-
-// let drag_state = TabDragState {
-// title: self.title.clone(),
-// };
-
-// div()
-// .id(self.id.clone())
-// .on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone()))
-// .drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().drop_target_background))
-// .on_drop(|_view, state: View<TabDragState>, cx| {
-// eprintln!("{:?}", state.read(cx));
-// })
-// .px_2()
-// .py_0p5()
-// .flex()
-// .items_center()
-// .justify_center()
-// .bg(tab_bg)
-// .hover(|h| h.bg(tab_hover_bg))
-// .active(|a| a.bg(tab_active_bg))
-// .child(
-// div()
-// .px_1()
-// .flex()
-// .items_center()
-// .gap_1p5()
-// .children(has_fs_conflict.then(|| {
-// IconElement::new(Icon::ExclamationTriangle)
-// .size(crate::IconSize::Small)
-// .color(TextColor::Warning)
-// }))
-// .children(self.icon.map(IconElement::new))
-// .children(if self.close_side == IconSide::Left {
-// Some(close_icon())
-// } else {
-// None
-// })
-// .child(label)
-// .children(if self.close_side == IconSide::Right {
-// Some(close_icon())
-// } else {
-// None
-// }),
-// )
-// }
-// }
-
-// impl Tab {
-// pub fn new(id: impl Into<ElementId>) -> Self {
-// Self {
-// id: id.into(),
-// title: "untitled".to_string(),
-// icon: None,
-// current: false,
-// dirty: false,
-// fs_status: FileSystemStatus::None,
-// git_status: GitStatus::None,
-// diagnostic_status: DiagnosticStatus::None,
-// close_side: IconSide::Right,
-// }
-// }
-
-// pub fn current(mut self, current: bool) -> Self {
-// self.current = current;
-// self
-// }
-
-// pub fn title(mut self, title: String) -> Self {
-// self.title = title;
-// self
-// }
-
-// pub fn icon<I>(mut self, icon: I) -> Self
-// where
-// I: Into<Option<Icon>>,
-// {
-// self.icon = icon.into();
-// self
-// }
-
-// pub fn dirty(mut self, dirty: bool) -> Self {
-// self.dirty = dirty;
-// self
-// }
-
-// pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
-// self.fs_status = fs_status;
-// self
-// }
-
-// pub fn git_status(mut self, git_status: GitStatus) -> Self {
-// self.git_status = git_status;
-// self
-// }
-
-// pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
-// self.diagnostic_status = diagnostic_status;
-// self
-// }
-
-// pub fn close_side(mut self, close_side: IconSide) -> Self {
-// self.close_side = close_side;
-// self
-// }
-// }
-
-// #[cfg(feature = "stories")]
-// pub use stories::*;
-
-// #[cfg(feature = "stories")]
-// mod stories {
-// use super::*;
-// use crate::{h_stack, v_stack, Icon, Story};
-// use strum::IntoEnumIterator;
-
-// pub struct TabStory;
-
-// impl Render for TabStory {
-// type Element = Div;
-
-// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
-// let git_statuses = GitStatus::iter();
-// let fs_statuses = FileSystemStatus::iter();
-
-// Story::container(cx)
-// .child(Story::title_for::<_, Tab>(cx))
-// .child(
-// h_stack().child(
-// v_stack()
-// .gap_2()
-// .child(Story::label(cx, "Default"))
-// .child(Tab::new("default")),
-// ),
-// )
-// .child(
-// h_stack().child(
-// v_stack().gap_2().child(Story::label(cx, "Current")).child(
-// h_stack()
-// .gap_4()
-// .child(
-// Tab::new("current")
-// .title("Current".to_string())
-// .current(true),
-// )
-// .child(
-// Tab::new("not_current")
-// .title("Not Current".to_string())
-// .current(false),
-// ),
-// ),
-// ),
-// )
-// .child(
-// h_stack().child(
-// v_stack()
-// .gap_2()
-// .child(Story::label(cx, "Titled"))
-// .child(Tab::new("titled").title("label".to_string())),
-// ),
-// )
-// .child(
-// h_stack().child(
-// v_stack()
-// .gap_2()
-// .child(Story::label(cx, "With Icon"))
-// .child(
-// Tab::new("with_icon")
-// .title("label".to_string())
-// .icon(Some(Icon::Envelope)),
-// ),
-// ),
-// )
-// .child(
-// h_stack().child(
-// v_stack()
-// .gap_2()
-// .child(Story::label(cx, "Close Side"))
-// .child(
-// h_stack()
-// .gap_4()
-// .child(
-// Tab::new("left")
-// .title("Left".to_string())
-// .close_side(IconSide::Left),
-// )
-// .child(Tab::new("right").title("Right".to_string())),
-// ),
-// ),
-// )
-// .child(
-// v_stack()
-// .gap_2()
-// .child(Story::label(cx, "Git Status"))
-// .child(h_stack().gap_4().children(git_statuses.map(|git_status| {
-// Tab::new("git_status")
-// .title(git_status.to_string())
-// .git_status(git_status)
-// }))),
-// )
-// .child(
-// v_stack()
-// .gap_2()
-// .child(Story::label(cx, "File System Status"))
-// .child(h_stack().gap_4().children(fs_statuses.map(|fs_status| {
-// Tab::new("file_system_status")
-// .title(fs_status.to_string())
-// .fs_status(fs_status)
-// }))),
-// )
-// }
-// }
-// }
@@ -1,117 +0,0 @@
-use crate::prelude::*;
-use gpui::{prelude::*, AnyElement, RenderOnce};
-use gpui::{Div, Element};
-use smallvec::SmallVec;
-
-#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
-pub enum ToastOrigin {
- #[default]
- Bottom,
- BottomRight,
-}
-
-/// Don't use toast directly:
-///
-/// - For messages with a required action, use a `NotificationToast`.
-/// - For messages that convey information, use a `StatusToast`.
-///
-/// A toast is a small, temporary window that appears to show a message to the user
-/// or indicate a required action.
-///
-/// Toasts should not persist on the screen for more than a few seconds unless
-/// they are actively showing the a process in progress.
-///
-/// Only one toast may be visible at a time.
-#[derive(RenderOnce)]
-pub struct Toast {
- origin: ToastOrigin,
- children: SmallVec<[AnyElement; 2]>,
-}
-
-impl Component for Toast {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- let mut div = div();
-
- if self.origin == ToastOrigin::Bottom {
- div = div.right_1_2();
- } else {
- div = div.right_2();
- }
-
- div.z_index(5)
- .absolute()
- .bottom_9()
- .flex()
- .py_1()
- .px_1p5()
- .rounded_lg()
- .shadow_md()
- .overflow_hidden()
- .bg(cx.theme().colors().elevated_surface_background)
- .children(self.children)
- }
-}
-
-impl Toast {
- pub fn new(origin: ToastOrigin) -> Self {
- Self {
- origin,
- children: SmallVec::new(),
- }
- }
-
- fn render(self, cx: &mut WindowContext) -> impl Element {
- let mut div = div();
-
- if self.origin == ToastOrigin::Bottom {
- div = div.right_1_2();
- } else {
- div = div.right_2();
- }
-
- div.z_index(5)
- .absolute()
- .bottom_9()
- .flex()
- .py_1()
- .px_1p5()
- .rounded_lg()
- .shadow_md()
- .overflow_hidden()
- .bg(cx.theme().colors().elevated_surface_background)
- .children(self.children)
- }
-}
-
-impl ParentElement for Toast {
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
- &mut self.children
- }
-}
-
-#[cfg(feature = "stories")]
-pub use stories::*;
-
-#[cfg(feature = "stories")]
-mod stories {
- use gpui::{Div, Render};
-
- use crate::{Label, Story};
-
- use super::*;
-
- pub struct ToastStory;
-
- impl Render for ToastStory {
- type Element = Div;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
- Story::container(cx)
- .child(Story::title_for::<Toast>(cx))
- .child(Story::label(cx, "Default"))
- .child(Toast::new(ToastOrigin::Bottom).child(Label::new("label")))
- }
- }
-}
@@ -1,23 +0,0 @@
-use crate::prelude::*;
-use gpui::{Div, RenderOnce};
-
-#[derive(RenderOnce)]
-pub struct ToolDivider;
-
-impl Component for ToolDivider {
- type Rendered = Div;
-
- fn render(self, cx: &mut WindowContext) -> Self::Rendered {
- div().w_px().h_3().bg(cx.theme().colors().border)
- }
-}
-
-impl ToolDivider {
- pub fn new() -> Self {
- Self
- }
-
- fn render(self, cx: &mut WindowContext) -> impl Element {
- div().w_px().h_3().bg(cx.theme().colors().border)
- }
-}
@@ -5,7 +5,6 @@ pub use gpui::{
ViewContext, WindowContext,
};
-pub use crate::elevation::*;
pub use crate::StyledExt;
pub use crate::{ButtonVariant, TextColor};
pub use theme2::ActiveTheme;
@@ -0,0 +1,2 @@
+mod elevation;
+pub use elevation::*;
@@ -1,7 +1,7 @@
use gpui::{hsla, point, px, BoxShadow};
use smallvec::{smallvec, SmallVec};
-#[doc = include_str!("elevation.md")]
+#[doc = include_str!("docs/elevation.md")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Elevation {
ElevationIndex(ElevationIndex),
@@ -15,15 +15,15 @@
#![allow(dead_code, unused_variables)]
mod components;
-mod elevation;
pub mod prelude;
mod styled_ext;
+mod styles;
pub mod utils;
pub use components::*;
pub use prelude::*;
-// pub use static_data::*;
pub use styled_ext::*;
+pub use styles::*;
#[cfg(feature = "stories")]
mod story;