diff --git a/build.rs b/build.rs index 58ada3f..80f3543 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,5 @@ use std::env; +use std::fmt::Display; use std::fs::File; use std::io::Write; use std::io::{self, BufRead}; @@ -10,14 +11,32 @@ use regex::Regex; fn main() { // Tell Cargo to rerun this build script if any SCSS file // in the 'src' directory or its subdirectories changes. - println!("cargo:rerun-if-changed=src/**/*.scss"); + println!("cargo:rerun-if-changed=src/ui/**/*.scss"); let out_dir = env::var("OUT_DIR").unwrap(); - let style_src_path = PathBuf::from("src/ui/_base.scss"); - let style_dst_path = Path::new(&out_dir).join("style_vars.rs"); + let mut tasks = Vec::new(); - export_color_variables(&style_src_path, &style_dst_path) + // Global tokens + tasks.push(Task::new( + PathBuf::from("src/ui/_base.scss"), + Path::new(&out_dir).join("style_tokens.rs"), + "style".to_string(), + )); + // variables defined by the Panel component + tasks.push(Task::new( + PathBuf::from("src/ui/components/_panel.scss"), + Path::new(&out_dir).join("style_component_panel.rs"), + "panel".to_string(), + )); + // Variables set by the Conversations layout + tasks.push(Task::new( + PathBuf::from("src/ui/layouts/conversations.scss"), + Path::new(&out_dir).join("style_layout_conversations.rs"), + "conversations".to_string(), + )); + + export_variables(tasks) } // From https://doc.rust-lang.org/rust-by-example/std_misc/file/read_lines.html @@ -32,14 +51,21 @@ where } #[derive(Debug)] -struct CssColorVariable<'a> { - name: &'a str, - value: &'a str, +struct ColorVariable { + name: String, + value: String, } -impl<'a> CssColorVariable<'a> { - pub fn to_rust(&self) -> String { - format!( +impl ColorVariable { + pub fn new(name: String, value: String) -> Self { + Self { name, value } + } +} + +impl Display for ColorVariable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, "const {name}: &str = \"{value}\";", name = self.name.replace('-', "_").to_uppercase(), value = self.value @@ -47,35 +73,83 @@ impl<'a> CssColorVariable<'a> { } } -fn export_color_variables(src_path: &PathBuf, dst_path: &PathBuf) { - let mut dst_file = File::create(dst_path).unwrap(); - if let Err(err) = dst_file.write(b"#[allow(dead_code)]\nmod style {") { - println!("{}", err); - return; - }; +#[derive(Debug)] +struct FloatVariable { + name: String, + value: f64, +} - let re = Regex::new(r"^\$([^:]+):[[:space:]]*#([^$]+);[[:space:]]*$").unwrap(); +impl FloatVariable { + pub fn new(name: String, value: f64) -> Self { + Self { name, value } + } +} - if let Ok(lines) = read_lines(src_path) { - for line in lines.map_while(Result::ok) { - let Some(groups) = re.captures(&line) else { - continue; - }; +impl Display for FloatVariable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "const {name}: f64 = {value};", + name = self.name.replace('-', "_").to_uppercase(), + value = self.value + ) + } +} - let var = CssColorVariable { - name: &groups[1], - value: &groups[2], - }; +struct Task { + src_path: PathBuf, + dst_path: PathBuf, + module_name: String, +} - let rust_export = var.to_rust(); - if let Err(err) = dst_file.write_fmt(format_args!(" pub {}\n", rust_export)) { +impl Task { + pub fn new(src_path: PathBuf, dst_path: PathBuf, module_name: String) -> Self { + Self { + src_path, + dst_path, + module_name, + } + } +} + +// fn export_variables(src_path: &PathBuf, dst_path: &PathBuf) { +fn export_variables(tasks: Vec) { + let color_re = Regex::new(r"^\$([^:]+):[[:space:]]*#([^$]+);[[:space:]]*$").unwrap(); + let variable_re = Regex::new(r"^\$([^:]+):[[:space:]]*([^;]+)[[:space:]]*;").unwrap(); + + for task in tasks { + let mut dst_file = File::create(task.dst_path).unwrap(); + if let Err(err) = dst_file.write_fmt(format_args!( + "#[allow(dead_code)]\nmod {} {{\n", + task.module_name + )) { + println!("{}", err); + return; + }; + + let mut variables = Vec::>::new(); + if let Ok(lines) = read_lines(task.src_path) { + for line in lines.map_while(Result::ok) { + if let Some(groups) = color_re.captures(&line) { + let var = ColorVariable::new(groups[1].to_string(), groups[2].to_string()); + variables.push(Box::new(var)); + } else if let Some(groups) = variable_re.captures(&line) { + if let Ok(value) = groups[2].parse::() { + variables.push(Box::new(FloatVariable::new(groups[1].to_string(), value))); + } + } + } + } + + for variable in variables { + if let Err(err) = dst_file.write_fmt(format_args!(" pub {}\n", variable)) { println!("{}", err); break; } } - } - if let Err(err) = dst_file.write(b"}\n") { - println!("{}", err); - }; + if let Err(err) = dst_file.write(b"}\n") { + println!("{}", err); + }; + } } diff --git a/src/ui/components/_panel.scss b/src/ui/components/_panel.scss index 9dc3efd..088acbf 100644 --- a/src/ui/components/_panel.scss +++ b/src/ui/components/_panel.scss @@ -1,6 +1,6 @@ @import "../base.scss"; -$panel-aspect-ratio: 1/1.618; +$aspect-ratio: 0.618; // 1/1.618; @mixin panel($padding-v: 2%, $padding-h: 2%) { padding: $padding-v $padding-h; diff --git a/src/ui/components/icons.rs b/src/ui/components/icons.rs index 99fcb3d..7a14764 100644 --- a/src/ui/components/icons.rs +++ b/src/ui/components/icons.rs @@ -7,7 +7,7 @@ use dioxus_free_icons::{Icon, IconShape}; turf::style_sheet!("src/ui/components/icons.scss"); -include!(concat!(env!("OUT_DIR"), "/style_vars.rs")); +include!(concat!(env!("OUT_DIR"), "/style_tokens.rs")); use style::{COLOR_PRIMARY_100, COLOR_TERNARY_100}; diff --git a/src/ui/components/login.rs b/src/ui/components/login.rs index fce17f0..b6f967e 100644 --- a/src/ui/components/login.rs +++ b/src/ui/components/login.rs @@ -19,7 +19,7 @@ use super::{ text_input::{PasswordInputState, PasswordTextInput, TextInput, TextInputState}, }; -include!(concat!(env!("OUT_DIR"), "/style_vars.rs")); +include!(concat!(env!("OUT_DIR"), "/style_tokens.rs")); use style::{ COLOR_PRIMARY_100, COLOR_PRIMARY_110, COLOR_PRIMARY_120, COLOR_PRIMARY_140, COLOR_PRIMARY_150, diff --git a/src/ui/components/modal.rs b/src/ui/components/modal.rs index 483c8ea..1b5b764 100644 --- a/src/ui/components/modal.rs +++ b/src/ui/components/modal.rs @@ -9,7 +9,7 @@ use crate::infrastructure::services::random_svg_generators::{ generate_random_svg_avatar, AvatarConfig, AvatarFeeling, }; -include!(concat!(env!("OUT_DIR"), "/style_vars.rs")); +include!(concat!(env!("OUT_DIR"), "/style_tokens.rs")); use style::{COLOR_CRITICAL_100, COLOR_SUCCESS_100, COLOR_WARNING_100}; diff --git a/src/ui/layouts/conversations.rs b/src/ui/layouts/conversations.rs index ee9c37d..5741189 100644 --- a/src/ui/layouts/conversations.rs +++ b/src/ui/layouts/conversations.rs @@ -3,15 +3,18 @@ use std::rc::Rc; use dioxus::prelude::*; use futures::join; +use crate::ui::components::{ + chat_panel::ChatPanel, conversations::Conversations as ConversationsComponent, + wallpaper::Wallpaper, +}; + turf::style_sheet!("src/ui/layouts/conversations.scss"); -use crate::ui::components::chat_panel::ChatPanel; -use crate::ui::components::conversations::Conversations as ConversationsComponent; -use crate::ui::components::wallpaper::Wallpaper; +include!(concat!(env!("OUT_DIR"), "/style_component_panel.rs")); +include!(concat!(env!("OUT_DIR"), "/style_layout_conversations.rs")); -// TODO: Get from SCSS -const WIDGET_HEIGHT_RATIO: f64 = 0.95; -const ASPECT_RATIO: f64 = 1.0 / 1.618; +use conversations::INNER_PANEL_HEIGHT_RATIO; +use panel::ASPECT_RATIO; async fn on_carousel_scroll( parent_div: &Rc, @@ -229,14 +232,13 @@ pub fn Conversations() -> Element { if let Ok(size) = data.get_border_box_size() { if let Some(size) = size.first() { // Use LayoutBig if the layout can contain 2 panels side by side - let component_width = size.height * WIDGET_HEIGHT_RATIO * ASPECT_RATIO; + let component_width = size.height * INNER_PANEL_HEIGHT_RATIO * ASPECT_RATIO; let breakpoint_width = component_width * 2_f64; use_big_layout = size.width > breakpoint_width; } } layout.set(rsx! { if use_big_layout { LayoutBig {} } else { LayoutSmall {} }}); - }, {layout} diff --git a/src/ui/layouts/conversations.scss b/src/ui/layouts/conversations.scss index 8844a62..146f19c 100644 --- a/src/ui/layouts/conversations.scss +++ b/src/ui/layouts/conversations.scss @@ -17,11 +17,13 @@ scroll-snap-align: start; } +$inner-panel-height-ratio: 0.95; + .conversations-view { $height: 100vh; $width: 100vw; - $conversations-panel-height: calc($height * 0.95); - $conversations-panel-width: calc($conversations-panel-height * $panel-aspect-ratio); + $conversations-panel-height: calc($height * $inner-panel-height-ratio); + $conversations-panel-width: calc($conversations-panel-height * $aspect-ratio); $gap: 1%; $content-height: 95%; $ratio: 2; @@ -63,11 +65,11 @@ height: 100%; // TODO: Is aspect-ratio the best criteria to defined that inner shall take all the available space ? - @media (max-aspect-ratio: $panel-aspect-ratio) { + @media (max-aspect-ratio: $aspect-ratio) { width: 100%; } - @media (min-aspect-ratio: $panel-aspect-ratio) { - aspect-ratio: $panel-aspect-ratio; + @media (min-aspect-ratio: $aspect-ratio) { + aspect-ratio: $aspect-ratio; } } } @@ -105,7 +107,7 @@ &__conversations-panel { height: $content-height; - aspect-ratio: $panel-aspect-ratio; + aspect-ratio: $aspect-ratio; } &__conversation-panels { diff --git a/src/ui/layouts/login.scss b/src/ui/layouts/login.scss index 4b40530..5dde0b4 100644 --- a/src/ui/layouts/login.scss +++ b/src/ui/layouts/login.scss @@ -18,14 +18,14 @@ align-items: safe center; &__login-panel { - @media (max-aspect-ratio: $panel-aspect-ratio) { + @media (max-aspect-ratio: $aspect-ratio) { width: 95%; } - @media (min-aspect-ratio: $panel-aspect-ratio) { + @media (min-aspect-ratio: $aspect-ratio) { height: 100%; } - aspect-ratio: $panel-aspect-ratio; + aspect-ratio: $aspect-ratio; max-height: $panel-max-height; flex-shrink: 0; @@ -36,6 +36,6 @@ justify-content: center; // Variables inherited by children - --aspect-ratio: #{$panel-aspect-ratio}; + --aspect-ratio: #{$aspect-ratio}; } }