Extract UI elements from `storybook` into new `ui` crate (#3008)

Marshall Bowers created

This PR extracts the various UI elements from the `storybook` crate into
a new `ui` library crate.

Release Notes:

- N/A

Change summary

Cargo.lock                               | 12 ++++++++++++
Cargo.toml                               |  1 +
crates/storybook/Cargo.toml              |  1 +
crates/storybook/src/collab_panel.rs     |  2 +-
crates/storybook/src/storybook.rs        |  8 +-------
crates/storybook/src/ui.rs               |  7 -------
crates/storybook/src/ui/component.rs     |  9 ---------
crates/storybook/src/workspace.rs        |  5 +----
crates/ui/Cargo.toml                     | 12 ++++++++++++
crates/ui/src/components.rs              | 23 ++++++++++++++++++-----
crates/ui/src/components/facepile.rs     |  7 ++++---
crates/ui/src/components/follow_group.rs |  8 ++++----
crates/ui/src/components/list_item.rs    | 10 +++++-----
crates/ui/src/components/tab.rs          |  7 ++++---
crates/ui/src/element_ext.rs             |  6 ++++--
crates/ui/src/elements.rs                |  0 
crates/ui/src/elements/avatar.rs         |  8 ++++----
crates/ui/src/elements/details.rs        |  6 +++---
crates/ui/src/elements/icon.rs           |  6 +++---
crates/ui/src/elements/icon_button.rs    | 10 +++++-----
crates/ui/src/elements/indicator.rs      |  7 ++++---
crates/ui/src/elements/input.rs          |  9 +++++----
crates/ui/src/elements/label.rs          |  6 +++---
crates/ui/src/elements/text_button.rs    |  9 +++++----
crates/ui/src/elements/tool_divider.rs   |  7 ++++---
crates/ui/src/lib.rs                     | 14 ++++++++++++++
crates/ui/src/modules.rs                 |  0 
crates/ui/src/modules/chat_panel.rs      |  8 ++++----
crates/ui/src/modules/project_panel.rs   | 19 ++++++++-----------
crates/ui/src/modules/status_bar.rs      |  9 +++++----
crates/ui/src/modules/tab_bar.rs         | 10 +++++-----
crates/ui/src/modules/title_bar.rs       | 10 +++++-----
crates/ui/src/prelude.rs                 |  0 
crates/ui/src/theme.rs                   | 15 +++++++++------
crates/ui/tracker.md                     |  0 
35 files changed, 154 insertions(+), 117 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -7397,6 +7397,7 @@ dependencies = [
  "settings",
  "simplelog",
  "theme",
+ "ui",
  "util",
 ]
 
@@ -8599,6 +8600,17 @@ version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
 
+[[package]]
+name = "ui"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "gpui2",
+ "serde",
+ "settings",
+ "theme",
+]
+
 [[package]]
 name = "unicase"
 version = "2.7.0"

Cargo.toml 🔗

@@ -69,6 +69,7 @@ members = [
     "crates/text",
     "crates/theme",
     "crates/theme_selector",
+    "crates/ui",
     "crates/util",
     "crates/semantic_index",
     "crates/vim",

crates/storybook/Cargo.toml 🔗

@@ -17,6 +17,7 @@ serde.workspace = true
 settings = { path = "../settings" }
 simplelog = "0.9"
 theme = { path = "../theme" }
+ui = { path = "../ui" }
 util = { path = "../util" }
 
 [dev-dependencies]

crates/storybook/src/collab_panel.rs 🔗

@@ -1,10 +1,10 @@
-use crate::theme::{theme, Theme};
 use gpui2::{
     elements::{div, div::ScrollState, img, svg},
     style::{StyleHelpers, Styleable},
     ArcCow, Element, IntoElement, ParentElement, ViewContext,
 };
 use std::marker::PhantomData;
+use ui::{theme, Theme};
 
 #[derive(Element)]
 pub struct CollabPanelElement<V: 'static> {

crates/storybook/src/storybook.rs 🔗

@@ -1,20 +1,14 @@
 #![allow(dead_code, unused_variables)]
 
-use crate::theme::Theme;
 use ::theme as legacy_theme;
-use element_ext::ElementExt;
 use gpui2::{serde_json, vec2f, view, Element, RectF, ViewContext, WindowBounds};
 use legacy_theme::ThemeSettings;
 use log::LevelFilter;
 use settings::{default_settings, SettingsStore};
 use simplelog::SimpleLogger;
+use ui::{ElementExt, Theme};
 
 mod collab_panel;
-mod components;
-mod element_ext;
-mod prelude;
-mod theme;
-mod ui;
 mod workspace;
 
 gpui2::actions! {

crates/storybook/src/ui.rs 🔗

@@ -1,7 +0,0 @@
-mod component;
-mod element;
-mod module;
-
-pub use component::*;
-pub use element::*;
-pub use module::*;

crates/storybook/src/ui/component.rs 🔗

@@ -1,9 +0,0 @@
-mod facepile;
-mod follow_group;
-mod list_item;
-mod tab;
-
-pub use facepile::*;
-pub use follow_group::*;
-pub use list_item::*;
-pub use tab::*;

crates/storybook/src/workspace.rs 🔗

@@ -1,12 +1,9 @@
-use crate::{
-    theme::theme,
-    ui::{chat_panel, project_panel, status_bar, tab_bar, title_bar},
-};
 use gpui2::{
     elements::{div, div::ScrollState},
     style::StyleHelpers,
     Element, IntoElement, ParentElement, ViewContext,
 };
+use ui::{chat_panel, project_panel, status_bar, tab_bar, theme, title_bar};
 
 #[derive(Element, Default)]
 struct WorkspaceElement {

crates/ui/Cargo.toml 🔗

@@ -0,0 +1,12 @@
+[package]
+name = "ui"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[dependencies]
+anyhow.workspace = true
+gpui2 = { path = "../gpui2" }
+serde.workspace = true
+settings = { path = "../settings" }
+theme = { path = "../theme" }

crates/storybook/src/components.rs → crates/ui/src/components.rs 🔗

@@ -1,8 +1,21 @@
-use gpui2::{
-    elements::div, interactive::Interactive, platform::MouseButton, style::StyleHelpers, ArcCow,
-    Element, EventContext, IntoElement, ParentElement, ViewContext,
-};
-use std::{marker::PhantomData, rc::Rc};
+mod facepile;
+mod follow_group;
+mod list_item;
+mod tab;
+
+pub use facepile::*;
+pub use follow_group::*;
+pub use list_item::*;
+pub use tab::*;
+
+use std::marker::PhantomData;
+use std::rc::Rc;
+
+use gpui2::elements::div;
+use gpui2::interactive::Interactive;
+use gpui2::platform::MouseButton;
+use gpui2::style::StyleHelpers;
+use gpui2::{ArcCow, Element, EventContext, IntoElement, ParentElement, ViewContext};
 
 struct ButtonHandlers<V, D> {
     click: Option<Rc<dyn Fn(&mut V, &D, &mut EventContext<V>)>>,

crates/storybook/src/ui/component/facepile.rs → crates/ui/src/components/facepile.rs 🔗

@@ -1,7 +1,8 @@
-use crate::{theme::theme, ui::Avatar};
+use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::{theme, Avatar};
 
 #[derive(Element)]
 pub struct Facepile {

crates/storybook/src/ui/component/follow_group.rs → crates/ui/src/components/follow_group.rs 🔗

@@ -1,8 +1,8 @@
-use crate::theme::theme;
-use crate::ui::{facepile, indicator, Avatar};
+use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::{facepile, indicator, theme, Avatar};
 
 #[derive(Element)]
 pub struct FollowGroup {

crates/storybook/src/ui/component/list_item.rs → crates/ui/src/components/list_item.rs 🔗

@@ -1,10 +1,10 @@
-use crate::prelude::{InteractionState, ToggleState};
-use crate::theme::theme;
-use crate::ui::{icon, IconAsset, Label};
+use gpui2::elements::div;
 use gpui2::geometry::rems;
 use gpui2::style::{StyleHelpers, Styleable};
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::prelude::*;
+use crate::{icon, theme, IconAsset, Label};
 
 #[derive(Element)]
 pub struct ListItem {

crates/storybook/src/ui/component/tab.rs → crates/ui/src/components/tab.rs 🔗

@@ -1,7 +1,8 @@
-use crate::theme::theme;
+use gpui2::elements::div;
 use gpui2::style::{StyleHelpers, Styleable};
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::theme;
 
 #[derive(Element)]
 pub struct Tab {

crates/storybook/src/element_ext.rs → crates/ui/src/element_ext.rs 🔗

@@ -1,7 +1,9 @@
-use crate::theme::{Theme, Themed};
-use gpui2::Element;
 use std::marker::PhantomData;
 
+use gpui2::Element;
+
+use crate::theme::{Theme, Themed};
+
 pub trait ElementExt<V: 'static>: Element<V> {
     fn themed(self, theme: Theme) -> Themed<V, Self>
     where

crates/storybook/src/ui/element/avatar.rs → crates/ui/src/elements/avatar.rs 🔗

@@ -1,9 +1,9 @@
-use crate::prelude::Shape;
-use crate::theme::theme;
 use gpui2::elements::img;
 use gpui2::style::StyleHelpers;
-use gpui2::{ArcCow, IntoElement};
-use gpui2::{Element, ViewContext};
+use gpui2::{ArcCow, Element, IntoElement, ViewContext};
+
+use crate::prelude::*;
+use crate::theme;
 
 #[derive(Element, Clone)]
 pub struct Avatar {

crates/storybook/src/ui/element/details.rs → crates/ui/src/elements/details.rs 🔗

@@ -1,8 +1,8 @@
-use crate::theme::theme;
 use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{Element, ViewContext};
-use gpui2::{IntoElement, ParentElement};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::theme;
 
 #[derive(Element, Clone)]
 pub struct Details {

crates/storybook/src/ui/element/icon.rs → crates/ui/src/elements/icon.rs 🔗

@@ -1,8 +1,8 @@
-use crate::theme::theme;
 use gpui2::elements::svg;
 use gpui2::style::StyleHelpers;
-use gpui2::IntoElement;
-use gpui2::{Element, ViewContext};
+use gpui2::{Element, IntoElement, ViewContext};
+
+use crate::theme;
 
 // Icon::Hash
 // icon(IconAsset::Hash).color(IconColor::Warning)

crates/storybook/src/ui/element/icon_button.rs → crates/ui/src/elements/icon_button.rs 🔗

@@ -1,9 +1,9 @@
-use crate::prelude::{ButtonVariant, InteractionState};
-use crate::theme::theme;
-use gpui2::elements::svg;
+use gpui2::elements::{div, svg};
 use gpui2::style::{StyleHelpers, Styleable};
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::prelude::*;
+use crate::theme;
 
 #[derive(Element)]
 pub struct IconButton {

crates/storybook/src/ui/element/indicator.rs → crates/ui/src/elements/indicator.rs 🔗

@@ -1,7 +1,8 @@
-use crate::theme::theme;
+use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ViewContext};
+use gpui2::{Element, IntoElement, ViewContext};
+
+use crate::theme;
 
 #[derive(Element)]
 pub struct Indicator {

crates/storybook/src/ui/element/input.rs → crates/ui/src/elements/input.rs 🔗

@@ -1,8 +1,9 @@
-use crate::prelude::{InputVariant, InteractionState};
-use crate::theme::theme;
+use gpui2::elements::div;
 use gpui2::style::{StyleHelpers, Styleable};
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::prelude::*;
+use crate::theme;
 
 #[derive(Element)]
 pub struct Input {

crates/storybook/src/ui/element/label.rs → crates/ui/src/elements/label.rs 🔗

@@ -1,8 +1,8 @@
-use crate::theme::theme;
 use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{Element, ViewContext};
-use gpui2::{IntoElement, ParentElement};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::theme;
 
 #[derive(Default, PartialEq, Copy, Clone)]
 pub enum LabelColor {

crates/storybook/src/ui/element/text_button.rs → crates/ui/src/elements/text_button.rs 🔗

@@ -1,8 +1,9 @@
-use crate::prelude::{ButtonVariant, InteractionState};
-use crate::theme::theme;
+use gpui2::elements::div;
 use gpui2::style::{StyleHelpers, Styleable};
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::prelude::*;
+use crate::theme;
 
 #[derive(Element)]
 pub struct TextButton {

crates/storybook/src/ui/element/tool_divider.rs → crates/ui/src/elements/tool_divider.rs 🔗

@@ -1,7 +1,8 @@
-use crate::theme::theme;
+use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ViewContext};
+use gpui2::{Element, IntoElement, ViewContext};
+
+use crate::theme;
 
 #[derive(Element)]
 pub struct ToolDivider {}

crates/ui/src/lib.rs 🔗

@@ -0,0 +1,14 @@
+#![allow(dead_code, unused_variables)]
+
+mod components;
+mod element_ext;
+mod elements;
+mod modules;
+pub mod prelude;
+mod theme;
+
+pub use components::*;
+pub use element_ext::*;
+pub use elements::*;
+pub use modules::*;
+pub use theme::*;

crates/storybook/src/ui/module/chat_panel.rs → crates/ui/src/modules/chat_panel.rs 🔗

@@ -1,11 +1,11 @@
 use std::marker::PhantomData;
 
-use crate::theme::theme;
-use crate::ui::icon_button;
+use gpui2::elements::div;
 use gpui2::elements::div::ScrollState;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::{icon_button, theme};
 
 #[derive(Element)]
 pub struct ChatPanel<V: 'static> {

crates/storybook/src/ui/module/project_panel.rs → crates/ui/src/modules/project_panel.rs 🔗

@@ -1,16 +1,13 @@
-use crate::{
-    prelude::{InteractionState, ToggleState},
-    theme::theme,
-    ui::{details, input, label, list_item, IconAsset, LabelColor},
-};
-use gpui2::{
-    elements::{div, div::ScrollState},
-    style::StyleHelpers,
-    ParentElement, ViewContext,
-};
-use gpui2::{Element, IntoElement};
 use std::marker::PhantomData;
 
+use gpui2::elements::div;
+use gpui2::elements::div::ScrollState;
+use gpui2::style::StyleHelpers;
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::prelude::*;
+use crate::{details, input, label, list_item, theme, IconAsset, LabelColor};
+
 #[derive(Element)]
 pub struct ProjectPanel<V: 'static> {
     view_type: PhantomData<V>,

crates/storybook/src/ui/module/status_bar.rs → crates/ui/src/modules/status_bar.rs 🔗

@@ -1,10 +1,11 @@
 use std::marker::PhantomData;
 
-use crate::theme::{theme, Theme};
-use crate::ui::{icon_button, text_button, tool_divider};
+use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::theme::{theme, Theme};
+use crate::{icon_button, text_button, tool_divider};
 
 #[derive(Default, PartialEq)]
 pub enum Tool {

crates/storybook/src/ui/module/tab_bar.rs → crates/ui/src/modules/tab_bar.rs 🔗

@@ -1,12 +1,12 @@
 use std::marker::PhantomData;
 
-use crate::prelude::InteractionState;
-use crate::theme::theme;
-use crate::ui::{icon_button, tab};
+use gpui2::elements::div;
 use gpui2::elements::div::ScrollState;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::prelude::InteractionState;
+use crate::{icon_button, tab, theme};
 
 #[derive(Element)]
 pub struct TabBar<V: 'static> {

crates/storybook/src/ui/module/title_bar.rs → crates/ui/src/modules/title_bar.rs 🔗

@@ -1,11 +1,11 @@
 use std::marker::PhantomData;
 
-use crate::prelude::Shape;
-use crate::theme::theme;
-use crate::ui::{avatar, follow_group, icon_button, text_button, tool_divider};
+use gpui2::elements::div;
 use gpui2::style::StyleHelpers;
-use gpui2::{elements::div, IntoElement};
-use gpui2::{Element, ParentElement, ViewContext};
+use gpui2::{Element, IntoElement, ParentElement, ViewContext};
+
+use crate::prelude::Shape;
+use crate::{avatar, follow_group, icon_button, text_button, theme, tool_divider};
 
 #[derive(Element)]
 pub struct TitleBar<V: 'static> {

crates/storybook/src/theme.rs → crates/ui/src/theme.rs 🔗

@@ -1,9 +1,12 @@
-use gpui2::{
-    color::Hsla, element::Element, serde_json, AppContext, IntoElement, Vector2F, ViewContext,
-    WindowContext,
-};
-use serde::{de::Visitor, Deserialize, Deserializer};
-use std::{collections::HashMap, fmt, marker::PhantomData};
+use std::collections::HashMap;
+use std::fmt;
+use std::marker::PhantomData;
+
+use gpui2::color::Hsla;
+use gpui2::element::Element;
+use gpui2::{serde_json, AppContext, IntoElement, Vector2F, ViewContext, WindowContext};
+use serde::de::Visitor;
+use serde::{Deserialize, Deserializer};
 use theme::ThemeSettings;
 
 #[derive(Deserialize, Clone, Default, Debug)]