use std::fmt::Display;
use crate::{
classes::{construct_theme, Label, RadioButton as RadioButtonComponent},
parse_color,
theme::Theme,
BORDER_RADIUS,
};
use floem::{
event::EventPropagation,
peniko::Color,
prelude::{SignalGet, SignalUpdate},
taffy::AlignItems,
unit::Pct,
views::{
clip, container, empty, img, label, slider::Slider, svg, v_stack, Checkbox, CheckboxClass,
Clip, Container, Decorators, LabelClass, RadioButton, RadioButtonClass,
},
IntoView,
};
const CHEVRON_DOWN: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>"#;
const CHEVRON_RIGHT: &str = r#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-right-icon lucide-chevron-right"><path d="m9 18 6-6-6-6"/></svg>"#;
pub fn avatar(image: impl Fn() -> Vec<u8> + 'static, size: i32, theme: Theme<'static>) -> Clip {
clip(img(image).style(move |s| s.width(size).height(size))).style(move |s| {
s.border_radius(100)
.width(size)
.height(size)
.border(1)
.border_color(parse_color(theme.base00))
})
}
pub fn checkbox(
checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
theme: Theme<'static>,
) -> impl IntoView {
Checkbox::new_rw(checked).style(move |s| {
s.border_radius(BORDER_RADIUS)
.border_color(parse_color(theme.base03))
.background(parse_color(theme.base00))
.color(parse_color(theme.base01))
.hover(|s| {
s.apply_if(checked.get(), |s| s.background(parse_color(theme.base0a)))
.apply_if(!checked.get(), |s| s.background(parse_color(theme.base00)))
})
.focus(|s| {
s.hover(|s| {
s.apply_if(checked.get(), |s| s.background(parse_color(theme.base0a)))
.apply_if(!checked.get(), |s| s.background(parse_color(theme.base00)))
})
.border_color(parse_color(theme.base0a))
})
.disabled(|s| {
s.color(parse_color(theme.base05).multiply_alpha(0.6))
.background(parse_color(theme.base03).multiply_alpha(0.6))
})
.active(|s| s.background(parse_color(theme.base0a)))
.apply_if(checked.get(), |s| s.background(parse_color(theme.base0a)))
})
}
pub fn labeled_checkbox<S: Display + 'static>(
checked: impl SignalGet<bool> + SignalUpdate<bool> + Copy + 'static,
label: impl Fn() -> S + 'static,
theme: Theme<'static>,
) -> impl IntoView {
Checkbox::labeled_rw(checked, label).style(move |s| {
s.class(CheckboxClass, |s| {
s.border_radius(BORDER_RADIUS)
.border_color(parse_color(theme.base03))
.background(parse_color(theme.base00))
.color(parse_color(theme.base01))
.hover(|s| {
s.apply_if(checked.get(), |s| s.background(parse_color(theme.base0a)))
.apply_if(!checked.get(), |s| s.background(parse_color(theme.base00)))
})
.focus(|s| {
s.hover(|s| {
s.apply_if(checked.get(), |s| s.background(parse_color(theme.base0a)))
.apply_if(!checked.get(), |s| s.background(parse_color(theme.base00)))
})
.border_color(parse_color(theme.base0a))
})
.disabled(|s| {
s.color(parse_color(theme.base05).multiply_alpha(0.6))
.background(parse_color(theme.base03).multiply_alpha(0.6))
})
.active(|s| s.background(parse_color(theme.base0a)))
.apply_if(checked.get(), |s| s.background(parse_color(theme.base0a)))
})
.class(LabelClass, |s| s.color(parse_color(theme.base05)))
.hover(|s| s.background(parse_color(theme.base01)))
.active(|s| s.class(CheckboxClass, |s| s.background(parse_color(theme.base01))))
})
}
pub fn labeled_radio_button<T, S>(
represented_value: T,
actual_value: impl SignalGet<T> + SignalUpdate<T> + Copy + 'static,
label: impl Fn() -> S + 'static,
theme: Theme<'static>,
) -> impl IntoView
where
S: Display + 'static,
T: Eq + PartialEq + Clone + 'static,
{
RadioButton::new_labeled_rw(represented_value, actual_value, label).style(move |_| {
construct_theme(theme)
.apply_class(RadioButtonComponent)
.class(LabelClass, |s| s.color(parse_color(theme.base05)))
.hover(|s| s.background(parse_color(theme.base01)))
.focus(|s| {
s.hover(|s| s.background(parse_color(theme.base01)))
.class(RadioButtonClass, |s| {
s.border_color(parse_color(theme.base0a))
})
})
.active(|s| {
s.class(RadioButtonClass, |s| {
s.background(parse_color(theme.base01))
.border_color(parse_color(theme.base0a))
})
})
})
}
pub fn slider(
percent: impl SignalGet<Pct> + SignalUpdate<Pct> + Copy + 'static,
theme: Theme<'static>,
) -> Slider {
Slider::new_rw(percent)
.slider_style(|s| {
s.bar_color(parse_color(theme.base02))
.accent_bar_color(parse_color(theme.base0a))
.handle_color(Some(parse_color(theme.base03).into()))
.accent_bar_radius(0)
})
.style(|s| {
s.focus_visible(|s| {
s.border(0.5)
.border_color(parse_color(theme.base0a))
.border_radius(100)
})
})
}
pub fn progress_bar(
percent: impl SignalGet<Pct> + SignalUpdate<Pct> + Copy + 'static,
theme: Theme<'static>,
) -> Slider {
Slider::new_rw(percent)
.slider_style(|s| {
s.bar_color(parse_color(theme.base03))
.accent_bar_color(parse_color(theme.base0a))
.handle_color(Some(Color::TRANSPARENT.into()))
})
.disabled(|| true)
}
pub fn separator(theme: Theme<'static>) -> Container {
container(empty()).style(|s| {
s.border(1)
.border_color(parse_color(theme.base03))
.border_radius(BORDER_RADIUS)
})
}
pub fn accordion_item<V, S>(
child: V,
text: impl Fn() -> S + 'static,
id: usize,
active_id: impl SignalGet<usize> + SignalUpdate<usize> + Copy + 'static,
theme: Theme<'static>,
) -> impl IntoView
where
V: IntoView + 'static,
S: Display + 'static,
{
let chevron_svg = svg(CHEVRON_RIGHT)
.style(|s| s.align_self(Some(AlignItems::End)).height(15).width(15))
.class(Label)
.update_value(move || {
if active_id.get() == id {
CHEVRON_DOWN
} else {
CHEVRON_RIGHT
}
});
v_stack((
container((
label(text)
.class(Label)
.style(|s| s.align_self(Some(AlignItems::Start)).font_bold()),
chevron_svg,
)),
child.style(move |s| s.apply_if(active_id.get() != id, |s| s.hide())),
))
.on_click(move |_| {
active_id.set(id);
EventPropagation::Stop
})
.keyboard_navigable()
.style(move |_| {
construct_theme(theme)
.gap(5)
.width_full()
.focus_visible(|s| s.border(1).border_color(parse_color(theme.base0a)))
})
}