Add a batch of color methods. (#20)
https://ui.shadcn.com/colors#colors
This commit is contained in:
parent
111f051707
commit
56eb7147d9
6 changed files with 2212 additions and 5 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -5418,6 +5418,8 @@ dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"image",
|
"image",
|
||||||
"log",
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"paste",
|
||||||
"resvg",
|
"resvg",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ image = "0.25.1"
|
||||||
resvg = { version = "0.41.0", default-features = false }
|
resvg = { version = "0.41.0", default-features = false }
|
||||||
usvg = { version = "0.41.0", default-features = false }
|
usvg = { version = "0.41.0", default-features = false }
|
||||||
taffy = "0.4.3"
|
taffy = "0.4.3"
|
||||||
|
paste = "1"
|
||||||
|
once_cell = "1.19.0"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
||||||
201
crates/ui/src/colors.rs
Normal file
201
crates/ui/src/colors.rs
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use gpui::Hsla;
|
||||||
|
use serde::{de::Error, Deserialize, Deserializer};
|
||||||
|
|
||||||
|
use crate::theme::hsl;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
static DEFAULT_COLOR: once_cell::sync::Lazy<ShacnColors> = once_cell::sync::Lazy::new(|| {
|
||||||
|
serde_json::from_str(include_str!("default-colors.json")).expect("failed to parse default-json")
|
||||||
|
});
|
||||||
|
|
||||||
|
type ColorScales = HashMap<usize, ShacnColor>;
|
||||||
|
|
||||||
|
mod color_scales {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::{ColorScales, ShacnColor};
|
||||||
|
|
||||||
|
use serde::de::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<ColorScales, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for color in Vec::<ShacnColor>::deserialize(deserializer)? {
|
||||||
|
map.insert(color.scale, color);
|
||||||
|
}
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
|
||||||
|
struct ShacnColors {
|
||||||
|
black: ShacnColor,
|
||||||
|
white: ShacnColor,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
slate: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
gray: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
zinc: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
neutral: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
stone: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
red: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
orange: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
amber: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
yellow: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
lime: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
green: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
emerald: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
teal: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
cyan: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
sky: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
blue: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
indigo: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
violet: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
purple: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
fuchsia: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
pink: ColorScales,
|
||||||
|
#[serde(with = "color_scales")]
|
||||||
|
rose: ColorScales,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize)]
|
||||||
|
struct ShacnColor {
|
||||||
|
#[serde(default)]
|
||||||
|
scale: usize,
|
||||||
|
#[serde(deserialize_with = "from_hsa_channel", alias = "hslChannel")]
|
||||||
|
hsla: Hsla,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize Hsla from a string in the format "210 40% 98%"
|
||||||
|
fn from_hsa_channel<'de, D>(deserializer: D) -> Result<Hsla, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s: String = Deserialize::deserialize(deserializer).unwrap();
|
||||||
|
|
||||||
|
let mut parts = s.split_whitespace();
|
||||||
|
if parts.clone().count() != 3 {
|
||||||
|
return Err(D::Error::custom(
|
||||||
|
"expected hslChannel has 3 parts, e.g: '210 40% 98%'",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_number(s: &str) -> f32 {
|
||||||
|
s.trim_end_matches('%').parse().unwrap_or(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
let (h, s, l) = (
|
||||||
|
parse_number(parts.next().unwrap()),
|
||||||
|
parse_number(parts.next().unwrap()),
|
||||||
|
parse_number(parts.next().unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(hsl(h, s, l))
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! color_method {
|
||||||
|
($color:tt, $scale:tt) => {
|
||||||
|
paste::paste! {
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn [<$color _ $scale>]() -> Hsla {
|
||||||
|
if let Some(color) = DEFAULT_COLOR.$color.get(&($scale as usize)) {
|
||||||
|
return color.hsla;
|
||||||
|
}
|
||||||
|
|
||||||
|
black()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! color_methods {
|
||||||
|
($color:tt) => {
|
||||||
|
color_method!($color, 50);
|
||||||
|
color_method!($color, 100);
|
||||||
|
color_method!($color, 200);
|
||||||
|
color_method!($color, 300);
|
||||||
|
color_method!($color, 400);
|
||||||
|
color_method!($color, 500);
|
||||||
|
color_method!($color, 600);
|
||||||
|
color_method!($color, 700);
|
||||||
|
color_method!($color, 800);
|
||||||
|
color_method!($color, 900);
|
||||||
|
color_method!($color, 950);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn black() -> Hsla {
|
||||||
|
DEFAULT_COLOR.black.hsla
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn white() -> Hsla {
|
||||||
|
DEFAULT_COLOR.white.hsla
|
||||||
|
}
|
||||||
|
|
||||||
|
color_methods!(slate);
|
||||||
|
color_methods!(gray);
|
||||||
|
color_methods!(zinc);
|
||||||
|
color_methods!(neutral);
|
||||||
|
color_methods!(stone);
|
||||||
|
color_methods!(red);
|
||||||
|
color_methods!(orange);
|
||||||
|
color_methods!(amber);
|
||||||
|
color_methods!(yellow);
|
||||||
|
color_methods!(lime);
|
||||||
|
color_methods!(green);
|
||||||
|
color_methods!(emerald);
|
||||||
|
color_methods!(teal);
|
||||||
|
color_methods!(cyan);
|
||||||
|
color_methods!(sky);
|
||||||
|
color_methods!(blue);
|
||||||
|
color_methods!(indigo);
|
||||||
|
color_methods!(violet);
|
||||||
|
color_methods!(purple);
|
||||||
|
color_methods!(fuchsia);
|
||||||
|
color_methods!(pink);
|
||||||
|
color_methods!(rose);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_colors() {
|
||||||
|
assert_eq!(white(), hsl(0.0, 0.0, 100.0));
|
||||||
|
assert_eq!(black(), hsl(0.0, 0.0, 0.0));
|
||||||
|
|
||||||
|
assert_eq!(slate_50(), hsl(210.0, 40.0, 98.0));
|
||||||
|
assert_eq!(slate_100(), hsl(210.0, 40.0, 96.1));
|
||||||
|
assert_eq!(slate_900(), hsl(222.2, 47.4, 11.2));
|
||||||
|
|
||||||
|
assert_eq!(red_50(), hsl(0.0, 85.7, 97.3));
|
||||||
|
assert_eq!(yellow_100(), hsl(54.9, 96.7, 88.0));
|
||||||
|
assert_eq!(green_200(), hsl(141.0, 78.9, 85.1));
|
||||||
|
assert_eq!(cyan_300(), hsl(187.0, 92.4, 69.0));
|
||||||
|
assert_eq!(blue_400(), hsl(213.1, 93.9, 67.8));
|
||||||
|
assert_eq!(indigo_500(), hsl(238.7, 83.5, 66.7));
|
||||||
|
}
|
||||||
|
}
|
||||||
2000
crates/ui/src/default-colors.json
Normal file
2000
crates/ui/src/default-colors.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,5 @@
|
||||||
mod clickable;
|
mod clickable;
|
||||||
|
mod colors;
|
||||||
mod disableable;
|
mod disableable;
|
||||||
mod event;
|
mod event;
|
||||||
mod focusable;
|
mod focusable;
|
||||||
|
|
@ -33,6 +34,7 @@ pub use event::InterativeElementExt;
|
||||||
pub use focusable::FocusableCycle;
|
pub use focusable::FocusableCycle;
|
||||||
pub use selectable::{Selectable, Selection};
|
pub use selectable::{Selectable, Selection};
|
||||||
|
|
||||||
|
pub use colors::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
pub use svg_img::*;
|
pub use svg_img::*;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::theme::{hsl, ActiveTheme};
|
use crate::theme::ActiveTheme;
|
||||||
use gpui::{hsla, point, px, BoxShadow, FocusHandle, Styled, WindowContext};
|
use gpui::{hsla, point, px, BoxShadow, FocusHandle, Styled, WindowContext};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ pub trait StyledExt: Styled + Sized {
|
||||||
/// Render a border with a width of 1px, color blue
|
/// Render a border with a width of 1px, color blue
|
||||||
fn debug_blue(self) -> Self {
|
fn debug_blue(self) -> Self {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
self.border_1().border_color(gpui::blue())
|
self.border_1().border_color(crate::blue_500())
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +89,7 @@ pub trait StyledExt: Styled + Sized {
|
||||||
/// Render a border with a width of 1px, color yellow
|
/// Render a border with a width of 1px, color yellow
|
||||||
fn debug_yellow(self) -> Self {
|
fn debug_yellow(self) -> Self {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
self.border_1().border_color(gpui::yellow())
|
self.border_1().border_color(crate::yellow_500())
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +98,7 @@ pub trait StyledExt: Styled + Sized {
|
||||||
/// Render a border with a width of 1px, color green
|
/// Render a border with a width of 1px, color green
|
||||||
fn debug_green(self) -> Self {
|
fn debug_green(self) -> Self {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
self.border_1().border_color(gpui::green())
|
self.border_1().border_color(crate::green_500())
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +107,7 @@ pub trait StyledExt: Styled + Sized {
|
||||||
/// Render a border with a width of 1px, color pink
|
/// Render a border with a width of 1px, color pink
|
||||||
fn debug_pink(self) -> Self {
|
fn debug_pink(self) -> Self {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
self.border_1().border_color(hsl(300., 100., 47.))
|
self.border_1().border_color(crate::pink_500())
|
||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue