based/src/ui/color.rs
2025-02-05 16:22:07 +01:00

283 lines
7.1 KiB
Rust

/// UI Color
pub trait UIColor {
#[must_use]
fn color_class(&self) -> &str;
}
pub trait ColorCircle {
#[must_use]
fn previous(&self) -> Self;
#[must_use]
fn middle(&self) -> Self;
#[must_use]
fn start(&self) -> Self;
#[must_use]
fn end(&self) -> Self;
#[must_use]
fn next(&self) -> Self;
}
#[allow(non_snake_case)]
pub fn RGB(r: u8, g: u8, b: u8) -> RGBColor {
RGBColor(format!("[#{r:02x}{g:02x}{b:02x}]"))
}
pub struct RGBColor(String);
impl UIColor for RGBColor {
fn color_class(&self) -> &str {
self.0.as_ref()
}
}
pub trait UIColorCircle: UIColor + ColorCircle + Sized {}
macro_rules! color_map {
($name:ident, $id:literal) => {
#[derive(Debug, Clone)]
pub enum $name {
_50,
_100,
_200,
_300,
_400,
_500,
_600,
_700,
_800,
_900,
_950,
}
impl UIColor for $name {
fn color_class(&self) -> &str {
match self {
$name::_50 => concat!($id, "-50"),
$name::_100 => concat!($id, "-100"),
$name::_200 => concat!($id, "-200"),
$name::_300 => concat!($id, "-300"),
$name::_400 => concat!($id, "-400"),
$name::_500 => concat!($id, "-500"),
$name::_600 => concat!($id, "-600"),
$name::_700 => concat!($id, "-700"),
$name::_800 => concat!($id, "-800"),
$name::_900 => concat!($id, "-900"),
$name::_950 => concat!($id, "-950"),
}
}
}
impl ColorCircle for $name {
fn next(&self) -> Self {
match self {
$name::_50 => $name::_100,
$name::_100 => $name::_200,
$name::_200 => $name::_300,
$name::_300 => $name::_400,
$name::_400 => $name::_500,
$name::_500 => $name::_600,
$name::_600 => $name::_700,
$name::_700 => $name::_800,
$name::_800 => $name::_900,
$name::_900 => $name::_950,
$name::_950 => $name::_50,
}
}
fn middle(&self) -> Self {
Self::_500
}
fn start(&self) -> Self {
Self::_50
}
fn end(&self) -> Self {
Self::_950
}
fn previous(&self) -> Self {
match self {
$name::_50 => $name::_950,
$name::_100 => $name::_50,
$name::_200 => $name::_100,
$name::_300 => $name::_200,
$name::_400 => $name::_300,
$name::_500 => $name::_400,
$name::_600 => $name::_500,
$name::_700 => $name::_600,
$name::_800 => $name::_700,
$name::_900 => $name::_800,
$name::_950 => $name::_900,
}
}
}
};
}
color_map!(Slate, "slate");
color_map!(Gray, "gray");
color_map!(Zinc, "zinc");
color_map!(Neutral, "neutral");
color_map!(Stone, "stone");
color_map!(Red, "red");
color_map!(Orange, "orange");
color_map!(Amber, "amber");
color_map!(Yellow, "yellow");
color_map!(Lime, "lime");
color_map!(Green, "green");
color_map!(Emerald, "emerald");
color_map!(Teal, "teal");
color_map!(Cyan, "cyan");
color_map!(Sky, "sky");
color_map!(Blue, "blue");
color_map!(Indigo, "indigo");
color_map!(Violet, "violet");
color_map!(Purple, "purple");
color_map!(Fuchsia, "fuchsia");
color_map!(Pink, "pink");
color_map!(Rose, "rose");
#[derive(Debug, Clone)]
pub enum Colors {
/// Inherit a color
Inherit,
/// Use current color
Current,
/// Transparency
Transparent,
/// Black
Black,
/// White
White,
}
impl ColorCircle for Colors {
fn previous(&self) -> Self {
match self {
Colors::Black => Colors::White,
Colors::White => Colors::Black,
_ => self.clone(),
}
}
fn middle(&self) -> Self {
self.clone()
}
fn start(&self) -> Self {
match self {
Colors::Black => Colors::White,
Colors::White => Colors::White,
_ => self.clone(),
}
}
fn end(&self) -> Self {
match self {
Colors::Black => Colors::Black,
Colors::White => Colors::Black,
_ => self.clone(),
}
}
fn next(&self) -> Self {
match self {
Colors::Black => Colors::White,
Colors::White => Colors::Black,
_ => self.clone(),
}
}
}
impl UIColor for Colors {
fn color_class(&self) -> &str {
match self {
Self::Inherit => "inherit",
Self::Current => "current",
Self::Transparent => "transparent",
Self::Black => "black",
Self::White => "white",
}
}
}
pub struct Gradient {
start: Box<dyn UIColor>,
middle: Option<Box<dyn UIColor>>,
end: Option<Box<dyn UIColor>>,
pos_start: Option<u8>,
pos_middle: Option<u8>,
pos_end: Option<u8>,
}
impl Gradient {
pub fn from<C: UIColor + 'static>(start: C) -> Self {
Self {
start: Box::new(start),
middle: None,
end: None,
pos_end: None,
pos_middle: None,
pos_start: None,
}
}
pub fn via<C: UIColor + 'static>(mut self, middle: C) -> Self {
self.middle = Some(Box::new(middle));
self
}
pub fn to<C: UIColor + 'static>(mut self, end: C) -> Self {
self.end = Some(Box::new(end));
self
}
pub fn step_start(mut self, percentage: u8) -> Self {
assert!(percentage <= 100, "Percentage should be under 100%");
self.pos_start = Some(percentage);
self
}
pub fn step_middle(mut self, percentage: u8) -> Self {
assert!(percentage <= 100, "Percentage should be under 100%");
self.pos_middle = Some(percentage);
self
}
pub fn step_end(mut self, percentage: u8) -> Self {
assert!(percentage <= 100, "Percentage should be under 100%");
self.pos_end = Some(percentage);
self
}
pub fn color_class(&self) -> Vec<String> {
let mut classes = vec![format!("from-{}", self.start.color_class())];
if let Some(via) = &self.middle {
classes.push(format!("via-{}", via.color_class()));
}
if let Some(end) = &self.end {
classes.push(format!("to-{}", end.color_class()));
}
if let Some(step) = &self.pos_start {
classes.push(format!("from-{step}%"));
}
if let Some(step) = &self.pos_middle {
classes.push(format!("via-{step}%"));
}
if let Some(step) = &self.pos_end {
classes.push(format!("to-{step}%"));
}
classes
}
}