feat: reactivityl; vdom; rhei; styles; fs; image element

This commit is contained in:
Faynot
2026-06-02 12:12:20 +03:00
commit f956b750e3
16 changed files with 7713 additions and 0 deletions

183
src/interpreter/style.rs Normal file
View File

@@ -0,0 +1,183 @@
use std::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct StyleSheet {
index: HashMap<String, HashMap<String, String>>,
}
impl StyleSheet {
pub fn new() -> Self {
Self::default()
}
pub fn add_rule(&mut self, selector: String, properties: HashMap<String, String>) {
self.index
.entry(selector.trim().to_string())
.or_default()
.extend(properties);
}
#[inline]
pub fn resolve(&self, element_type: &str) -> Option<&HashMap<String, String>> {
self.index.get(element_type.trim())
}
pub fn is_empty(&self) -> bool {
self.index.is_empty()
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ComputedStyle {
pub font_size: Option<f32>,
pub color: Option<iced::Color>,
pub padding: Option<f32>,
pub background: Option<iced::Color>,
pub spacing: Option<f32>,
pub border_radius: Option<f32>,
pub border_width: Option<f32>,
pub border_color: Option<iced::Color>,
pub width: Option<f32>,
pub direction: Option<LayoutDirection>,
pub align_items: Option<iced::Alignment>,
pub content_align: Option<ContentAlign>,
}
impl ComputedStyle {
pub fn compute(
inline: &HashMap<String, String>,
sheet: Option<&HashMap<String, String>>,
) -> Self {
Self {
font_size: lookup("font-size", inline, sheet).and_then(parse_size),
color: lookup("color", inline, sheet).and_then(parse_color),
padding: lookup("padding", inline, sheet).and_then(parse_size),
background: lookup("background", inline, sheet)
.or_else(|| lookup("background-color", inline, sheet))
.and_then(parse_color),
spacing: lookup("spacing", inline, sheet)
.or_else(|| lookup("gap", inline, sheet))
.and_then(parse_size),
border_radius: lookup("border-radius", inline, sheet).and_then(parse_size),
border_width: lookup("border-width", inline, sheet).and_then(parse_size),
border_color: lookup("border-color", inline, sheet).and_then(parse_color),
width: lookup("width", inline, sheet).and_then(parse_size),
direction: lookup("direction", inline, sheet).and_then(parse_direction),
align_items: lookup("align-items", inline, sheet).and_then(parse_alignment),
content_align: lookup("content-align", inline, sheet).and_then(parse_content_align),
}
}
}
#[inline]
fn lookup<'a>(
key: &str,
inline: &'a HashMap<String, String>,
sheet: Option<&'a HashMap<String, String>>,
) -> Option<&'a str> {
if let Some(v) = inline.get(key) {
return Some(v.as_str());
}
if let Some(v) = inline.get(&format!("style:{key}")) {
return Some(v.as_str());
}
sheet.and_then(|s| s.get(key)).map(String::as_str)
}
pub fn parse_color(s: &str) -> Option<iced::Color> {
let s = s.trim();
if let Some(hex) = s.strip_prefix('#') {
return match hex.len() {
3 => {
let r = u8::from_str_radix(&hex[0..1].repeat(2), 16).ok()?;
let g = u8::from_str_radix(&hex[1..2].repeat(2), 16).ok()?;
let b = u8::from_str_radix(&hex[2..3].repeat(2), 16).ok()?;
Some(iced::Color::from_rgb(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
))
}
6 => {
let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
Some(iced::Color::from_rgb(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
))
}
8 => {
let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
let a = u8::from_str_radix(&hex[6..8], 16).ok()?;
Some(iced::Color::from_rgba(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
a as f32 / 255.0,
))
}
_ => None,
};
}
match s {
"white" => Some(iced::Color::WHITE),
"black" => Some(iced::Color::BLACK),
"transparent" => Some(iced::Color::TRANSPARENT),
_ => None,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutDirection {
Column,
Row,
Grid,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContentAlign {
Start,
Center,
End,
}
pub fn parse_direction(s: &str) -> Option<LayoutDirection> {
match s.trim().to_lowercase().as_str() {
"horizontal" | "row" => Some(LayoutDirection::Row),
"vertical" | "column" => Some(LayoutDirection::Column),
"grid" => Some(LayoutDirection::Grid),
_ => None,
}
}
pub fn parse_alignment(s: &str) -> Option<iced::Alignment> {
match s.trim().to_lowercase().as_str() {
"start" => Some(iced::Alignment::Start),
"center" => Some(iced::Alignment::Center),
"end" => Some(iced::Alignment::End),
_ => None,
}
}
pub fn parse_content_align(s: &str) -> Option<ContentAlign> {
match s.trim().to_lowercase().as_str() {
"start" | "left" | "top" => Some(ContentAlign::Start),
"center" => Some(ContentAlign::Center),
"end" | "right" | "bottom" => Some(ContentAlign::End),
_ => None,
}
}
pub fn parse_size(s: &str) -> Option<f32> {
s.trim()
.trim_end_matches(|c: char| c.is_alphabetic() || c == '%')
.parse::<f32>()
.ok()
}