1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use hex::FromHex;
use uuid::Uuid;

use crate::{DefaultModifiers, Renderable};
use crate::components::{Alignment, Appendable, HStack, Text, TextStyle, View};
use crate::node::{Node, NodeContainer};

#[derive(Debug, Clone)]
pub struct Color(u8, u8, u8);

impl Color {
    pub fn from_hex(hex: &str) -> Self {
        let color_string = hex
            .to_string()
            .replace("#", "");
        let color_parsed = <[u8; 3]>::from_hex(color_string).expect("color format not valid");
        Color(color_parsed[0], color_parsed[1], color_parsed[2])
    }

    

    pub fn to_string(&self) -> String {
        let color_array = [self.0, self.1, self.2];
        hex::encode(color_array)
    }
    pub fn to_css_value(&self) -> String {
        format!("#{}", self.to_string())
    }
}

#[derive(Debug, Clone)]
pub enum ColorPickerStyle {
    Palette(Vec<Color>),
    Free,
}

impl ColorPickerStyle {
    pub fn get_style_name(&self) -> String {
        match self {
            ColorPickerStyle::Palette(_) => { "palette" }
            ColorPickerStyle::Free => { "free" }
        }.to_string()
    }
}

#[derive(Debug, Clone)]
pub struct ColorPicker {
    node: Node,
    style: ColorPickerStyle,
    label: Option<String>,
    name: String,
    children: Vec<Box<dyn Renderable>>,
}

impl ColorPicker {
    pub fn new(name: &str, style: ColorPickerStyle) -> Self {
        ColorPicker {
            node: Default::default(),
            style,
            label: None,
            name: name.to_string(),
            children: vec![],
        }
    }
    pub fn label(&mut self, label: &str) -> Self {
        self.label = Some(label.to_string());
        self.clone()
    }


}

impl NodeContainer for ColorPicker {
    fn get_node(&mut self) -> &mut Node {
        &mut self.node
    }
}

impl DefaultModifiers<ColorPicker> for ColorPicker {}

impl Renderable for ColorPicker {
    fn render(&self) -> Node {
        let base_class = format!("color-picker--{}", self.style.get_style_name());
        let mut picker = self.clone()
            .add_class("color-picker")
            .add_class(&base_class);
        if let Some(label) = picker.label {
            picker.node.children.push({
                Text::new(label.as_str(), TextStyle::Label)
            }.render());
        }
        let picker_id = Uuid::new_v4().to_hyphenated().to_string();
        match &self.style {
            ColorPickerStyle::Palette(color_palette) => {
                picker.node.children.push({
                    let mut option_list = HStack::new(Alignment::Center)
                        .add_class(&format!("{}--option-list", &base_class));
                    for color in color_palette {
                        let radio_id = format!("color-picker-{}-{}", picker_id, color.to_string());
                        option_list
                            .append_child({
                                View::new()
                                    .tag("input")
                                    .set_attr("type", "radio")
                                    .set_attr("name", self.name.as_str())
                                    .set_attr("value", &color.to_string())
                                    .set_attr("id", radio_id.as_str())
                                    .add_class(&format!("{}--option-list--radio", &base_class))
                            });
                        option_list.append_child({
                            View::new()
                                .tag("label")
                                .set_attr("for", radio_id.as_str())
                                .color(&color.to_css_value())
                                .add_class(&format!("{}--option-list--swatch", &base_class))
                        });
                    }
                    option_list.render()
                })
            }
            ColorPickerStyle::Free => {
                unimplemented!("Free selection mode is not implemented yet.")
            }
        }
        picker.node
    }
}