增加QScheme主题样式的生成与导出。
This commit is contained in:
		@@ -1,7 +1,8 @@
 | 
			
		||||
use palette::{
 | 
			
		||||
    cam16::{Cam16Jch, Parameters},
 | 
			
		||||
    convert::FromColorUnclamped,
 | 
			
		||||
    Hsl, IsWithinBounds, Srgb,
 | 
			
		||||
    luma::Luma,
 | 
			
		||||
    Hsl, IntoColor, IsWithinBounds, Oklab, Oklch, Srgb,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub fn map_cam16jch_to_srgb(origin: &Cam16Jch<f32>) -> Srgb {
 | 
			
		||||
@@ -44,3 +45,43 @@ pub fn map_hsl_to_srgb(origin: &Hsl) -> Srgb {
 | 
			
		||||
pub fn map_hsl_to_srgb_hex(origin: &Hsl) -> String {
 | 
			
		||||
    format!("{:x}", map_hsl_to_srgb(origin).into_format::<u8>())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn map_oklch_to_luma(origin: &Oklch) -> Luma {
 | 
			
		||||
    let lab_color: Oklab = (*origin).into_color();
 | 
			
		||||
    let linear_rgb = Srgb::from_linear(lab_color.into_color());
 | 
			
		||||
    let luma_color: Luma = linear_rgb.into_color();
 | 
			
		||||
    luma_color
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn map_oklch_to_srgb(origin: &Oklch) -> Srgb {
 | 
			
		||||
    Srgb::from_linear::<f32>((*origin).into_color())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn map_oklch_to_srgb_hex(origin: &Oklch) -> String {
 | 
			
		||||
    format!("{:x}", map_oklch_to_srgb(origin).into_format::<u8>())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! parse_to_oklch {
 | 
			
		||||
    ($origin: ident) => {
 | 
			
		||||
        palette::Oklch::from_color(
 | 
			
		||||
            palette::Srgb::from_str($origin)
 | 
			
		||||
                .map_err(|_| crate::errors::ColorError::UnrecogniazedRGB($origin.to_string()))?
 | 
			
		||||
                .into_format::<f32>(),
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! parse_option_to_oklch {
 | 
			
		||||
    ($origin: ident) => {
 | 
			
		||||
        $origin
 | 
			
		||||
            .map(|color| {
 | 
			
		||||
                let rgb = palette::Srgb::from_str(color)
 | 
			
		||||
                    .map_err(|_| crate::errors::ColorError::UnrecogniazedRGB(color.to_string()))?
 | 
			
		||||
                    .into_format::<f32>();
 | 
			
		||||
                Ok(palette::Oklch::from_color(rgb))
 | 
			
		||||
            })
 | 
			
		||||
            .transpose()?
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,14 @@ use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use material_design_2::MaterialDesign2Scheme;
 | 
			
		||||
use material_design_3::MaterialDesign3Scheme;
 | 
			
		||||
use q_style::{QScheme, SchemeSetting};
 | 
			
		||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
 | 
			
		||||
 | 
			
		||||
use crate::errors;
 | 
			
		||||
 | 
			
		||||
pub mod material_design_2;
 | 
			
		||||
pub mod material_design_3;
 | 
			
		||||
pub mod q_style;
 | 
			
		||||
 | 
			
		||||
pub trait SchemeExport {
 | 
			
		||||
    fn output_css_variables(&self) -> String;
 | 
			
		||||
@@ -57,3 +59,73 @@ pub fn generate_material_design_2_scheme(
 | 
			
		||||
    ))
 | 
			
		||||
    .map_err(|_| errors::ColorError::UnableToAssembleOutput)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen]
 | 
			
		||||
pub fn generate_q_scheme_automatically(
 | 
			
		||||
    primary_color: &str,
 | 
			
		||||
    danger_color: &str,
 | 
			
		||||
    success_color: &str,
 | 
			
		||||
    warning_color: &str,
 | 
			
		||||
    info_color: &str,
 | 
			
		||||
    fg_color: &str,
 | 
			
		||||
    bg_color: &str,
 | 
			
		||||
    setting: JsValue,
 | 
			
		||||
) -> Result<JsValue, errors::ColorError> {
 | 
			
		||||
    let settings: SchemeSetting = serde_wasm_bindgen::from_value(setting)
 | 
			
		||||
        .map_err(|_| errors::ColorError::UnableToParseArgument)?;
 | 
			
		||||
    let scheme = QScheme::new(
 | 
			
		||||
        primary_color,
 | 
			
		||||
        danger_color,
 | 
			
		||||
        success_color,
 | 
			
		||||
        warning_color,
 | 
			
		||||
        info_color,
 | 
			
		||||
        fg_color,
 | 
			
		||||
        bg_color,
 | 
			
		||||
        settings,
 | 
			
		||||
    )?;
 | 
			
		||||
    Ok(serde_wasm_bindgen::to_value(&(
 | 
			
		||||
        scheme.clone(),
 | 
			
		||||
        scheme.output_css_variables(),
 | 
			
		||||
        scheme.output_scss_variables(),
 | 
			
		||||
        scheme.output_javascript_object(),
 | 
			
		||||
    ))
 | 
			
		||||
    .map_err(|_| errors::ColorError::UnableToAssembleOutput)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen]
 | 
			
		||||
pub fn generate_q_scheme_manually(
 | 
			
		||||
    primary_color: &str,
 | 
			
		||||
    secondary_color: Option<String>,
 | 
			
		||||
    tertiary_color: Option<String>,
 | 
			
		||||
    accent_color: Option<String>,
 | 
			
		||||
    danger_color: &str,
 | 
			
		||||
    success_color: &str,
 | 
			
		||||
    warning_color: &str,
 | 
			
		||||
    info_color: &str,
 | 
			
		||||
    fg_color: &str,
 | 
			
		||||
    bg_color: &str,
 | 
			
		||||
    setting: JsValue,
 | 
			
		||||
) -> Result<JsValue, errors::ColorError> {
 | 
			
		||||
    let settings: SchemeSetting = serde_wasm_bindgen::from_value(setting)
 | 
			
		||||
        .map_err(|_| errors::ColorError::UnableToParseArgument)?;
 | 
			
		||||
    let scheme = QScheme::custom(
 | 
			
		||||
        primary_color,
 | 
			
		||||
        secondary_color.as_deref(),
 | 
			
		||||
        tertiary_color.as_deref(),
 | 
			
		||||
        accent_color.as_deref(),
 | 
			
		||||
        danger_color,
 | 
			
		||||
        success_color,
 | 
			
		||||
        warning_color,
 | 
			
		||||
        info_color,
 | 
			
		||||
        fg_color,
 | 
			
		||||
        bg_color,
 | 
			
		||||
        settings,
 | 
			
		||||
    )?;
 | 
			
		||||
    Ok(serde_wasm_bindgen::to_value(&(
 | 
			
		||||
        scheme.clone(),
 | 
			
		||||
        scheme.output_css_variables(),
 | 
			
		||||
        scheme.output_scss_variables(),
 | 
			
		||||
        scheme.output_javascript_object(),
 | 
			
		||||
    ))
 | 
			
		||||
    .map_err(|_| errors::ColorError::UnableToAssembleOutput)?)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										241
									
								
								color-module/src/schemes/q_style/baseline.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								color-module/src/schemes/q_style/baseline.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,241 @@
 | 
			
		||||
use palette::{
 | 
			
		||||
    color_theory::{Analogous, Complementary, SplitComplementary, Tetradic, Triadic},
 | 
			
		||||
    Oklch, ShiftHue,
 | 
			
		||||
};
 | 
			
		||||
use serde::{ser::SerializeStruct, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::convert::map_oklch_to_srgb_hex;
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    color_set::ColorSet,
 | 
			
		||||
    neutral_swatch::NeutralSwatch,
 | 
			
		||||
    scheme_setting::{ColorExpand, SchemeSetting},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Baseline {
 | 
			
		||||
    pub primary: ColorSet,
 | 
			
		||||
    pub secondary: Option<ColorSet>,
 | 
			
		||||
    pub tertiary: Option<ColorSet>,
 | 
			
		||||
    pub accent: Option<ColorSet>,
 | 
			
		||||
    pub neutral: ColorSet,
 | 
			
		||||
    pub danger: ColorSet,
 | 
			
		||||
    pub success: ColorSet,
 | 
			
		||||
    pub warning: ColorSet,
 | 
			
		||||
    pub info: ColorSet,
 | 
			
		||||
    pub outline: ColorSet,
 | 
			
		||||
    pub foreground: Oklch,
 | 
			
		||||
    pub background: Oklch,
 | 
			
		||||
    _neutral_swatch: NeutralSwatch,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Baseline {
 | 
			
		||||
    pub fn custom(
 | 
			
		||||
        primary: &Oklch,
 | 
			
		||||
        secondary: &Option<Oklch>,
 | 
			
		||||
        tertiary: &Option<Oklch>,
 | 
			
		||||
        accent: &Option<Oklch>,
 | 
			
		||||
        danger: &Oklch,
 | 
			
		||||
        success: &Oklch,
 | 
			
		||||
        warning: &Oklch,
 | 
			
		||||
        info: &Oklch,
 | 
			
		||||
        foreground: &Oklch,
 | 
			
		||||
        background: &Oklch,
 | 
			
		||||
        setting: SchemeSetting,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let neutral_swatch = NeutralSwatch::new(*foreground, *background);
 | 
			
		||||
        let neutral_color = neutral_swatch.get(primary.l);
 | 
			
		||||
        let outline_color = neutral_swatch.get(background.l * 0.7);
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            primary: ColorSet::new(primary, &neutral_swatch, &setting),
 | 
			
		||||
            secondary: secondary.map(|color| ColorSet::new(&color, &neutral_swatch, &setting)),
 | 
			
		||||
            tertiary: tertiary.map(|color| ColorSet::new(&color, &neutral_swatch, &setting)),
 | 
			
		||||
            accent: accent.map(|color| ColorSet::new(&color, &neutral_swatch, &setting)),
 | 
			
		||||
            neutral: ColorSet::new(&neutral_color, &neutral_swatch, &setting),
 | 
			
		||||
            danger: ColorSet::new(danger, &neutral_swatch, &setting),
 | 
			
		||||
            success: ColorSet::new(success, &neutral_swatch, &setting),
 | 
			
		||||
            warning: ColorSet::new(warning, &neutral_swatch, &setting),
 | 
			
		||||
            info: ColorSet::new(info, &neutral_swatch, &setting),
 | 
			
		||||
            outline: ColorSet::new(&outline_color, &neutral_swatch, &setting),
 | 
			
		||||
            foreground: *foreground,
 | 
			
		||||
            background: *background,
 | 
			
		||||
            _neutral_swatch: neutral_swatch,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        primary: &Oklch,
 | 
			
		||||
        danger: &Oklch,
 | 
			
		||||
        success: &Oklch,
 | 
			
		||||
        warning: &Oklch,
 | 
			
		||||
        info: &Oklch,
 | 
			
		||||
        foreground: &Oklch,
 | 
			
		||||
        background: &Oklch,
 | 
			
		||||
        setting: SchemeSetting,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let (secondary, tertiary, accent) = match setting.expand_method {
 | 
			
		||||
            ColorExpand::Complementary => (Some(primary.complementary()), None, None),
 | 
			
		||||
            ColorExpand::Analogous => {
 | 
			
		||||
                let analogous_color = primary.analogous();
 | 
			
		||||
                (Some(analogous_color.0), Some(analogous_color.1), None)
 | 
			
		||||
            }
 | 
			
		||||
            ColorExpand::AnalogousAndComplementary => {
 | 
			
		||||
                let analogous_color = primary.analogous();
 | 
			
		||||
                (
 | 
			
		||||
                    Some(analogous_color.0),
 | 
			
		||||
                    Some(analogous_color.1),
 | 
			
		||||
                    Some(primary.complementary()),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            ColorExpand::Triadic => {
 | 
			
		||||
                let triadic_color = primary.triadic();
 | 
			
		||||
                (Some(triadic_color.0), Some(triadic_color.1), None)
 | 
			
		||||
            }
 | 
			
		||||
            ColorExpand::SplitComplementary => {
 | 
			
		||||
                let split_complementary_color = primary.split_complementary();
 | 
			
		||||
                (
 | 
			
		||||
                    Some(split_complementary_color.0),
 | 
			
		||||
                    None,
 | 
			
		||||
                    Some(split_complementary_color.1),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            ColorExpand::Tetradic => {
 | 
			
		||||
                let tetradic_color = primary.tetradic();
 | 
			
		||||
                (
 | 
			
		||||
                    Some(tetradic_color.0),
 | 
			
		||||
                    Some(tetradic_color.2),
 | 
			
		||||
                    Some(tetradic_color.1),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            ColorExpand::Square => {
 | 
			
		||||
                let c_90 = primary.shift_hue(90.0);
 | 
			
		||||
                let complementary = primary.complementary();
 | 
			
		||||
                let c_270 = primary.shift_hue(270.0);
 | 
			
		||||
                (Some(c_90), Some(c_270), Some(complementary))
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Self::custom(
 | 
			
		||||
            primary, &secondary, &tertiary, &accent, danger, success, warning, info, foreground,
 | 
			
		||||
            background, setting,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_css_variables(&self, prefix: &str) -> Vec<String> {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
 | 
			
		||||
        variables.extend(self.primary.to_css_variables(prefix, "primary"));
 | 
			
		||||
        if let Some(secondary) = &self.secondary {
 | 
			
		||||
            variables.extend(secondary.to_css_variables(prefix, "secondary"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(tertiary) = &self.tertiary {
 | 
			
		||||
            variables.extend(tertiary.to_css_variables(prefix, "tertiary"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(accent) = &self.accent {
 | 
			
		||||
            variables.extend(accent.to_css_variables(prefix, "accent"));
 | 
			
		||||
        }
 | 
			
		||||
        variables.extend(self.neutral.to_css_variables(prefix, "neutral"));
 | 
			
		||||
        variables.extend(self.danger.to_css_variables(prefix, "danger"));
 | 
			
		||||
        variables.extend(self.success.to_css_variables(prefix, "success"));
 | 
			
		||||
        variables.extend(self.warning.to_css_variables(prefix, "warning"));
 | 
			
		||||
        variables.extend(self.info.to_css_variables(prefix, "info"));
 | 
			
		||||
        variables.extend(self.outline.to_css_variables(prefix, "outline"));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-foreground: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.foreground)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-background: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.background)
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        variables
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_scss_variables(&self, prefix: &str) -> Vec<String> {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
 | 
			
		||||
        variables.extend(self.primary.to_scss_variables(prefix, "primary"));
 | 
			
		||||
        if let Some(secondary) = &self.secondary {
 | 
			
		||||
            variables.extend(secondary.to_scss_variables(prefix, "secondary"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(tertiary) = &self.tertiary {
 | 
			
		||||
            variables.extend(tertiary.to_scss_variables(prefix, "tertiary"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(accent) = &self.accent {
 | 
			
		||||
            variables.extend(accent.to_scss_variables(prefix, "accent"));
 | 
			
		||||
        }
 | 
			
		||||
        variables.extend(self.neutral.to_scss_variables(prefix, "neutral"));
 | 
			
		||||
        variables.extend(self.danger.to_scss_variables(prefix, "danger"));
 | 
			
		||||
        variables.extend(self.success.to_scss_variables(prefix, "success"));
 | 
			
		||||
        variables.extend(self.warning.to_scss_variables(prefix, "warning"));
 | 
			
		||||
        variables.extend(self.info.to_scss_variables(prefix, "info"));
 | 
			
		||||
        variables.extend(self.outline.to_scss_variables(prefix, "outline"));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-foreground: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.foreground)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-background: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.background)
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        variables
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_javascript_fields(&self) -> Vec<String> {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
 | 
			
		||||
        variables.extend(self.primary.to_javascript_fields("primary"));
 | 
			
		||||
        if let Some(secondary) = &self.secondary {
 | 
			
		||||
            variables.extend(secondary.to_javascript_fields("secondary"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(tertiary) = &self.tertiary {
 | 
			
		||||
            variables.extend(tertiary.to_javascript_fields("tertiary"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(accent) = &self.accent {
 | 
			
		||||
            variables.extend(accent.to_javascript_fields("accent"));
 | 
			
		||||
        }
 | 
			
		||||
        variables.extend(self.neutral.to_javascript_fields("neutral"));
 | 
			
		||||
        variables.extend(self.danger.to_javascript_fields("danger"));
 | 
			
		||||
        variables.extend(self.success.to_javascript_fields("success"));
 | 
			
		||||
        variables.extend(self.warning.to_javascript_fields("warning"));
 | 
			
		||||
        variables.extend(self.info.to_javascript_fields("info"));
 | 
			
		||||
        variables.extend(self.outline.to_javascript_fields("outline"));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "foreground: '#{}',",
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.foreground)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "background: '#{}',",
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.background)
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        variables
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Serialize for Baseline {
 | 
			
		||||
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
 | 
			
		||||
        let mut map = serializer.serialize_struct("Baseline", 13)?;
 | 
			
		||||
 | 
			
		||||
        map.serialize_field("primary", &self.primary)?;
 | 
			
		||||
        map.serialize_field("secondary", &self.secondary)?;
 | 
			
		||||
        map.serialize_field("tertiary", &self.tertiary)?;
 | 
			
		||||
        map.serialize_field("accent", &self.accent)?;
 | 
			
		||||
        map.serialize_field("neutral", &self.neutral)?;
 | 
			
		||||
        map.serialize_field("danger", &self.danger)?;
 | 
			
		||||
        map.serialize_field("success", &self.success)?;
 | 
			
		||||
        map.serialize_field("warning", &self.warning)?;
 | 
			
		||||
        map.serialize_field("info", &self.info)?;
 | 
			
		||||
        map.serialize_field("outline", &self.outline)?;
 | 
			
		||||
        map.serialize_field("foreground", &map_oklch_to_srgb_hex(&self.foreground))?;
 | 
			
		||||
        map.serialize_field("background", &map_oklch_to_srgb_hex(&self.background))?;
 | 
			
		||||
        map.end()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										327
									
								
								color-module/src/schemes/q_style/color_set.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								color-module/src/schemes/q_style/color_set.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,327 @@
 | 
			
		||||
use palette::{color_difference::Wcag21RelativeContrast, luma::Luma, Oklch};
 | 
			
		||||
use serde::{ser::SerializeStruct, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::convert::{map_oklch_to_luma, map_oklch_to_srgb_hex};
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    neutral_swatch::NeutralSwatch,
 | 
			
		||||
    scheme_setting::{SchemeSetting, WACGSetting},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct ColorSet {
 | 
			
		||||
    pub root: Oklch,
 | 
			
		||||
    pub hover: Oklch,
 | 
			
		||||
    pub active: Oklch,
 | 
			
		||||
    pub focus: Oklch,
 | 
			
		||||
    pub disabled: Oklch,
 | 
			
		||||
    pub on_root: Oklch,
 | 
			
		||||
    pub on_hover: Oklch,
 | 
			
		||||
    pub on_active: Oklch,
 | 
			
		||||
    pub on_focus: Oklch,
 | 
			
		||||
    pub on_disabled: Oklch,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn fit_to_wacg(reference: &Oklch, neutral_swatch: &NeutralSwatch, ratio: f32) -> Oklch {
 | 
			
		||||
    let reference_luma = map_oklch_to_luma(reference);
 | 
			
		||||
    let mut new_target = neutral_swatch.get(reference.l);
 | 
			
		||||
    let quick_factor: f32 = if reference.l <= 0.5 { 0.05 } else { -0.05 };
 | 
			
		||||
    let fine_factor: f32 = if reference.l <= 0.5 { 0.01 } else { -0.01 };
 | 
			
		||||
 | 
			
		||||
    let match_wacg = |original: &Oklch<f32>, reference: &Luma| {
 | 
			
		||||
        let luma = map_oklch_to_luma(original);
 | 
			
		||||
        luma.relative_contrast(*reference)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    while match_wacg(&new_target, &reference_luma) < ratio {
 | 
			
		||||
        new_target.l = new_target.l * (1.0 + quick_factor);
 | 
			
		||||
        if new_target.l > 1.0 {
 | 
			
		||||
            new_target.l = 1.0;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    while match_wacg(&new_target, &reference_luma) < ratio {
 | 
			
		||||
        new_target.l = new_target.l * (1.0 + fine_factor);
 | 
			
		||||
        if new_target.l > 1.0 {
 | 
			
		||||
            new_target.l = 1.0;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    new_target
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ColorSet {
 | 
			
		||||
    pub fn new(color: &Oklch, neutral_swatch: &NeutralSwatch, setting: &SchemeSetting) -> Self {
 | 
			
		||||
        let root = color.clone();
 | 
			
		||||
        let hover = color * setting.hover;
 | 
			
		||||
        let active = color * setting.active;
 | 
			
		||||
        let focus = color * setting.focus;
 | 
			
		||||
        let disabled = color * setting.disabled;
 | 
			
		||||
 | 
			
		||||
        let (on_root, on_hover, on_active, on_focus, on_disabled) = match setting.wacg_follows {
 | 
			
		||||
            WACGSetting::Fixed => (
 | 
			
		||||
                neutral_swatch.get(root.l),
 | 
			
		||||
                neutral_swatch.get(hover.l),
 | 
			
		||||
                neutral_swatch.get(active.l),
 | 
			
		||||
                neutral_swatch.get(focus.l),
 | 
			
		||||
                neutral_swatch.get(disabled.l),
 | 
			
		||||
            ),
 | 
			
		||||
            WACGSetting::AutomaticAA => (
 | 
			
		||||
                fit_to_wacg(&root, neutral_swatch, 4.5),
 | 
			
		||||
                fit_to_wacg(&hover, neutral_swatch, 4.5),
 | 
			
		||||
                fit_to_wacg(&active, neutral_swatch, 4.5),
 | 
			
		||||
                fit_to_wacg(&focus, neutral_swatch, 4.5),
 | 
			
		||||
                fit_to_wacg(&disabled, neutral_swatch, 4.5),
 | 
			
		||||
            ),
 | 
			
		||||
            WACGSetting::AutomaticAAA => (
 | 
			
		||||
                fit_to_wacg(&root, neutral_swatch, 7.0),
 | 
			
		||||
                fit_to_wacg(&hover, neutral_swatch, 7.0),
 | 
			
		||||
                fit_to_wacg(&active, neutral_swatch, 7.0),
 | 
			
		||||
                fit_to_wacg(&focus, neutral_swatch, 7.0),
 | 
			
		||||
                fit_to_wacg(&disabled, neutral_swatch, 7.0),
 | 
			
		||||
            ),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            root,
 | 
			
		||||
            hover,
 | 
			
		||||
            active,
 | 
			
		||||
            focus,
 | 
			
		||||
            disabled,
 | 
			
		||||
            on_root,
 | 
			
		||||
            on_hover,
 | 
			
		||||
            on_active,
 | 
			
		||||
            on_focus,
 | 
			
		||||
            on_disabled,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_css_variables(&self, prefix: &str, name: &str) -> Vec<String> {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-{}: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.root)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-{}-hover: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.hover)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-{}-active: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.active)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-{}-focus: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.focus)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-{}-disabled: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.disabled)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-on-{}: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_root)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-on-{}-hover: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_hover)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-on-{}-active: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_active)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-on-{}-focus: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_focus)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "--color-{}-on-{}-disabled: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_disabled)
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        variables
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_scss_variables(&self, prefix: &str, name: &str) -> Vec<String> {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-{}: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.root)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-{}-hover: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.hover)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-{}-active: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.active)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-{}-focus: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.focus)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-{}-disabled: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.disabled)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-on-{}: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_root)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-on-{}-hover: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_hover)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-on-{}-active: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_active)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-on-{}-focus: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_focus)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "$color-{}-on-{}-disabled: #{};",
 | 
			
		||||
            prefix,
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_disabled)
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        variables
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn to_javascript_fields(&self, name: &str) -> Vec<String> {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
        let capitalized_name = name
 | 
			
		||||
            .chars()
 | 
			
		||||
            .next()
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .to_ascii_uppercase()
 | 
			
		||||
            .to_string()
 | 
			
		||||
            + &name[1..];
 | 
			
		||||
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "{}: '#{}',",
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.root)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "{}Hover: '#{}',",
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.hover)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "{}Active: '#{}',",
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.active)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "{}Focus: '#{}',",
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.focus)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "{}Disabled: '#{}',",
 | 
			
		||||
            name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.disabled)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "on{}: '#{}',",
 | 
			
		||||
            capitalized_name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_root)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "on{}Hover: '#{}',",
 | 
			
		||||
            capitalized_name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_hover)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "on{}Active: '#{}',",
 | 
			
		||||
            capitalized_name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_active)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "on{}Focus: '#{}',",
 | 
			
		||||
            capitalized_name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_focus)
 | 
			
		||||
        ));
 | 
			
		||||
        variables.push(format!(
 | 
			
		||||
            "on{}Disabled: '#{}',",
 | 
			
		||||
            capitalized_name,
 | 
			
		||||
            map_oklch_to_srgb_hex(&self.on_disabled)
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        variables
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Serialize for ColorSet {
 | 
			
		||||
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 | 
			
		||||
    where
 | 
			
		||||
        S: serde::Serializer,
 | 
			
		||||
    {
 | 
			
		||||
        let root = map_oklch_to_srgb_hex(&self.root);
 | 
			
		||||
        let hover = map_oklch_to_srgb_hex(&self.hover);
 | 
			
		||||
        let active = map_oklch_to_srgb_hex(&self.active);
 | 
			
		||||
        let focus = map_oklch_to_srgb_hex(&self.focus);
 | 
			
		||||
        let disabled = map_oklch_to_srgb_hex(&self.disabled);
 | 
			
		||||
        let on_root = map_oklch_to_srgb_hex(&self.on_root);
 | 
			
		||||
        let on_hover = map_oklch_to_srgb_hex(&self.on_hover);
 | 
			
		||||
        let on_active = map_oklch_to_srgb_hex(&self.on_active);
 | 
			
		||||
        let on_focus = map_oklch_to_srgb_hex(&self.on_focus);
 | 
			
		||||
        let on_disabled = map_oklch_to_srgb_hex(&self.on_disabled);
 | 
			
		||||
 | 
			
		||||
        let mut state = serializer.serialize_struct("ColorSet", 10)?;
 | 
			
		||||
        state.serialize_field("root", &root)?;
 | 
			
		||||
        state.serialize_field("hover", &hover)?;
 | 
			
		||||
        state.serialize_field("active", &active)?;
 | 
			
		||||
        state.serialize_field("focus", &focus)?;
 | 
			
		||||
        state.serialize_field("disabled", &disabled)?;
 | 
			
		||||
        state.serialize_field("onRoot", &on_root)?;
 | 
			
		||||
        state.serialize_field("onHover", &on_hover)?;
 | 
			
		||||
        state.serialize_field("onActive", &on_active)?;
 | 
			
		||||
        state.serialize_field("onFocus", &on_focus)?;
 | 
			
		||||
        state.serialize_field("onDisabled", &on_disabled)?;
 | 
			
		||||
        state.end()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										196
									
								
								color-module/src/schemes/q_style/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								color-module/src/schemes/q_style/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
			
		||||
use std::str::FromStr;
 | 
			
		||||
 | 
			
		||||
use baseline::Baseline;
 | 
			
		||||
use palette::FromColor;
 | 
			
		||||
use scheme_setting::{ColorExpand, WACGSetting};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use strum::IntoEnumIterator;
 | 
			
		||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
 | 
			
		||||
 | 
			
		||||
use crate::{errors, parse_option_to_oklch, parse_to_oklch};
 | 
			
		||||
 | 
			
		||||
use super::SchemeExport;
 | 
			
		||||
 | 
			
		||||
mod baseline;
 | 
			
		||||
mod color_set;
 | 
			
		||||
mod neutral_swatch;
 | 
			
		||||
mod scheme_setting;
 | 
			
		||||
 | 
			
		||||
pub use scheme_setting::SchemeSetting;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize)]
 | 
			
		||||
pub struct QScheme {
 | 
			
		||||
    pub light: Baseline,
 | 
			
		||||
    pub dark: Baseline,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl QScheme {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        primary: &str,
 | 
			
		||||
        danger: &str,
 | 
			
		||||
        success: &str,
 | 
			
		||||
        warning: &str,
 | 
			
		||||
        info: &str,
 | 
			
		||||
        foreground: &str,
 | 
			
		||||
        background: &str,
 | 
			
		||||
        setting: SchemeSetting,
 | 
			
		||||
    ) -> Result<Self, errors::ColorError> {
 | 
			
		||||
        let primary = parse_to_oklch!(primary);
 | 
			
		||||
        let danger = parse_to_oklch!(danger);
 | 
			
		||||
        let success = parse_to_oklch!(success);
 | 
			
		||||
        let warning = parse_to_oklch!(warning);
 | 
			
		||||
        let info = parse_to_oklch!(info);
 | 
			
		||||
        let foreground = parse_to_oklch!(foreground);
 | 
			
		||||
        let background = parse_to_oklch!(background);
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            light: Baseline::new(
 | 
			
		||||
                &primary,
 | 
			
		||||
                &danger,
 | 
			
		||||
                &success,
 | 
			
		||||
                &warning,
 | 
			
		||||
                &info,
 | 
			
		||||
                &foreground,
 | 
			
		||||
                &background,
 | 
			
		||||
                setting.clone(),
 | 
			
		||||
            ),
 | 
			
		||||
            dark: Baseline::new(
 | 
			
		||||
                &(primary * setting.dark_convert),
 | 
			
		||||
                &(danger * setting.dark_convert),
 | 
			
		||||
                &(success * setting.dark_convert),
 | 
			
		||||
                &(warning * setting.dark_convert),
 | 
			
		||||
                &(info * setting.dark_convert),
 | 
			
		||||
                &(foreground * setting.dark_convert),
 | 
			
		||||
                &(background * setting.dark_convert),
 | 
			
		||||
                setting.clone(),
 | 
			
		||||
            ),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn custom(
 | 
			
		||||
        primary: &str,
 | 
			
		||||
        secondary: Option<&str>,
 | 
			
		||||
        tertiary: Option<&str>,
 | 
			
		||||
        accent: Option<&str>,
 | 
			
		||||
        danger: &str,
 | 
			
		||||
        success: &str,
 | 
			
		||||
        warning: &str,
 | 
			
		||||
        info: &str,
 | 
			
		||||
        foreground: &str,
 | 
			
		||||
        background: &str,
 | 
			
		||||
        setting: SchemeSetting,
 | 
			
		||||
    ) -> Result<Self, errors::ColorError> {
 | 
			
		||||
        let primary = parse_to_oklch!(primary);
 | 
			
		||||
        let secondary = parse_option_to_oklch!(secondary);
 | 
			
		||||
        let tertiary = parse_option_to_oklch!(tertiary);
 | 
			
		||||
        let accent = parse_option_to_oklch!(accent);
 | 
			
		||||
        let danger = parse_to_oklch!(danger);
 | 
			
		||||
        let success = parse_to_oklch!(success);
 | 
			
		||||
        let warning = parse_to_oklch!(warning);
 | 
			
		||||
        let info = parse_to_oklch!(info);
 | 
			
		||||
        let foreground = parse_to_oklch!(foreground);
 | 
			
		||||
        let background = parse_to_oklch!(background);
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            light: Baseline::custom(
 | 
			
		||||
                &primary,
 | 
			
		||||
                &secondary,
 | 
			
		||||
                &tertiary,
 | 
			
		||||
                &accent,
 | 
			
		||||
                &danger,
 | 
			
		||||
                &success,
 | 
			
		||||
                &warning,
 | 
			
		||||
                &info,
 | 
			
		||||
                &foreground,
 | 
			
		||||
                &background,
 | 
			
		||||
                setting.clone(),
 | 
			
		||||
            ),
 | 
			
		||||
            dark: Baseline::custom(
 | 
			
		||||
                &(primary * setting.dark_convert),
 | 
			
		||||
                &secondary.map(|color| color * setting.dark_convert),
 | 
			
		||||
                &tertiary.map(|color| color * setting.dark_convert),
 | 
			
		||||
                &accent.map(|color| color * setting.dark_convert),
 | 
			
		||||
                &(danger * setting.dark_convert),
 | 
			
		||||
                &(success * setting.dark_convert),
 | 
			
		||||
                &(warning * setting.dark_convert),
 | 
			
		||||
                &(info * setting.dark_convert),
 | 
			
		||||
                &(foreground * setting.dark_convert),
 | 
			
		||||
                &(background * setting.dark_convert),
 | 
			
		||||
                setting.clone(),
 | 
			
		||||
            ),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SchemeExport for QScheme {
 | 
			
		||||
    fn output_css_variables(&self) -> String {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
 | 
			
		||||
        variables.extend(self.light.to_css_variables("light"));
 | 
			
		||||
        variables.extend(self.dark.to_css_variables("dark"));
 | 
			
		||||
 | 
			
		||||
        variables.join("\n")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn output_scss_variables(&self) -> String {
 | 
			
		||||
        let mut variables = Vec::new();
 | 
			
		||||
 | 
			
		||||
        variables.extend(self.light.to_scss_variables("light"));
 | 
			
		||||
        variables.extend(self.dark.to_scss_variables("dark"));
 | 
			
		||||
 | 
			
		||||
        variables.join("\n")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn output_javascript_object(&self) -> String {
 | 
			
		||||
        let mut javascript_object = Vec::new();
 | 
			
		||||
 | 
			
		||||
        javascript_object.push("{".to_string());
 | 
			
		||||
        javascript_object.push("  light: {".to_string());
 | 
			
		||||
        javascript_object.extend(
 | 
			
		||||
            self.light
 | 
			
		||||
                .to_javascript_fields()
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|c| format!("    {}", c))
 | 
			
		||||
                .collect::<Vec<_>>(),
 | 
			
		||||
        );
 | 
			
		||||
        javascript_object.push("  },".to_string());
 | 
			
		||||
        javascript_object.push("  dark: {".to_string());
 | 
			
		||||
        javascript_object.extend(
 | 
			
		||||
            self.dark
 | 
			
		||||
                .to_javascript_fields()
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|c| format!("    {}", c))
 | 
			
		||||
                .collect::<Vec<_>>(),
 | 
			
		||||
        );
 | 
			
		||||
        javascript_object.push("}".to_string());
 | 
			
		||||
 | 
			
		||||
        javascript_object.join("\n")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen]
 | 
			
		||||
pub fn q_scheme_color_expanding_methods() -> Result<JsValue, String> {
 | 
			
		||||
    let methods = ColorExpand::iter()
 | 
			
		||||
        .map(|variant| {
 | 
			
		||||
            serde_json::json!({
 | 
			
		||||
                "label": variant.label(),
 | 
			
		||||
                "value": variant.to_string(),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
    serde_wasm_bindgen::to_value(&methods).map_err(|e| e.to_string())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[wasm_bindgen]
 | 
			
		||||
pub fn q_scheme_wacg_settings() -> Result<JsValue, String> {
 | 
			
		||||
    let settings = WACGSetting::iter()
 | 
			
		||||
        .map(|setting| {
 | 
			
		||||
            serde_json::json!({
 | 
			
		||||
                "label": setting.label(),
 | 
			
		||||
                "value": setting.to_string(),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
    serde_wasm_bindgen::to_value(&settings).map_err(|e| e.to_string())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								color-module/src/schemes/q_style/neutral_swatch.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								color-module/src/schemes/q_style/neutral_swatch.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
use palette::Oklch;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct NeutralSwatch(Oklch, Oklch);
 | 
			
		||||
 | 
			
		||||
impl NeutralSwatch {
 | 
			
		||||
    pub fn new(color1: Oklch, color2: Oklch) -> Self {
 | 
			
		||||
        if color1.l < color2.l {
 | 
			
		||||
            NeutralSwatch(
 | 
			
		||||
                Oklch {
 | 
			
		||||
                    l: color1.l * 0.4,
 | 
			
		||||
                    chroma: color1.chroma * 0.1,
 | 
			
		||||
                    ..color1
 | 
			
		||||
                },
 | 
			
		||||
                Oklch {
 | 
			
		||||
                    l: color2.l * 1.6,
 | 
			
		||||
                    chroma: color2.chroma * 0.1,
 | 
			
		||||
                    ..color2
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        } else {
 | 
			
		||||
            NeutralSwatch(
 | 
			
		||||
                Oklch {
 | 
			
		||||
                    l: color2.l * 0.4,
 | 
			
		||||
                    chroma: color2.chroma * 0.1,
 | 
			
		||||
                    ..color2
 | 
			
		||||
                },
 | 
			
		||||
                Oklch {
 | 
			
		||||
                    l: color1.l * 1.6,
 | 
			
		||||
                    chroma: color1.chroma * 0.1,
 | 
			
		||||
                    ..color1
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get(&self, percent: f32) -> Oklch {
 | 
			
		||||
        let start_hue = self.0.hue.into_positive_degrees();
 | 
			
		||||
        let end_hue = self.1.hue.into_positive_degrees();
 | 
			
		||||
 | 
			
		||||
        let hue = if (start_hue - end_hue).abs() > 180.0 {
 | 
			
		||||
            if end_hue > start_hue {
 | 
			
		||||
                start_hue + (end_hue + 360.0 - start_hue) * percent
 | 
			
		||||
            } else {
 | 
			
		||||
                start_hue + (end_hue - start_hue + 360.0) * percent
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            start_hue + (end_hue - start_hue) * percent
 | 
			
		||||
        }
 | 
			
		||||
        .rem_euclid(360.0);
 | 
			
		||||
 | 
			
		||||
        Oklch {
 | 
			
		||||
            l: percent,
 | 
			
		||||
            chroma: self.0.chroma + (self.1.chroma - self.0.chroma) * percent,
 | 
			
		||||
            hue: hue.into(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										139
									
								
								color-module/src/schemes/q_style/scheme_setting.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								color-module/src/schemes/q_style/scheme_setting.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
use std::ops::Mul;
 | 
			
		||||
 | 
			
		||||
use palette::Oklch;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use strum::{Display, EnumIter, EnumString};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
 | 
			
		||||
pub struct ColorShifting {
 | 
			
		||||
    pub chroma: f32,
 | 
			
		||||
    pub lightness: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Mul<ColorShifting> for Oklch<f32> {
 | 
			
		||||
    type Output = Oklch<f32>;
 | 
			
		||||
 | 
			
		||||
    fn mul(self, rhs: ColorShifting) -> Self::Output {
 | 
			
		||||
        Oklch::new(
 | 
			
		||||
            self.l
 | 
			
		||||
                + if rhs.lightness > 0.0 {
 | 
			
		||||
                    (1.0 - self.l) * rhs.lightness
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.l * rhs.lightness * -1.0
 | 
			
		||||
                },
 | 
			
		||||
            self.chroma
 | 
			
		||||
                + if rhs.chroma > 0.0 {
 | 
			
		||||
                    (100.0 - self.chroma) * rhs.chroma
 | 
			
		||||
                } else {
 | 
			
		||||
                    -(self.chroma * rhs.chroma)
 | 
			
		||||
                },
 | 
			
		||||
            self.hue,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Mul<ColorShifting> for &Oklch<f32> {
 | 
			
		||||
    type Output = Oklch<f32>;
 | 
			
		||||
 | 
			
		||||
    fn mul(self, rhs: ColorShifting) -> Self::Output {
 | 
			
		||||
        Oklch::new(
 | 
			
		||||
            self.l
 | 
			
		||||
                + if rhs.lightness > 0.0 {
 | 
			
		||||
                    (1.0 - self.l) * rhs.lightness
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.l * rhs.lightness * -1.0
 | 
			
		||||
                },
 | 
			
		||||
            self.chroma
 | 
			
		||||
                + if rhs.chroma > 0.0 {
 | 
			
		||||
                    (100.0 - self.chroma) * rhs.chroma
 | 
			
		||||
                } else {
 | 
			
		||||
                    -(self.chroma * rhs.chroma)
 | 
			
		||||
                },
 | 
			
		||||
            self.hue,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
 | 
			
		||||
pub struct SchemeSetting {
 | 
			
		||||
    pub hover: ColorShifting,
 | 
			
		||||
    pub active: ColorShifting,
 | 
			
		||||
    pub focus: ColorShifting,
 | 
			
		||||
    pub disabled: ColorShifting,
 | 
			
		||||
    pub dark_convert: ColorShifting,
 | 
			
		||||
    pub expand_method: ColorExpand,
 | 
			
		||||
    pub wacg_follows: WACGSetting,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, Display, EnumString, EnumIter, Serialize, Deserialize)]
 | 
			
		||||
#[strum(serialize_all = "lowercase")]
 | 
			
		||||
pub enum ColorExpand {
 | 
			
		||||
    Complementary,
 | 
			
		||||
    Analogous,
 | 
			
		||||
    AnalogousAndComplementary,
 | 
			
		||||
    Triadic,
 | 
			
		||||
    SplitComplementary,
 | 
			
		||||
    Tetradic,
 | 
			
		||||
    Square,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ColorExpand {
 | 
			
		||||
    pub fn label(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            ColorExpand::Complementary => "Complementary".to_string(),
 | 
			
		||||
            ColorExpand::Analogous => "Analogous".to_string(),
 | 
			
		||||
            ColorExpand::AnalogousAndComplementary => "Analogous and Complementary".to_string(),
 | 
			
		||||
            ColorExpand::Triadic => "Triadic".to_string(),
 | 
			
		||||
            ColorExpand::SplitComplementary => "Split Complementary".to_string(),
 | 
			
		||||
            ColorExpand::Tetradic => "Tetradic".to_string(),
 | 
			
		||||
            ColorExpand::Square => "Square".to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, Display, EnumString, EnumIter, Serialize, Deserialize)]
 | 
			
		||||
#[strum(serialize_all = "lowercase")]
 | 
			
		||||
pub enum WACGSetting {
 | 
			
		||||
    Fixed,
 | 
			
		||||
    AutomaticAA,
 | 
			
		||||
    AutomaticAAA,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WACGSetting {
 | 
			
		||||
    pub fn label(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            WACGSetting::Fixed => "Fixed".to_string(),
 | 
			
		||||
            WACGSetting::AutomaticAA => "Automatic AA".to_string(),
 | 
			
		||||
            WACGSetting::AutomaticAAA => "Automatic AAA".to_string(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for SchemeSetting {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        SchemeSetting {
 | 
			
		||||
            hover: ColorShifting {
 | 
			
		||||
                chroma: 0.0,
 | 
			
		||||
                lightness: 0.3,
 | 
			
		||||
            },
 | 
			
		||||
            active: ColorShifting {
 | 
			
		||||
                chroma: 0.0,
 | 
			
		||||
                lightness: -0.2,
 | 
			
		||||
            },
 | 
			
		||||
            focus: ColorShifting {
 | 
			
		||||
                chroma: 0.0,
 | 
			
		||||
                lightness: 0.5,
 | 
			
		||||
            },
 | 
			
		||||
            disabled: ColorShifting {
 | 
			
		||||
                chroma: -0.9,
 | 
			
		||||
                lightness: -0.2,
 | 
			
		||||
            },
 | 
			
		||||
            dark_convert: ColorShifting {
 | 
			
		||||
                chroma: -0.3,
 | 
			
		||||
                lightness: -0.3,
 | 
			
		||||
            },
 | 
			
		||||
            expand_method: ColorExpand::AnalogousAndComplementary,
 | 
			
		||||
            wacg_follows: WACGSetting::AutomaticAAA,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user