woocraft 0.4.5

GPUI components lib for Woocraft design system.
Documentation
use gpui::{
  Action, AnyElement, AnyView, App, AppContext, Context, IntoElement, ParentElement, Render,
  SharedString, StyleRefinement, Styled, Window, div, prelude::FluentBuilder,
};

use crate::{ActiveTheme, CardStyle, Kbd, Size, StyleSized, StyledExt, h_flex};

type TooltipElementBuilder = Box<dyn Fn(&mut Window, &mut App) -> AnyElement>;

enum TooltipContent {
  Text(SharedString),
  Element(TooltipElementBuilder),
}

pub struct Tooltip {
  style: StyleRefinement,
  content: TooltipContent,
  key_binding: Option<Kbd>,
  action: Option<(Box<dyn Action>, Option<SharedString>)>,
  size: Size,
}

impl Tooltip {
  pub fn new(text: impl Into<SharedString>) -> Self {
    Self {
      style: StyleRefinement::default(),
      content: TooltipContent::Text(text.into()),
      key_binding: None,
      action: None,
      size: Size::Medium,
    }
  }

  pub fn element<E, F>(builder: F) -> Self
  where
    E: IntoElement,
    F: Fn(&mut Window, &mut App) -> E + 'static, {
    Self {
      style: StyleRefinement::default(),
      key_binding: None,
      action: None,
      content: TooltipContent::Element(Box::new(move |window, cx| {
        builder(window, cx).into_any_element()
      })),
      size: Size::Medium,
    }
  }

  pub fn key_binding(mut self, key_binding: Option<Kbd>) -> Self {
    self.key_binding = key_binding;
    self
  }

  pub fn action(mut self, action: &dyn Action, context: Option<&str>) -> Self {
    self.action = Some((action.boxed_clone(), context.map(SharedString::new)));
    self
  }

  pub fn build(self, _: &mut Window, cx: &mut App) -> AnyView {
    cx.new(|_| self).into()
  }
}

impl FluentBuilder for Tooltip {}

impl_sizable!(Tooltip);
impl_styled!(Tooltip);

impl Render for Tooltip {
  fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
    let key_binding = if let Some(key_binding) = &self.key_binding {
      Some(key_binding.clone())
    } else if let Some((action, context)) = &self.action {
      Kbd::binding_for_action(
        action.as_ref(),
        context.as_ref().map(|s| s.as_ref()),
        window,
      )
    } else {
      None
    };

    h_flex()
      .m_3()
      .tooltip_style(cx.theme())
      .justify_between()
      .component_padding(self.size)
      .component_gap(self.size)
      .refine_style(&self.style)
      .map(|this| match self.content {
        TooltipContent::Text(ref text) => this.child(text.clone()),
        TooltipContent::Element(ref builder) => this.child(builder(window, cx)),
      })
      .when_some(key_binding, |this, kbd| {
        this.child(
          div()
            .text_sm()
            .flex_shrink_0()
            .text_color(cx.theme().muted_foreground)
            .child(kbd.outline()),
        )
      })
  }
}