use dioxus::prelude::*;
use crate::components::MdxIcon;
use crate::parser::{CardGroupNode, CardNode};
#[derive(Props, Clone, PartialEq)]
pub struct DocCardGroupProps {
pub group: CardGroupNode,
#[props(optional)]
pub on_link: Option<EventHandler<String>>,
#[props(default = "/docs".to_string())]
pub doc_base_path: String,
}
#[component]
pub fn DocCardGroup(props: DocCardGroupProps) -> Element {
let grid_class = match props.group.cols {
1 => "grid grid-cols-1 gap-4",
2 => "grid grid-cols-1 md:grid-cols-2 gap-4",
3 => "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",
_ => "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4",
};
rsx! {
div { class: "my-6 {grid_class}",
for (i, card) in props.group.cards.iter().enumerate() {
DocCard {
key: "{i}",
card: card.clone(),
on_link: props.on_link,
doc_base_path: props.doc_base_path.clone(),
}
}
}
}
}
#[derive(Props, Clone, PartialEq)]
pub struct DocCardProps {
pub card: CardNode,
#[props(optional)]
pub on_link: Option<EventHandler<String>>,
#[props(default = "/docs".to_string())]
pub doc_base_path: String,
}
#[component]
pub fn DocCard(props: DocCardProps) -> Element {
let html = if !props.card.content.is_empty() {
markdown::to_html_with_options(&props.card.content, &markdown::Options::gfm())
.unwrap_or_else(|_| props.card.content.clone())
} else {
String::new()
};
let card_content = rsx! {
div { class: "bg-base-300 hover:border-primary/50 transition-colors duration-150 border border-base-content/10 rounded-lg h-full",
div { class: "p-6",
if let Some(icon) = &props.card.icon {
div { class: "text-primary mb-5",
MdxIcon { name: icon.clone(), class: "size-6".to_string() }
}
}
h3 { class: "font-semibold text-base-content mb-2 no-underline",
"{props.card.title}"
}
if !html.is_empty() {
div {
class: "text-sm text-base-content/60 leading-relaxed [&>p]:my-0 [&_a]:no-underline [&_a]:text-base-content/60",
dangerous_inner_html: html,
}
}
}
}
};
if let Some(href) = &props.card.href {
if href.starts_with("http://") || href.starts_with("https://") {
rsx! {
a {
href: "{href}",
target: "_blank",
rel: "noopener noreferrer",
class: "block no-underline hover:no-underline not-prose",
{card_content}
}
}
} else {
let internal_href = convert_doc_href(href, &props.doc_base_path);
if let Some(on_link) = &props.on_link {
let href_for_click = internal_href.clone();
let on_link = *on_link;
rsx! {
button {
class: "block no-underline text-left w-full not-prose",
onclick: move |_| on_link.call(href_for_click.clone()),
{card_content}
}
}
} else {
rsx! {
a {
href: "{internal_href}",
class: "block no-underline hover:no-underline not-prose",
{card_content}
}
}
}
}
} else {
card_content
}
}
fn convert_doc_href(href: &str, base_path: &str) -> String {
if href.starts_with(base_path) {
return href.to_string();
}
let path = href.trim_start_matches('/');
format!("{}/{}", base_path, path)
}