#![allow(non_snake_case)]
use std::rc::Rc;
use repose_core::*;
use repose_ui::{Box, Column, TextStyle, ViewExt};
use super::util::apply_tonal_elevation;
use super::*;
#[derive(Clone, Debug)]
pub struct CardConfig {
pub modifier: Modifier,
pub enabled: bool,
pub container_color: Color,
pub content_color: Color,
pub disabled_container_color: Color,
pub disabled_content_color: Color,
pub shape_radius: f32,
pub tonal_elevation: f32,
pub state_elevation: Option<StateElevation>,
pub border: Option<(f32, Color)>,
pub interaction_source: Option<MutableInteractionSource>,
}
impl Default for CardConfig {
fn default() -> Self {
Self {
modifier: Modifier::new(),
enabled: true,
container_color: CardDefaults::filled_container_color(),
content_color: CardDefaults::filled_content_color(),
disabled_container_color: CardDefaults::disabled_container_color(),
disabled_content_color: CardDefaults::disabled_content_color(),
shape_radius: CardDefaults::SHAPE_RADIUS,
tonal_elevation: CardDefaults::ELEVATION,
state_elevation: None,
border: None,
interaction_source: None,
}
}
}
pub fn Card(config: CardConfig, content: impl FnOnce() -> View) -> View {
let bg = if !config.enabled {
config.disabled_container_color
} else {
config.container_color
};
let fg = if !config.enabled {
config.disabled_content_color
} else {
config.content_color
};
let source: Rc<MutableInteractionSource> = config
.interaction_source
.clone()
.map(Rc::new)
.unwrap_or_else(|| remember(MutableInteractionSource::new));
let mut m = config
.modifier
.background(bg)
.clip_rounded(config.shape_radius)
.interaction_source(&source);
if let Some((w, c)) = config.border {
m = m.border(w, c, config.shape_radius);
}
if let Some(se) = config.state_elevation {
m = m.state_elevation(if config.enabled {
se
} else {
StateElevation {
default: 0.0,
hovered: 0.0,
focused: 0.0,
pressed: 0.0,
dragged: 0.0,
disabled: 0.0,
}
});
} else if config.tonal_elevation > 0.0 {
m = apply_tonal_elevation(m, config.tonal_elevation, bg);
}
if !config.enabled {
m = m.enabled(false);
}
Box(m).color(fg).child(with_content_color(fg, content))
}
pub fn ElevatedCard(config: CardConfig, content: impl FnOnce() -> View) -> View {
let th = theme();
Card(
CardConfig {
container_color: CardDefaults::elevated_container_color(),
state_elevation: Some(StateElevation {
default: th.elevation.level1,
hovered: th.elevation.level2,
focused: th.elevation.level2,
pressed: th.elevation.level3,
dragged: th.elevation.level3,
disabled: 0.0,
}),
..config
},
content,
)
}
pub fn OutlinedCard(config: CardConfig, content: impl FnOnce() -> View) -> View {
Card(
CardConfig {
container_color: CardDefaults::outlined_container_color(),
border: Some((1.0, CardDefaults::outlined_border_color())),
..config
},
content,
)
}
fn card_state_colors(bg: Color) -> StateColors {
let th = theme();
StateColors {
default: Color::TRANSPARENT,
hovered: Color::TRANSPARENT,
focused: Color::TRANSPARENT,
pressed: Color::TRANSPARENT,
dragged: th.on_surface.with_alpha_f32(0.12).composite_over(bg),
disabled: th.on_surface.with_alpha_f32(0.12).composite_over(bg),
}
}
fn clickable_card_impl(
on_click: impl Fn() + 'static,
modifier: Modifier,
bg: Color,
shape_radius: f32,
config: CardConfig,
content: impl FnOnce() -> View,
) -> View {
let mut m = modifier
.state_colors(card_state_colors(bg))
.indication(crate::ripple::ripple(crate::ripple::RippleConfig {
color: Some(theme().on_surface),
bounded: true,
..Default::default()
}));
if config.enabled {
m = m.clickable().on_click(on_click);
} else {
m = m.enabled(false);
}
Card(
CardConfig {
modifier: m,
enabled: config.enabled,
container_color: bg,
content_color: config.content_color,
disabled_container_color: config.disabled_container_color,
disabled_content_color: config.disabled_content_color,
shape_radius,
border: config.border,
state_elevation: config.state_elevation,
tonal_elevation: config.tonal_elevation,
interaction_source: config.interaction_source.clone(),
},
|| Column(Modifier::new().fill_max_size()).child(content()),
)
}
pub fn ClickableCard(
on_click: impl Fn() + 'static,
modifier: Modifier,
config: CardConfig,
content: impl FnOnce() -> View,
) -> View {
let th = theme();
let bg = if config.container_color != CardDefaults::filled_container_color() {
config.container_color
} else {
th.surface_container_highest
};
clickable_card_impl(on_click, modifier, bg, th.shapes.medium, config, content)
}
pub fn ClickableElevatedCard(
on_click: impl Fn() + 'static,
modifier: Modifier,
config: CardConfig,
content: impl FnOnce() -> View,
) -> View {
let th = theme();
let cfg = CardConfig {
state_elevation: Some(StateElevation {
default: th.elevation.level1,
hovered: th.elevation.level2,
focused: th.elevation.level2,
pressed: th.elevation.level3,
dragged: th.elevation.level3,
disabled: 0.0,
}),
..config
};
clickable_card_impl(
on_click,
modifier,
th.surface,
th.shapes.medium,
cfg,
content,
)
}
pub fn ClickableOutlinedCard(
on_click: impl Fn() + 'static,
modifier: Modifier,
config: CardConfig,
content: impl FnOnce() -> View,
) -> View {
let th = theme();
let cfg = CardConfig {
border: Some((1.0, th.outline_variant)),
..config
};
clickable_card_impl(
on_click,
modifier,
th.surface,
th.shapes.medium,
cfg,
content,
)
}