Start on popover

Nate Butler created

Change summary

crates/ui2/src/components.rs         |  2 +
crates/ui2/src/components/popover.rs | 52 ++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+)

Detailed changes

crates/ui2/src/components.rs 🔗

@@ -10,6 +10,7 @@ mod input;
 mod keybinding;
 mod label;
 mod list;
+mod popover;
 mod slot;
 mod stack;
 mod stories;
@@ -28,6 +29,7 @@ pub use input::*;
 pub use keybinding::*;
 pub use label::*;
 pub use list::*;
+pub use popover::*;
 pub use slot::*;
 pub use stack::*;
 pub use stories::*;

crates/ui2/src/components/popover.rs 🔗

@@ -0,0 +1,52 @@
+use gpui::{
+    AnyElement, Component, Div, ElementId, ParentElement, RenderOnce, Styled, WindowContext,
+};
+use smallvec::SmallVec;
+
+use crate::{v_stack, StyledExt};
+
+#[derive(RenderOnce)]
+pub struct Popover {
+    children: SmallVec<[AnyElement; 2]>,
+    aside: Option<SmallVec<[AnyElement; 2]>>,
+}
+
+impl Component for Popover {
+    type Rendered = Div;
+
+    fn render(self, cx: &mut WindowContext) -> Self::Rendered {
+        v_stack()
+            .relative()
+            .elevation_2(cx)
+            .p_1()
+            .children(self.children)
+            .when_some(self.aside, |this, aside| {
+                // TODO: This will statically position the aside to the top right of the popover.
+                // We should update this to avoid collisions with the window edges.
+                this.child(
+                    v_stack()
+                        .top_0()
+                        .neg_right_1()
+                        .absolute()
+                        .elevation_2(cx)
+                        .p_1()
+                        .children(aside),
+                )
+            })
+    }
+}
+
+impl Popover {
+    pub fn new() -> Self {
+        Self {
+            children: SmallVec::new(),
+            aside: None,
+        }
+    }
+}
+
+impl ParentElement for Popover {
+    fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
+        &mut self.children
+    }
+}