use leptos::callback::Callback;
use leptos::prelude::*;
use orbital_macros::component_doc;
use crate::context::{ChartKind, HeatmapPlotContext, HeatmapPlotProvider};
use crate::engine::default_continuous_scale;
use crate::shared::{ChartContainer, ContinuousColorLegend, HeatmapPlot};
use crate::{
AxisClickData, AxisDef, AxisHighlightConfig, AxisPosition, ColorScale, FadeMode, HeatmapCell,
HighlightMode, HighlightScope, LegendConfig, PlotInset, ScaleType, TooltipConfig,
};
#[component_doc(
category = "Charts",
preview_slug = "heatmap",
preview_label = "Heatmap",
preview_icon = icondata::AiHeatMapOutlined,
)]
#[component]
pub fn Heatmap(
x_categories: Vec<String>,
y_categories: Vec<String>,
cells: Vec<HeatmapCell>,
#[prop(optional)]
color_scale: Option<ColorScale>,
#[prop(optional)]
value_min: Option<f64>,
#[prop(optional)]
value_max: Option<f64>,
#[prop(default = 520.0)]
width: f64,
#[prop(default = 320.0)]
height: f64,
#[prop(optional)]
margin: Option<PlotInset>,
#[prop(optional)]
loading: Option<bool>,
#[prop(optional)]
highlight_scope: Option<HighlightScope>,
#[prop(optional)]
axis_highlight: Option<AxisHighlightConfig>,
#[prop(optional)]
legend: Option<LegendConfig>,
#[prop(optional)]
tooltip: Option<TooltipConfig>,
#[prop(optional)]
on_axis_click: Option<Callback<(AxisClickData,), ()>>,
#[prop(optional, into)]
class: MaybeProp<String>,
) -> impl IntoView {
let scale = color_scale.unwrap_or_else(default_continuous_scale);
let renderer = if cells.len() > crate::engine::HEATMAP_CANVAS_THRESHOLD {
"canvas"
} else {
"svg"
};
let x_axis = vec![AxisDef {
id: "x".into(),
scale_type: ScaleType::Band,
data: Some(x_categories),
position: AxisPosition::Bottom,
category_gap_ratio: Some(0.08),
..Default::default()
}];
let y_axis = vec![AxisDef {
id: "y".into(),
scale_type: ScaleType::Band,
data: Some(y_categories),
position: AxisPosition::Left,
category_gap_ratio: Some(0.08),
..Default::default()
}];
let plot_context = HeatmapPlotContext {
cells,
color_scale: scale,
value_min,
value_max,
};
let inset = margin.unwrap_or_else(PlotInset::with_axes);
let highlight_scope = highlight_scope.or({
Some(HighlightScope {
highlight: HighlightMode::Item,
fade: FadeMode::Global,
})
});
let tooltip = tooltip.or_else(|| Some(TooltipConfig::item()));
let shell_style = format!("width: {width}px; height: {height}px;");
view! {
<div data-orbital-heatmap-renderer=renderer class="orb-heatmap-root">
<HeatmapPlotProvider context=plot_context>
<div class="orb-heatmap-shell" style=shell_style>
<ChartContainer
class=class
x_axis=Some(x_axis)
y_axis=Some(y_axis)
width=Some(width)
height=Some(height)
margin=Some(inset)
loading=loading
chart_kind=ChartKind::Heatmap
highlight_scope=highlight_scope
axis_highlight=axis_highlight
tooltip=tooltip
on_axis_click=on_axis_click
>
<HeatmapPlot />
</ChartContainer>
{legend
.filter(|c| !c.hidden)
.map(|config| view! { <ContinuousColorLegend config=config /> })}
</div>
</HeatmapPlotProvider>
</div>
}
}