ui/components/
hover_card.rs1use std::rc::Rc;
2
3use gpui::{Context, Render, StatefulInteractiveElement};
4
5use crate::{Popover, prelude::*};
6
7struct HoverCardTooltip {
8 content_builder: Rc<dyn Fn(&mut Window, &mut App) -> AnyElement + 'static>,
9}
10
11impl Render for HoverCardTooltip {
12 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
13 let content = (self.content_builder)(window, cx);
14 Popover::new().child(div().max_w(px(320.)).p_3().child(content))
15 }
16}
17
18#[derive(IntoElement, RegisterComponent)]
24pub struct HoverCard {
25 trigger: AnyElement,
26 content_builder: Rc<dyn Fn(&mut Window, &mut App) -> AnyElement + 'static>,
27}
28
29impl HoverCard {
30 pub fn new(trigger: impl IntoElement) -> Self {
31 Self {
32 trigger: trigger.into_any_element(),
33 content_builder: Rc::new(|_, _| div().into_any_element()),
34 }
35 }
36
37 pub fn content(
38 mut self,
39 content: impl Fn(&mut Window, &mut App) -> AnyElement + 'static,
40 ) -> Self {
41 self.content_builder = Rc::new(content);
42 self
43 }
44}
45
46impl RenderOnce for HoverCard {
47 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
48 let content_builder = self.content_builder.clone();
49 div()
50 .id("hover-card")
51 .child(self.trigger)
52 .hoverable_tooltip(move |_, cx| {
53 cx.new(|_| HoverCardTooltip {
54 content_builder: content_builder.clone(),
55 })
56 .into()
57 })
58 }
59}
60
61impl Component for HoverCard {
62 fn scope() -> ComponentScope {
63 ComponentScope::Overlays
64 }
65
66 fn description() -> Option<&'static str> {
67 Some("Rich hover content with delay, composing tooltip timing and Popover styling.")
68 }
69
70 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
71 Some(
72 HoverCard::new(
73 Button::new("hover-card-trigger", "@nextjs").style(ButtonStyle::Transparent),
74 )
75 .content(|_, _| {
76 v_flex()
77 .gap_2()
78 .child(Label::new("Next.js").size(LabelSize::Small))
79 .child(
80 Label::new("The React Framework for the Web — built by Vercel.")
81 .size(LabelSize::XSmall)
82 .color(Color::Muted),
83 )
84 .child(
85 h_flex()
86 .gap_3()
87 .child(Label::new("Joined December 2021").size(LabelSize::XSmall))
88 .child(Label::new("1.2k followers").size(LabelSize::XSmall)),
89 )
90 .into_any_element()
91 })
92 .into_any_element(),
93 )
94 }
95}