use crate::{prelude::*, *};
use std::sync::Arc;
#[derive(IntoElement)]
pub struct Rate {
instance: SharedString,
count: usize,
value: f32,
allow_half: bool,
disabled: bool,
on_change: Option<Arc<dyn Fn(f32, &mut Window, &mut App) + Send + Sync + 'static>>,
style: StyleRefinement,
}
impl Rate {
#[track_caller]
pub fn new() -> Self {
Self {
instance: crate::caller_element_id("rate"),
count: 5,
value: 0.0,
allow_half: false,
disabled: false,
on_change: None,
style: StyleRefinement::default(),
}
}
pub fn count(mut self, count: usize) -> Self {
self.count = count.max(1);
self
}
pub fn value(mut self, value: f32) -> Self {
self.value = value.max(0.0);
self
}
pub fn allow_half(mut self, allow: bool) -> Self {
self.allow_half = allow;
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn id(mut self, id: impl Into<SharedString>) -> Self {
self.instance = id.into();
self
}
pub fn on_change<F>(mut self, f: F) -> Self
where
F: Fn(f32, &mut Window, &mut App) + Send + Sync + 'static,
{
self.on_change = Some(Arc::new(f));
self
}
}
impl Default for Rate {
fn default() -> Self {
Self::new()
}
}
impl Styled for Rate {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl RenderOnce for Rate {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
let accent = theme.tokens.accent.color;
let muted_foreground = theme.tokens.muted_foreground.color;
let user_style = self.style;
let value = self.value;
let count = self.count;
let allow_half = self.allow_half;
let disabled = self.disabled;
let on_change = self.on_change;
let instance = self.instance;
div()
.flex()
.flex_row()
.items_center()
.gap(px(2.0))
.children((0..count).map(|ix| {
let base = ix as f32 + 1.0;
let filled = value >= base;
let half = !filled && allow_half && value >= base - 0.5;
let lit = filled || half;
let on_change = on_change.clone();
div()
.id(SharedString::from(format!("rate-{instance}-{ix}")))
.cursor_pointer()
.text_lg()
.text_color(if lit { accent } else { muted_foreground })
.when(half, |this| this.opacity(0.5))
.child(if lit { "★" } else { "☆" })
.when(!disabled, |this| {
this.on_click(move |_, window, cx| {
if let Some(ref cb) = on_change {
cb(base, window, cx);
}
})
})
.into_any_element()
}))
.map(|mut this| {
this.style().refine(&user_style);
this
})
}
}