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
}
}