diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6c3004e..6196e18 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -182,6 +182,7 @@ jobs: stellation-backend-tower stellation-backend-cli stellation-frontend + stellation-stylist stctl stellation ) diff --git a/Cargo.lock b/Cargo.lock index c686f9f..9695ac6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -506,6 +506,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -568,6 +574,7 @@ dependencies = [ "example-fullstack-view", "gloo", "stellation-frontend", + "stellation-stylist", "tracing", "tracing-subscriber", "yew", @@ -584,6 +591,7 @@ dependencies = [ "stellation-backend", "stellation-backend-cli", "stellation-backend-tower", + "stellation-stylist", "tokio", "tracing", "yew", @@ -596,6 +604,7 @@ dependencies = [ "bounce", "example-fullstack-api", "stellation-bridge", + "stylist", "time", "tracing", "web-sys", @@ -611,6 +620,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "filetime" version = "0.2.21" @@ -1225,6 +1240,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -1255,6 +1273,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -1348,6 +1375,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +[[package]] +name = "litrs" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f17c3668f3cc1132437cdadc93dab05e52d592f06948d3f64828430c36e4a70" +dependencies = [ + "proc-macro2", +] + [[package]] name = "lock_api" version = "0.4.10" @@ -1430,6 +1466,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1481,6 +1523,16 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "notify" version = "6.0.1" @@ -2571,12 +2623,70 @@ dependencies = [ "yew-router", ] +[[package]] +name = "stellation-stylist" +version = "0.2.0" +dependencies = [ + "stellation-backend", + "stylist", + "yew", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "stylist" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d46ee215e84089a9f648d5d77fb21fa5c34f187b88377e9c080ee41940dc9c33" +dependencies = [ + "fastrand 2.0.0", + "gloo-events", + "html-escape", + "instant", + "once_cell", + "serde", + "stylist-core", + "stylist-macros", + "wasm-bindgen", + "web-sys", + "yew", +] + +[[package]] +name = "stylist-core" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "805ae52bc4eb164abd0953acdcacca2eefae3e39b21cbc6616ce9ac3c5d4c222" +dependencies = [ + "nom", + "once_cell", + "serde", + "thiserror", + "wasm-bindgen", +] + +[[package]] +name = "stylist-macros" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a37d492603237395c6d8367ae9d3bd473726c9ec0daf238b1fc58a4c6d087ec" +dependencies = [ + "itertools", + "litrs", + "log", + "nom", + "proc-macro-error", + "proc-macro2", + "quote", + "stylist-core", + "syn 1.0.109", +] + [[package]] name = "syn" version = "1.0.109" @@ -2607,7 +2717,7 @@ checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ "autocfg", "cfg-if", - "fastrand", + "fastrand 1.9.0", "redox_syscall 0.3.5", "rustix 0.37.21", "windows-sys 0.48.0", diff --git a/crates/stellation-backend/src/hooks.rs b/crates/stellation-backend/src/hooks.rs new file mode 100644 index 0000000..648976f --- /dev/null +++ b/crates/stellation-backend/src/hooks.rs @@ -0,0 +1,76 @@ +//! Useful hooks for stellation backend. + +use std::cell::RefCell; +use std::fmt; +use std::rc::Rc; + +use bounce::{use_atom_value, Atom}; +use futures::future::LocalBoxFuture; +use futures::{Future, FutureExt}; +use yew::prelude::*; + +type RenderAppendHead = Box LocalBoxFuture<'static, String>>; + +#[derive(Atom, Clone)] +pub(crate) struct HeadContents { + inner: Rc>>, +} + +impl Default for HeadContents { + fn default() -> Self { + panic!("Attempting to use use_append_head_content on client side rendering!"); + } +} + +impl PartialEq for HeadContents { + // We never set this atom, so it will always be equal. + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl Eq for HeadContents {} + +impl HeadContents { + pub(crate) fn new() -> Self { + Self { + inner: Rc::default(), + } + } + + pub(crate) async fn render_into(&self, w: &mut dyn fmt::Write) { + for i in self.inner.take() { + let _ = write!(w, "{}", i().await); + } + } +} + +/// A server-side hook that appends content to head element. +/// This async function is resolved after the page is completed rendered and the returned string is +/// appended at the location of the ` ` comment in `index.html`, after other +/// contents. +/// +/// # Warning +/// +/// The content is not managed at the client side. +/// This hook is used to facility specific crates such as a CSS-in-Rust solution. +/// +/// If you wish to render content into the `` element, you should use +/// [`bounce::helmet::Helmet`]. +/// +/// +/// # Panics +/// +/// This hook should be used by a server-side only component. Panics if used in client-side +/// rendering. +#[hook] +pub fn use_append_head_content(f: F) +where + F: 'static + FnOnce() -> Fut, + Fut: 'static + Future, +{ + let boxed_f: RenderAppendHead = Box::new(move || f().boxed_local()); + let head_contents = use_atom_value::(); + + head_contents.inner.borrow_mut().push(boxed_f); +} diff --git a/crates/stellation-backend/src/lib.rs b/crates/stellation-backend/src/lib.rs index 1bbec8f..097d79c 100644 --- a/crates/stellation-backend/src/lib.rs +++ b/crates/stellation-backend/src/lib.rs @@ -22,4 +22,5 @@ mod request; pub use request::{RenderRequest, Request}; mod renderer; pub use renderer::ServerRenderer; +pub mod hooks; mod html; diff --git a/crates/stellation-backend/src/renderer.rs b/crates/stellation-backend/src/renderer.rs index 57a294e..27cfebc 100644 --- a/crates/stellation-backend/src/renderer.rs +++ b/crates/stellation-backend/src/renderer.rs @@ -8,6 +8,7 @@ use stellation_bridge::links::{Link, PhantomLink}; use stellation_bridge::Bridge; use yew::BaseComponent; +use crate::hooks::HeadContents; use crate::request::RenderRequest; use crate::root::{StellationRoot, StellationRootProps}; use crate::{html, ServerAppProps}; @@ -92,6 +93,8 @@ where let request: Rc<_> = request.into(); if !request.is_client_only() { + let head_contents = HeadContents::new(); + let (reader, writer) = render_static(); let props = ServerAppProps::from_request(request.clone()); @@ -101,6 +104,7 @@ where server_app_props: props, helmet_writer: writer, bridge, + head_contents: head_contents.clone(), }, ) .render() @@ -111,6 +115,8 @@ where &mut head_s, r#""# ); + + head_contents.render_into(&mut head_s).await; } html::format_html(request.template(), helmet_tags, head_s, body_s).await diff --git a/crates/stellation-backend/src/root.rs b/crates/stellation-backend/src/root.rs index 03a2b2c..6a1cfc7 100644 --- a/crates/stellation-backend/src/root.rs +++ b/crates/stellation-backend/src/root.rs @@ -10,6 +10,7 @@ use yew::prelude::*; use yew_router::history::{AnyHistory, History, MemoryHistory}; use yew_router::Router; +use crate::hooks::HeadContents; use crate::props::ServerAppProps; use crate::Request; @@ -21,6 +22,7 @@ where pub helmet_writer: StaticWriter, pub server_app_props: ServerAppProps, pub bridge: Option>, + pub head_contents: HeadContents, } impl PartialEq for StellationRootProps @@ -43,6 +45,7 @@ where helmet_writer: self.helmet_writer.clone(), server_app_props: self.server_app_props.clone(), bridge: self.bridge.clone(), + head_contents: self.head_contents.clone(), } } } @@ -59,12 +62,14 @@ where helmet_writer, server_app_props, bridge, + head_contents, .. } = props.clone(); let get_init_states = use_callback( move |_, bridge| { let mut states = AnyMap::new(); + states.insert(head_contents.clone()); if let Some(m) = bridge.clone().map(BridgeState::from_bridge) { states.insert(m); } diff --git a/crates/stellation-stylist/Cargo.toml b/crates/stellation-stylist/Cargo.toml new file mode 100644 index 0000000..2c292e1 --- /dev/null +++ b/crates/stellation-stylist/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "stellation-stylist" +version = "0.2.0" +edition = "2021" +rust-version = "1.66" +repository = "https://github.com/futursolo/stellation" +authors = [ + "Kaede Hoshiakwa ", +] +description = "The framework experience for Yew." +keywords = ["web", "wasm", "yew", "framework", "ssr"] +categories = ["wasm", "web-programming"] +readme = "../../README.md" +homepage = "https://github.com/futursolo/stellation" +license = "MIT OR Apache-2.0" + +[dependencies] +stylist = { version = "0.12.1", features = ["yew_integration"] } +yew = "0.20" +stellation-backend = { version = "0.2.0", path = "../stellation-backend", optional = true } + +[features] +frontend = ["stylist/hydration"] +backend = ["stylist/ssr", "dep:stellation-backend"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "documenting"] diff --git a/crates/stellation-stylist/src/backend.rs b/crates/stellation-stylist/src/backend.rs new file mode 100644 index 0000000..0752c7a --- /dev/null +++ b/crates/stellation-stylist/src/backend.rs @@ -0,0 +1,59 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use stellation_backend::hooks::use_append_head_content; +use stylist::manager::{render_static, StyleManager}; +use stylist::yew::ManagerProvider; +use yew::html::ChildrenProps; +use yew::prelude::*; + +/// A Stylist [`ManagerProvider`] that writes server-side styles to +/// [`ServerRenderer`](stellation_backend::ServerRenderer) automatically. +/// +/// # Panics +/// +/// This provider should be used in the server app instance. Using this component in the client app +/// will panic. +/// +/// This provider requires a [`FrontendManagerProvider`](crate::FrontendManagerProvider) to be +/// placed in the client app or hydration will fail. +/// +/// You can check out this [example](https://github.com/futursolo/stellation/blob/main/examples/fullstack/server/src/app.rs) for how to use this provider. +#[function_component] +pub fn BackendManagerProvider(props: &ChildrenProps) -> Html { + let (reader, manager) = use_memo( + |_| { + let (writer, reader) = render_static(); + + let style_mgr = StyleManager::builder() + .writer(writer) + .build() + .expect("failed to create style manager."); + + (Rc::new(RefCell::new(Some(reader))), style_mgr) + }, + (), + ) + .as_ref() + .to_owned(); + + use_append_head_content(move || async move { + let style_data = reader + .borrow_mut() + .take() + .expect("reader is called twice!") + .read_style_data(); + + let mut s = String::new(); + // Write to a String can never fail. + let _ = style_data.write_static_markup(&mut s); + + s + }); + + html! { + + {props.children.clone()} + + } +} diff --git a/crates/stellation-stylist/src/frontend.rs b/crates/stellation-stylist/src/frontend.rs new file mode 100644 index 0000000..a84aa20 --- /dev/null +++ b/crates/stellation-stylist/src/frontend.rs @@ -0,0 +1,29 @@ +use stylist::manager::StyleManager; +use stylist::yew::ManagerProvider; +use yew::html::ChildrenProps; +use yew::prelude::*; + +/// A Stylist [`ManagerProvider`] that hydrates styles from SSR automatically. +/// This provider should be used in the client app instance. +/// +/// # Panics +/// +/// This provider requires a [`BackendManagerProvider`](crate::BackendManagerProvider) to be +/// placed in the server app or hydration will fail. +/// +/// You can check out this [example](https://github.com/futursolo/stellation/blob/main/examples/fullstack/client/src/app.rs) for how to use this provider. +#[function_component] +pub fn FrontendManagerProvider(props: &ChildrenProps) -> Html { + let manager = use_memo( + |_| StyleManager::new().expect("failed to create style manager."), + (), + ) + .as_ref() + .to_owned(); + + html! { + + {props.children.clone()} + + } +} diff --git a/crates/stellation-stylist/src/lib.rs b/crates/stellation-stylist/src/lib.rs new file mode 100644 index 0000000..58e8231 --- /dev/null +++ b/crates/stellation-stylist/src/lib.rs @@ -0,0 +1,23 @@ +//! The stylist integration for stellation. +//! +//! You can check out this [example](https://github.com/futursolo/stellation/tree/main/examples/fullstack) for how to use this crate. + +#![deny(clippy::all)] +#![deny(missing_debug_implementations)] +#![deny(unsafe_code)] +#![deny(non_snake_case)] +#![deny(clippy::cognitive_complexity)] +#![deny(missing_docs)] +#![cfg_attr(documenting, feature(doc_cfg))] +#![cfg_attr(documenting, feature(doc_auto_cfg))] +#![cfg_attr(any(releasing, not(debug_assertions)), deny(dead_code, unused_imports))] + +#[cfg(feature = "backend")] +mod backend; +#[cfg(feature = "frontend")] +mod frontend; + +#[cfg(feature = "backend")] +pub use backend::BackendManagerProvider; +#[cfg(feature = "frontend")] +pub use frontend::FrontendManagerProvider; diff --git a/examples/fullstack/client/Cargo.toml b/examples/fullstack/client/Cargo.toml index a2a0e75..87749fc 100644 --- a/examples/fullstack/client/Cargo.toml +++ b/examples/fullstack/client/Cargo.toml @@ -7,10 +7,13 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -stellation-frontend = { version = "0.2.0", path = "../../../crates/stellation-frontend" } yew = "0.20.0" gloo = "0.8" +# Stellation Components +stellation-frontend = { version = "0.2.0", path = "../../../crates/stellation-frontend" } +stellation-stylist = { version = "0.2.0", path = "../../../crates/stellation-stylist", features = ["frontend"] } + # Logging tracing = "0.1" tracing-subscriber = { version = "0.3.17", default-features = false, features = ["time", "std", "fmt", "ansi"] } diff --git a/examples/fullstack/client/src/app.rs b/examples/fullstack/client/src/app.rs index ff11ebe..2277a2f 100644 --- a/examples/fullstack/client/src/app.rs +++ b/examples/fullstack/client/src/app.rs @@ -1,9 +1,14 @@ use example_fullstack_view::Main; +use stellation_stylist::FrontendManagerProvider; use yew::prelude::*; #[function_component] pub fn App() -> Html { html! { -
+ + +
+ + } } diff --git a/examples/fullstack/index.html b/examples/fullstack/index.html index e783fbe..3dceb3b 100644 --- a/examples/fullstack/index.html +++ b/examples/fullstack/index.html @@ -4,7 +4,6 @@ - diff --git a/examples/fullstack/index.scss b/examples/fullstack/index.scss deleted file mode 100644 index 31bfcb8..0000000 --- a/examples/fullstack/index.scss +++ /dev/null @@ -1,112 +0,0 @@ -html, -body { - margin: 0; - padding: 0; - - font-family: Verdana, Geneva, Tahoma, sans-serif; - font-size: 15px; -} - -@media (prefers-color-scheme: dark) { - html { - background-color: rgb(50, 50, 50); - color: white; - } -} - -.container { - height: 100vh; - width: 100%; - - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.title { - font-size: 2rem; - line-height: 1.5em; -} - -.greeting-container { - padding-top: 2rem; - max-width: 300px; - width: calc(100% - 20px); -} - -.greeting-input { - width: 100%; - height: 40px; - - display: block; - box-sizing: border-box; - - border-radius: 8px; - - background-color: rgb(230, 226, 245); - color: rgb(0, 0, 0); - - border: 0; - outline: 0; - padding-left: 1rem; - padding-right: 1rem; - - font-size: 1rem; - - &::-webkit-input-placeholder, - &::-moz-placeholder, - &:-moz-placeholder, - &:-ms-input-placeholder { - color: rgb(206, 206, 206); - } -} - - -.greeting-button { - width: 100%; - height: 40px; - - margin: 0; - padding: 0; - margin-top: 1rem; - - display: block; - - border-radius: 8px; - - background-color: rgb(132, 112, 198); - color: white; - border: 0; - - cursor: pointer; - - font-size: 1rem; - font-weight: bold; -} - -.greeting-message { - padding-top: 1rem; - height: 2rem; - box-sizing: border-box; - text-align: center; -} - -@media (prefers-color-scheme: dark) { - .greeting-input { - background-color: rgb(87, 86, 91); - color: white; - - - &::-webkit-input-placeholder, - &::-moz-placeholder, - &:-moz-placeholder, - &:-ms-input-placeholder { - color: rgb(181, 181, 181); - } - } - - .greeting-button { - background-color: rgb(95, 76, 159); - } -} diff --git a/examples/fullstack/server/Cargo.toml b/examples/fullstack/server/Cargo.toml index 37c9e34..54d29e2 100644 --- a/examples/fullstack/server/Cargo.toml +++ b/examples/fullstack/server/Cargo.toml @@ -16,6 +16,7 @@ yew = "0.20.0" stellation-backend = { version = "0.2.0", path = "../../../crates/stellation-backend" } stellation-backend-tower = { version = "0.2.0", path = "../../../crates/stellation-backend-tower" } stellation-backend-cli = { version = "0.2.0", path = "../../../crates/stellation-backend-cli" } +stellation-stylist = { version = "0.2.0", path = "../../../crates/stellation-stylist", features = ["backend"] } # Example Workspace example-fullstack-view = { path = "../view" } diff --git a/examples/fullstack/server/src/app.rs b/examples/fullstack/server/src/app.rs index fe07b4b..c38e117 100644 --- a/examples/fullstack/server/src/app.rs +++ b/examples/fullstack/server/src/app.rs @@ -1,5 +1,6 @@ use example_fullstack_view::Main; use stellation_backend::{Request, ServerAppProps}; +use stellation_stylist::BackendManagerProvider; use yew::prelude::*; #[function_component] @@ -8,6 +9,10 @@ where REQ: Request, { html! { -
+ + +
+ + } } diff --git a/examples/fullstack/view/Cargo.toml b/examples/fullstack/view/Cargo.toml index f523c3c..be7853e 100644 --- a/examples/fullstack/view/Cargo.toml +++ b/examples/fullstack/view/Cargo.toml @@ -13,6 +13,7 @@ stellation-bridge = { version = "0.2.0", path = "../../../crates/stellation-brid time = { version = "0.3", features = ["wasm-bindgen", "serde-human-readable", "macros"] } tracing = "0.1.37" bounce = { version = "0.7.0", features = ["helmet"] } +stylist = { version = "0.12.1", features = ["yew_integration"] } [dependencies.web-sys] version = "0.3" diff --git a/examples/fullstack/view/src/lib.rs b/examples/fullstack/view/src/lib.rs index b1b58b3..9691e8d 100644 --- a/examples/fullstack/view/src/lib.rs +++ b/examples/fullstack/view/src/lib.rs @@ -2,22 +2,53 @@ #![deny(missing_debug_implementations)] use bounce::helmet::Helmet; +use stylist::yew::{styled_component, Global}; use yew::prelude::*; mod pages; use pages::{Greeting, ServerTime}; -#[function_component] +#[styled_component] pub fn Main() -> Html { let fallback = html! {
{"Loading..."}
}; html! { <> + {"Welcome to Stellation!"} -
-
{"Welcome to Stellation!"}
+
+
+ {"Welcome to Stellation!"} +
diff --git a/examples/fullstack/view/src/pages/greeting.rs b/examples/fullstack/view/src/pages/greeting.rs index 0c9c141..a985ad1 100644 --- a/examples/fullstack/view/src/pages/greeting.rs +++ b/examples/fullstack/view/src/pages/greeting.rs @@ -1,9 +1,10 @@ use example_fullstack_api::{Bridge, GreetingMutation}; +use stylist::yew::styled_component; use web_sys::HtmlInputElement; use yew::platform::spawn_local; use yew::prelude::*; -#[function_component] +#[styled_component] pub fn Greeting() -> Html { let handle = Bridge::use_mutation::(); @@ -48,10 +49,52 @@ pub fn Greeting() -> Html { }; html! { -
+
Html { {oninput} ref={input_ref} /> - +
-
{message}
+
{message}
} } diff --git a/examples/fullstack/view/src/pages/server_time.rs b/examples/fullstack/view/src/pages/server_time.rs index 5e20a93..4047507 100644 --- a/examples/fullstack/view/src/pages/server_time.rs +++ b/examples/fullstack/view/src/pages/server_time.rs @@ -34,12 +34,12 @@ pub fn ServerTime() -> HtmlResult { .expect("failed to format time!"), Err(_) => { return Ok(html! { -
{"Waiting for Server..."}
+
{"Waiting for Server..."}
}) } }; Ok(html! { -
{"Server Time: "}{server_time}
+
{"Server Time: "}{server_time}
}) } diff --git a/templates/README.md b/templates/README.md index 33f6ade..e2177c2 100644 --- a/templates/README.md +++ b/templates/README.md @@ -3,5 +3,5 @@ To create a stellation project from one of the templates, use the following command: ```bash - $ cargo generate --git https://github.com/stellation-templates + $ cargo generate --git https://github.com/futursolo/stellation-templates ``` diff --git a/templates/default/crates/client/Cargo.toml.liquid b/templates/default/crates/client/Cargo.toml.liquid index ffb745a..6d6dc6f 100644 --- a/templates/default/crates/client/Cargo.toml.liquid +++ b/templates/default/crates/client/Cargo.toml.liquid @@ -13,14 +13,17 @@ gloo = "0.8" {% if stellation_target == "release" %} # Stellation stellation-frontend = "{{stellation_release_ver}}" +stellation-stylist = { version = "{{stellation_release_ver}}", features = ["frontend"] } {% elsif stellation_target == "main" %} # Stellation stellation-frontend = { git = "https://github.com/futursolo/stellation" } +stellation-stylist = { git = "https://github.com/futursolo/stellation", features = ["frontend"] } {% elsif stellation_target == "ci" %} # Stellation stellation-frontend = { path = "../../../../stellation/crates/stellation-frontend" } +stellation-stylist = { path = "../../../../stellation/crates/stellation-stylist", features = ["frontend"] } {% endif %} # Logging diff --git a/templates/default/crates/client/src/app.rs b/templates/default/crates/client/src/app.rs index 0a8e279..8d2f853 100644 --- a/templates/default/crates/client/src/app.rs +++ b/templates/default/crates/client/src/app.rs @@ -1,3 +1,4 @@ +use stellation_stylist::FrontendManagerProvider; use yew::prelude::*; use crate::view::Main; @@ -5,6 +6,10 @@ use crate::view::Main; #[function_component] pub fn App() -> Html { html! { -
+ + +
+ + } } diff --git a/templates/default/crates/server/Cargo.toml.liquid b/templates/default/crates/server/Cargo.toml.liquid index da92658..f5dc13f 100644 --- a/templates/default/crates/server/Cargo.toml.liquid +++ b/templates/default/crates/server/Cargo.toml.liquid @@ -18,18 +18,21 @@ rust-embed = { version = "6.4.2", features = ["interpolate-folder-path"] } stellation-backend = { version = "{{stellation_release_ver}}" } stellation-backend-tower = { version = "{{stellation_release_ver}}" } stellation-backend-cli = { version = "{{stellation_release_ver}}" } +stellation-stylist = { version = "{{stellation_release_ver}}", features = ["backend"] } {% elsif stellation_target == "main" %} # Stellation stellation-backend = { git = "https://github.com/futursolo/stellation" } stellation-backend-tower = { git = "https://github.com/futursolo/stellation" } stellation-backend-cli = { git = "https://github.com/futursolo/stellation" } +stellation-stylist = { git = "https://github.com/futursolo/stellation", features = ["backend"] } {% elsif stellation_target == "ci" %} # Stellation stellation-backend = { path = "../../../../stellation/crates/stellation-backend" } stellation-backend-tower = { path = "../../../../stellation/crates/stellation-backend-tower" } stellation-backend-cli = { path = "../../../../stellation/crates/stellation-backend-cli" } +stellation-stylist = { path = "../../../../stellation/crates/stellation-stylist", features = ["backend"] } {% endif %} # Example Workspace diff --git a/templates/default/crates/server/src/app.rs b/templates/default/crates/server/src/app.rs index d504e4c..4221715 100644 --- a/templates/default/crates/server/src/app.rs +++ b/templates/default/crates/server/src/app.rs @@ -1,4 +1,5 @@ use stellation_backend::{Request, ServerAppProps}; +use stellation_stylist::BackendManagerProvider; use yew::prelude::*; use crate::view::Main; @@ -9,6 +10,10 @@ where REQ: Request, { html! { -
+ + +
+ + } } diff --git a/templates/default/crates/view/Cargo.toml.liquid b/templates/default/crates/view/Cargo.toml.liquid index a47bfd4..90e21d7 100644 --- a/templates/default/crates/view/Cargo.toml.liquid +++ b/templates/default/crates/view/Cargo.toml.liquid @@ -12,6 +12,7 @@ yew = { version = "0.20.0" } time = { version = "0.3", features = ["wasm-bindgen", "serde-human-readable", "macros"] } tracing = "0.1.37" bounce = { version = "0.6.0", features = ["helmet"] } +stylist = { version = "0.12.0", features = ["yew_integration"] } {% if stellation_target == "release" %} # Stellation diff --git a/templates/default/crates/view/src/lib.rs.liquid b/templates/default/crates/view/src/lib.rs.liquid index 2b47e00..4a50c0a 100644 --- a/templates/default/crates/view/src/lib.rs.liquid +++ b/templates/default/crates/view/src/lib.rs.liquid @@ -3,22 +3,53 @@ use bounce::helmet::Helmet; use yew::prelude::*; +use stylist::yew::{styled_component, Global}; use {{crate_name}}_api as api; mod pages; use pages::{Greeting, ServerTime}; -#[function_component] +#[styled_component] pub fn Main() -> Html { let fallback = html! {
{"Loading..."}
}; html! { <> + {"Welcome to Stellation!"} -
-
{"Welcome to Stellation!"}
+
+
+ {"Welcome to Stellation!"} +
diff --git a/templates/default/crates/view/src/pages/greeting.rs b/templates/default/crates/view/src/pages/greeting.rs index 9732795..3d82f69 100644 --- a/templates/default/crates/view/src/pages/greeting.rs +++ b/templates/default/crates/view/src/pages/greeting.rs @@ -1,10 +1,11 @@ +use stylist::yew::styled_component; use web_sys::HtmlInputElement; use yew::platform::spawn_local; use yew::prelude::*; use crate::api::{Bridge, GreetingMutation}; -#[function_component] +#[styled_component] pub fn Greeting() -> Html { let handle = Bridge::use_mutation::(); @@ -49,10 +50,52 @@ pub fn Greeting() -> Html { }; html! { -
+
Html { {oninput} ref={input_ref} /> - +
-
{message}
+
{message}
} } diff --git a/templates/default/crates/view/src/pages/server_time.rs b/templates/default/crates/view/src/pages/server_time.rs index d52fa71..7968381 100644 --- a/templates/default/crates/view/src/pages/server_time.rs +++ b/templates/default/crates/view/src/pages/server_time.rs @@ -35,12 +35,12 @@ pub fn ServerTime() -> HtmlResult { .expect("failed to format time!"), Err(_) => { return Ok(html! { -
{"Waiting for Server..."}
+
{"Waiting for Server..."}
}) } }; Ok(html! { -
{"Server Time: "}{server_time}
+
{"Server Time: "}{server_time}
}) } diff --git a/templates/default/index.html.liquid b/templates/default/index.html.liquid index d1c0b6b..6d063ce 100644 --- a/templates/default/index.html.liquid +++ b/templates/default/index.html.liquid @@ -4,7 +4,6 @@ - diff --git a/templates/default/index.scss b/templates/default/index.scss deleted file mode 100644 index 31bfcb8..0000000 --- a/templates/default/index.scss +++ /dev/null @@ -1,112 +0,0 @@ -html, -body { - margin: 0; - padding: 0; - - font-family: Verdana, Geneva, Tahoma, sans-serif; - font-size: 15px; -} - -@media (prefers-color-scheme: dark) { - html { - background-color: rgb(50, 50, 50); - color: white; - } -} - -.container { - height: 100vh; - width: 100%; - - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.title { - font-size: 2rem; - line-height: 1.5em; -} - -.greeting-container { - padding-top: 2rem; - max-width: 300px; - width: calc(100% - 20px); -} - -.greeting-input { - width: 100%; - height: 40px; - - display: block; - box-sizing: border-box; - - border-radius: 8px; - - background-color: rgb(230, 226, 245); - color: rgb(0, 0, 0); - - border: 0; - outline: 0; - padding-left: 1rem; - padding-right: 1rem; - - font-size: 1rem; - - &::-webkit-input-placeholder, - &::-moz-placeholder, - &:-moz-placeholder, - &:-ms-input-placeholder { - color: rgb(206, 206, 206); - } -} - - -.greeting-button { - width: 100%; - height: 40px; - - margin: 0; - padding: 0; - margin-top: 1rem; - - display: block; - - border-radius: 8px; - - background-color: rgb(132, 112, 198); - color: white; - border: 0; - - cursor: pointer; - - font-size: 1rem; - font-weight: bold; -} - -.greeting-message { - padding-top: 1rem; - height: 2rem; - box-sizing: border-box; - text-align: center; -} - -@media (prefers-color-scheme: dark) { - .greeting-input { - background-color: rgb(87, 86, 91); - color: white; - - - &::-webkit-input-placeholder, - &::-moz-placeholder, - &:-moz-placeholder, - &:-ms-input-placeholder { - color: rgb(181, 181, 181); - } - } - - .greeting-button { - background-color: rgb(95, 76, 159); - } -}