From 7d460043da5a6038058dbc35eacba4f4ebebcbfb Mon Sep 17 00:00:00 2001 From: Kaede Hoshikawa Date: Fri, 20 Aug 2021 21:03:56 +0900 Subject: [PATCH] Implement Global and GlobalStyle properly. --- CHANGELOG.md | 3 +- examples/benchmarks/src/main.rs | 6 +- examples/yew-integration/src/main.rs | 6 +- examples/yew-shadow/src/main.rs | 6 +- examples/yew-theme-agent/src/main.rs | 6 +- examples/yew-theme-yewdux/src/main.rs | 6 +- packages/stylist-core/Cargo.toml | 4 + packages/stylist-core/src/ast/block.rs | 7 +- packages/stylist-core/src/ast/mod.rs | 6 +- packages/stylist-core/src/ast/rule.rs | 2 +- packages/stylist-core/src/ast/rule_content.rs | 2 +- .../stylist-core/src/ast/scope_content.rs | 2 +- packages/stylist-core/src/ast/selector.rs | 64 ++++-- packages/stylist-core/src/ast/sheet.rs | 2 +- packages/stylist-core/src/ast/style_attr.rs | 2 +- packages/stylist-core/src/ast/to_style_str.rs | 6 +- packages/stylist-core/src/lib.rs | 4 + packages/stylist-core/src/yew.rs | 29 +++ packages/stylist/Cargo.toml | 2 +- packages/stylist/src/bindings/mod.rs | 5 - packages/stylist/src/bindings/yew.rs | 202 ---------------- packages/stylist/src/global_style.rs | 216 ++++++++++++++++++ packages/stylist/src/lib.rs | 26 +-- packages/stylist/src/manager.rs | 19 +- packages/stylist/src/registry.rs | 14 +- packages/stylist/src/style.rs | 59 ++--- packages/stylist/src/yew.rs | 137 +++++++++++ 27 files changed, 518 insertions(+), 325 deletions(-) create mode 100644 packages/stylist-core/src/yew.rs delete mode 100644 packages/stylist/src/bindings/mod.rs delete mode 100644 packages/stylist/src/bindings/yew.rs create mode 100644 packages/stylist/src/global_style.rs create mode 100644 packages/stylist/src/yew.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e532120..01a6221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ and styled-components. ### Other Changes: -- Added a `` Component for global styling. +- Added a `GlobalStyle` struct to register global styles. +- Added a `` Component for global styling for yew applications. - Supported `@supports` CSS at-rule. - Added an alternative counter-based class name on the style when feature `random` is disabled. diff --git a/examples/benchmarks/src/main.rs b/examples/benchmarks/src/main.rs index d5b1dce..fc3d96a 100644 --- a/examples/benchmarks/src/main.rs +++ b/examples/benchmarks/src/main.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use gloo::timers::callback::Timeout; -use stylist::yew::GlobalStyle; +use stylist::yew::Global; use stylist::YieldStyle; use yew::prelude::*; @@ -11,7 +11,7 @@ mod benchmarks; mod utils; static GLOBAL_STYLE: &str = r#" - html&, body { + html, body { margin: 0; padding: 0; font-family: sans-serif; @@ -256,7 +256,7 @@ impl Component for App { fn view(&self) -> Html { html! { <> - +

{"Stylist Benchmark"}

{ diff --git a/examples/yew-integration/src/main.rs b/examples/yew-integration/src/main.rs index c105288..de44b51 100644 --- a/examples/yew-integration/src/main.rs +++ b/examples/yew-integration/src/main.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use stylist::yew::GlobalStyle; +use stylist::yew::Global; use stylist::{Style, YieldStyle}; use yew::{html, Component, ComponentLink, Html, ShouldRender}; @@ -100,8 +100,8 @@ impl Component for App { html! { <> // Global Styles can be applied with component. - Html { html! { <> - // Global Styles can be applied with component. - // Global Styles can be applied with component. - (&self, w: &mut W, class_name: &str) -> fmt::Result { + fn write_style(&self, w: &mut W, class_name: Option<&str>) -> fmt::Result { if !self.condition.is_empty() { for (index, sel) in self.condition.iter().enumerate() { sel.write_style(w, class_name)?; @@ -32,8 +32,11 @@ impl ToStyleStr for Block { } write!(w, " ")?; } + } else if let Some(m) = class_name { + write!(w, ".{} ", m)?; } else { - write!(w, ".{} ", class_name)?; + // Generates global style for dangling block. + write!(w, "html ")?; } writeln!(w, "{{")?; diff --git a/packages/stylist-core/src/ast/mod.rs b/packages/stylist-core/src/ast/mod.rs index e2d0737..a5dff30 100644 --- a/packages/stylist-core/src/ast/mod.rs +++ b/packages/stylist-core/src/ast/mod.rs @@ -4,7 +4,7 @@ //! struct Sheet //! └── Vec //! ├── struct Block -//! │ ├── selector: String +//! │ ├── selector: Vec //! │ └── Vec //! │ ├── key: String //! │ └── value: String @@ -75,7 +75,7 @@ width: 200px; }), ]); assert_eq!( - test_block.to_style_str("test"), + test_block.to_style_str(Some("test")), r#".test { width: 100vw; } @@ -133,7 +133,7 @@ width: 200px; .into(), })]); assert_eq!( - test_block.to_style_str("test"), + test_block.to_style_str(Some("test")), r#"@media only screen and (min-width: 1000px) { .test { width: 100vw; diff --git a/packages/stylist-core/src/ast/rule.rs b/packages/stylist-core/src/ast/rule.rs index ab450f9..645610b 100644 --- a/packages/stylist-core/src/ast/rule.rs +++ b/packages/stylist-core/src/ast/rule.rs @@ -25,7 +25,7 @@ pub struct Rule { } impl ToStyleStr for Rule { - fn write_style(&self, w: &mut W, class_name: &str) -> fmt::Result { + fn write_style(&self, w: &mut W, class_name: Option<&str>) -> fmt::Result { writeln!(w, "{} {{", self.condition)?; for i in self.content.iter() { diff --git a/packages/stylist-core/src/ast/rule_content.rs b/packages/stylist-core/src/ast/rule_content.rs index e2fe468..d102ffe 100644 --- a/packages/stylist-core/src/ast/rule_content.rs +++ b/packages/stylist-core/src/ast/rule_content.rs @@ -26,7 +26,7 @@ impl From for RuleContent { } impl ToStyleStr for RuleContent { - fn write_style(&self, w: &mut W, class_name: &str) -> fmt::Result { + fn write_style(&self, w: &mut W, class_name: Option<&str>) -> fmt::Result { match self { RuleContent::Block(ref b) => b.write_style(w, class_name), RuleContent::Rule(ref r) => r.write_style(w, class_name), diff --git a/packages/stylist-core/src/ast/scope_content.rs b/packages/stylist-core/src/ast/scope_content.rs index 0e4e532..ffe829a 100644 --- a/packages/stylist-core/src/ast/scope_content.rs +++ b/packages/stylist-core/src/ast/scope_content.rs @@ -31,7 +31,7 @@ pub enum ScopeContent { } impl ToStyleStr for ScopeContent { - fn write_style(&self, w: &mut W, class_name: &str) -> fmt::Result { + fn write_style(&self, w: &mut W, class_name: Option<&str>) -> fmt::Result { match self { ScopeContent::Block(ref b) => b.write_style(w, class_name), ScopeContent::Rule(ref r) => r.write_style(w, class_name), diff --git a/packages/stylist-core/src/ast/selector.rs b/packages/stylist-core/src/ast/selector.rs index 5201eab..ec54969 100644 --- a/packages/stylist-core/src/ast/selector.rs +++ b/packages/stylist-core/src/ast/selector.rs @@ -15,26 +15,35 @@ pub struct Selector { } impl ToStyleStr for Selector { - fn write_style(&self, w: &mut W, class_name: &str) -> fmt::Result { - // If contains current selector or root pseudo class, replace them with class name. - if self.inner.contains('&') || self.inner.contains(":root") { - let scoped_class = format!(".{}", class_name); - - write!( - w, - "{}", - self.inner - .replace("&", scoped_class.as_str()) - .replace(":root", scoped_class.as_str()) - )?; - - // If selector starts with a pseudo-class, apply it to the root element. - } else if self.inner.starts_with(':') { - write!(w, ".{}{}", class_name, self.inner)?; - - // For other selectors, scope it to be the children of the root element. + fn write_style(&self, w: &mut W, class_name: Option<&str>) -> fmt::Result { + if let Some(m) = class_name { + // If contains current selector or root pseudo class, replace them with class name. + if self.inner.contains('&') || self.inner.contains(":root") { + let scoped_class = format!(".{}", m); + + write!( + w, + "{}", + self.inner + .replace("&", scoped_class.as_str()) + .replace(":root", scoped_class.as_str()) + )?; + + // If selector starts with a pseudo-class, apply it to the root element. + } else if self.inner.starts_with(':') { + write!(w, ".{}{}", m, self.inner)?; + + // For other selectors, scope it to be the children of the root element. + } else { + write!(w, ".{} {}", m, self.inner)?; + } + + // For global styles, if it contains &, it will be replaced with html. + } else if self.inner.contains('&') { + write!(w, "{}", self.inner.replace("&", "html"))?; + // For other styles, it will be written as is. } else { - write!(w, ".{} {}", class_name, self.inner)?; + write!(w, "{}", self.inner)?; } Ok(()) @@ -55,7 +64,10 @@ mod tests { fn test_selector_gen_simple() { let s: Selector = ".abc".into(); - assert_eq!(s.to_style_str("stylist-abcdefgh"), ".stylist-abcdefgh .abc"); + assert_eq!( + s.to_style_str(Some("stylist-abcdefgh")), + ".stylist-abcdefgh .abc" + ); } #[test] @@ -63,7 +75,7 @@ mod tests { let s: Selector = ":hover".into(); assert_eq!( - s.to_style_str("stylist-abcdefgh"), + s.to_style_str(Some("stylist-abcdefgh")), ".stylist-abcdefgh:hover" ); } @@ -72,13 +84,19 @@ mod tests { fn test_selector_root_pseduo() { let s: Selector = ":root.big".into(); - assert_eq!(s.to_style_str("stylist-abcdefgh"), ".stylist-abcdefgh.big"); + assert_eq!( + s.to_style_str(Some("stylist-abcdefgh")), + ".stylist-abcdefgh.big" + ); } #[test] fn test_selector_gen_current() { let s: Selector = "&.big".into(); - assert_eq!(s.to_style_str("stylist-abcdefgh"), ".stylist-abcdefgh.big"); + assert_eq!( + s.to_style_str(Some("stylist-abcdefgh")), + ".stylist-abcdefgh.big" + ); } } diff --git a/packages/stylist-core/src/ast/sheet.rs b/packages/stylist-core/src/ast/sheet.rs index 497fb48..6dea6fd 100644 --- a/packages/stylist-core/src/ast/sheet.rs +++ b/packages/stylist-core/src/ast/sheet.rs @@ -58,7 +58,7 @@ impl Default for Sheet { } impl ToStyleStr for Sheet { - fn write_style(&self, w: &mut W, class_name: &str) -> fmt::Result { + fn write_style(&self, w: &mut W, class_name: Option<&str>) -> fmt::Result { for scope in self.0.iter() { scope.write_style(w, class_name)?; writeln!(w)?; diff --git a/packages/stylist-core/src/ast/style_attr.rs b/packages/stylist-core/src/ast/style_attr.rs index 180e768..029f6df 100644 --- a/packages/stylist-core/src/ast/style_attr.rs +++ b/packages/stylist-core/src/ast/style_attr.rs @@ -14,7 +14,7 @@ pub struct StyleAttribute { } impl ToStyleStr for StyleAttribute { - fn write_style(&self, w: &mut W, _class_name: &str) -> fmt::Result { + fn write_style(&self, w: &mut W, _class_name: Option<&str>) -> fmt::Result { write!(w, "{}: {};", self.key, self.value) } } diff --git a/packages/stylist-core/src/ast/to_style_str.rs b/packages/stylist-core/src/ast/to_style_str.rs index 1ed0224..48d44d1 100644 --- a/packages/stylist-core/src/ast/to_style_str.rs +++ b/packages/stylist-core/src/ast/to_style_str.rs @@ -3,12 +3,14 @@ use std::fmt; /// Structs implementing this trait should be able to turn into /// a part of a CSS style sheet. pub trait ToStyleStr { - fn to_style_str(&self, class_name: &str) -> String { + fn to_style_str(&self, class_name: Option<&str>) -> String { let mut s = String::new(); self.write_style(&mut s, class_name).unwrap(); s } - fn write_style(&self, w: &mut W, class_name: &str) -> fmt::Result; + + // If None is passed as class_name, it means to write a global style. + fn write_style(&self, w: &mut W, class_name: Option<&str>) -> fmt::Result; } diff --git a/packages/stylist-core/src/lib.rs b/packages/stylist-core/src/lib.rs index cff09a7..2d5e0c4 100644 --- a/packages/stylist-core/src/lib.rs +++ b/packages/stylist-core/src/lib.rs @@ -9,3 +9,7 @@ mod error; pub use error::{Error, Result}; pub mod ast; mod parser; + +#[cfg_attr(documenting, doc(cfg(feature = "yew_integration")))] +#[cfg(feature = "yew_integration")] +mod yew; diff --git a/packages/stylist-core/src/yew.rs b/packages/stylist-core/src/yew.rs new file mode 100644 index 0000000..f02f0e0 --- /dev/null +++ b/packages/stylist-core/src/yew.rs @@ -0,0 +1,29 @@ +use std::borrow::Cow; + +use yew::html::IntoPropValue; + +use crate::ast::Sheet; + +impl<'a> IntoPropValue for String { + fn into_prop_value(self) -> Sheet { + self.parse::().expect("Failed to parse style.") + } +} + +impl<'a> IntoPropValue for &'a str { + fn into_prop_value(self) -> Sheet { + self.parse::().expect("Failed to parse style.") + } +} + +impl<'a> IntoPropValue for Cow<'a, str> { + fn into_prop_value(self) -> Sheet { + self.parse::().expect("Failed to parse style.") + } +} + +impl<'a> IntoPropValue for &'a Sheet { + fn into_prop_value(self) -> Sheet { + self.clone() + } +} diff --git a/packages/stylist/Cargo.toml b/packages/stylist/Cargo.toml index 610cec6..4cdcebc 100644 --- a/packages/stylist/Cargo.toml +++ b/packages/stylist/Cargo.toml @@ -50,7 +50,7 @@ env_logger = "0.9" [features] random = ["rand", "getrandom"] default = ["random"] -yew_integration = ["yew"] +yew_integration = ["yew", "stylist-core/yew_integration"] [package.metadata.docs.rs] features = ["yew_integration"] diff --git a/packages/stylist/src/bindings/mod.rs b/packages/stylist/src/bindings/mod.rs deleted file mode 100644 index 1b8927e..0000000 --- a/packages/stylist/src/bindings/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © 2020 Lukas Wagner - -#[cfg_attr(documenting, doc(cfg(feature = "yew_integration")))] -#[cfg(feature = "yew_integration")] -pub mod yew; diff --git a/packages/stylist/src/bindings/yew.rs b/packages/stylist/src/bindings/yew.rs deleted file mode 100644 index e73efd7..0000000 --- a/packages/stylist/src/bindings/yew.rs +++ /dev/null @@ -1,202 +0,0 @@ -//! This module contains yew specific features. - -use std::borrow::Cow; - -use yew::html::Classes; -use yew::html::IntoPropValue; -use yew::prelude::*; - -#[cfg(target_arch = "wasm32")] -use crate::arch::document; -use crate::Style; - -impl From