Work on sample `collab_panel` story in the storybook

Nate Butler and Nathan Sobo created

Co-Authored-By: Nathan Sobo <1789+nathansobo@users.noreply.github.com>

Change summary

crates/gpui2/src/element.rs                  | 31 ++++++++
crates/gpui2/src/style.rs                    |  4 
crates/gpui2_macros/src/styleable_helpers.rs | 10 ++
crates/storybook/src/collab_panel.rs         | 82 ++++++++++++++++++++-
4 files changed, 117 insertions(+), 10 deletions(-)

Detailed changes

crates/gpui2/src/element.rs 🔗

@@ -62,6 +62,21 @@ enum ElementPhase<V: 'static, E: Element<V>> {
     Error(String),
 }
 
+impl<V: 'static, E: Element<V>> std::fmt::Debug for ElementPhase<V, E> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ElementPhase::Init => write!(f, "Init"),
+            ElementPhase::PostLayout { layout_id, .. } => {
+                write!(f, "PostLayout with layout id: {:?}", layout_id)
+            }
+            ElementPhase::PostPaint { layout, .. } => {
+                write!(f, "PostPaint with layout: {:?}", layout)
+            }
+            ElementPhase::Error(err) => write!(f, "Error: {}", err),
+        }
+    }
+}
+
 impl<V: 'static, E: Element<V>> Default for ElementPhase<V, E> {
     fn default() -> Self {
         Self::Init
@@ -105,8 +120,22 @@ impl<V, E: Element<V>> AnyStatefulElement<V> for StatefulElement<V, E> {
                 }
                 Err(error) => ElementPhase::Error(error.to_string()),
             },
+            ElementPhase::PostPaint {
+                mut layout,
+                mut paint_state,
+            } => {
+                layout.bounds = layout.bounds + parent_origin;
+                self.element.paint(view, &layout, &mut paint_state, cx);
+                ElementPhase::PostPaint {
+                    layout,
+                    paint_state,
+                }
+            }
             phase @ ElementPhase::Error(_) => phase,
-            _ => panic!("invalid element phase to call paint"),
+
+            phase @ _ => {
+                panic!("invalid element phase to call paint: {:?}", phase);
+            }
         };
     }
 }

crates/gpui2/src/style.rs 🔗

@@ -268,14 +268,14 @@ pub trait Styleable {
         Self::Style::from_refinement(&self.style_cascade().merged())
     }
 
-    fn hovered(self) -> Hoverable<Self>
+    fn hover(self) -> Hoverable<Self>
     where
         Self: Sized,
     {
         hoverable(self)
     }
 
-    fn pressed(self) -> Pressable<Self>
+    fn active(self) -> Pressable<Self>
     where
         Self: Sized,
     {

crates/gpui2_macros/src/styleable_helpers.rs 🔗

@@ -68,8 +68,11 @@ fn tailwind_lengths() -> Vec<(&'static str, TokenStream2)> {
         ("4", quote! { rems(1.) }),
         ("5", quote! { rems(1.25) }),
         ("6", quote! { rems(1.5) }),
+        ("7", quote! { rems(1.75) }),
         ("8", quote! { rems(2.0) }),
+        ("9", quote! { rems(2.25) }),
         ("10", quote! { rems(2.5) }),
+        ("11", quote! { rems(2.75) }),
         ("12", quote! { rems(3.) }),
         ("16", quote! { rems(4.) }),
         ("20", quote! { rems(5.) }),
@@ -149,5 +152,12 @@ fn tailwind_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>)> {
         ("bottom", true, vec![quote! { inset.bottom }]),
         ("left", true, vec![quote! { inset.left }]),
         ("right", true, vec![quote! { inset.right }]),
+        (
+            "gap",
+            false,
+            vec![quote! { gap.width }, quote! { gap.height }],
+        ),
+        ("gap_x", false, vec![quote! { gap.width }]),
+        ("gap_y", false, vec![quote! { gap.height }]),
     ]
 }

crates/storybook/src/collab_panel.rs 🔗

@@ -1,5 +1,9 @@
 use crate::theme::theme;
-use gpui2::{elements::div, style::StyleHelpers, Element, IntoElement, ParentElement, ViewContext};
+use gpui2::{
+    elements::div,
+    style::{StyleHelpers, Styleable},
+    Element, IntoElement, ParentElement, ViewContext,
+};
 use std::marker::PhantomData;
 
 #[derive(Element)]
@@ -13,23 +17,87 @@ pub fn collab_panel<V: 'static>() -> CollabPanelElement<V> {
     }
 }
 
+// fn list_item<V: 'static>(cx: &mut ViewContext<V>) -> impl IntoElement<V> {
+//     let theme = theme(cx);
+//     div()
+//         .px_2()
+//         .flex()
+//         .justify_between()
+//         //:: States - https://tailwindcss.com/docs/hover-focus-and-other-states#hover-focus-and-active
+//         .hover()
+//         .fill(theme.middle.variant.hovered.background)
+//         .active()
+//         .fill(theme.middle.variant.pressed.background)
+//         .child(div().flex().gap_1().child("#").child("Collab Panel"))
+//         .child(div().flex().gap_1().child("v"))
+// }
+
+// Macros to impl:
+// - border (b, t, l, r, x, y)
+// - border_[foo]_[size] (border_b_2, border_t_4, etc)
+// - border_color
+// - items_[center, start, end]
+
 impl<V: 'static> CollabPanelElement<V> {
     fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
         let theme = theme(cx);
 
+        // Panel
         div()
             .full()
             .font("Zed Sans")
             .text_color(theme.middle.variant.default.foreground)
+            // .border_color(theme.middle.base.default.border)
             .fill(theme.middle.base.default.background)
-            .py_2()
+            // List Container
             .child(
                 div()
-                    .px_2()
-                    .flex()
-                    .justify_between()
-                    .child("#CRDB")
-                    .child("V"),
+                    .full()
+                    .fill(theme.middle.variant.default.background)
+                    .py_2()
+                    // .border_b()
+                    //:: https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state
+                    // .group()
+                    // List Section Header
+                    .child(
+                        div()
+                            .px_2()
+                            .flex()
+                            .justify_between()
+                            //:: States - https://tailwindcss.com/docs/hover-focus-and-other-states#hover-focus-and-active
+                            .hover()
+                            .fill(theme.middle.variant.hovered.background)
+                            // .focus().fill(theme.middle.variant.active.background)
+                            .active()
+                            .fill(theme.middle.variant.pressed.background)
+                            .child(
+                                div()
+                                    .flex()
+                                    .gap_1()
+                                    //:: State based on group interaction state
+                                    // .group_hover().text_color(theme.middle.variant.hovered.foreground)
+                                    .child("#")
+                                    .child("Collab Panel"),
+                            )
+                            .child(div().flex().gap_1().child("v")),
+                    )
+                    // List Item Large
+                    .child(
+                        div()
+                            .px_2()
+                            .h_7()
+                            .flex()
+                            .justify_between()
+                            // .items_center()
+                            .child(
+                                div()
+                                    .flex()
+                                    .gap_1()
+                                    .child(div().w_4().h_4().child("img"))
+                                    .child("maxbrunsfeld"),
+                            )
+                            .child(div().flex().gap_2().child("icon")),
+                    ),
             )
     }
 }