Skip to main content

maud_extensions/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3#![deny(rustdoc::broken_intra_doc_links)]
4//! Tiny Maud superpowers.
5//!
6//! This crate is the public runtime/support surface:
7//! - reexports proc macros like [`css!`] and [`js!`]
8//! - optionally reexports the experimental [`Component`] derive
9//! - optionally reexports the experimental [`component`] impl macro
10//! - provides runtime slot wrapper types like [`Slot`] and [`Slots`]
11//! - reexports `bon` so generated component code can depend only on
12//!   `maud-extensions`
13//!
14//! Recommended dependency spelling:
15//!
16//! ```toml
17//! [dependencies]
18//! mx = { package = "maud-extensions", version = "0.6.2", features = ["components"] }
19//! ```
20//!
21//! # Experimental components
22//!
23//! The component system is currently opt-in behind the `components` feature.
24//! The macro that turns impl-local component rendering and asset blocks on is:
25//!
26//! ```ignore
27//! #[mx::component]
28//! impl Card {
29//!     render! { ... }
30//!     css! { ... }
31//!     js!(once, { ... });
32//! }
33//! ```
34//!
35//! That impl macro is what makes `render!`, `css!`, and `js!` part of the
36//! component render pipeline.
37//!
38//! The preferred authoring pattern is:
39//!
40//! - `#[derive(Component)]` on the struct
41//! - `Slot<maud::Markup>` / `Slot<Vec<maud::Markup>>` for slot fields
42//! - `#[mx(default)]` on the single default slot
43//! - `#[mx::component]` on the inherent impl block
44//! - `render! { ... }` for the component root
45//! - optional colocated `css! { ... }` and `js!(once, { ... })` blocks
46//!
47//! The builder `.render()` path uses the hidden [`ComponentRender`] hook and
48//! currently auto-injects impl-local CSS and JS into the rendered root.
49//!
50//! ```ignore
51//! use maud::Markup;
52//! use mx::{Component, Slot};
53//!
54//! #[derive(Component)]
55//! struct Card {
56//!     title: String,
57//!     header: Slot<Markup>,
58//!     #[mx(default)]
59//!     body: Slot<Markup>,
60//!     footer: Slot<Markup>,
61//!     #[mx(each = action)]
62//!     actions: Slot<Vec<Markup>>,
63//! }
64//!
65//! #[mx::component]
66//! impl Card {
67//!     css! {
68//!         me {
69//!             padding: 1rem;
70//!             border: 1px solid #ddd;
71//!         }
72//!     }
73//!
74//!     js!(once, {
75//!         me().class_add("ready");
76//!     });
77//!
78//!     render! {
79//!         article.card {
80//!             header class="header" { (self.header) }
81//!             h2 { (self.title) }
82//!             div.body { (self.body) }
83//!             footer class="footer" { (self.footer) }
84//!             div.actions { (self.actions) }
85//!         }
86//!     }
87//! }
88//! ```
89//!
90//! The broader browser-side runtime pieces this layers on top of today include:
91//!
92//! - Surreal: <https://github.com/gnat/surreal>
93//! - css-scope-inline: <https://github.com/gnat/css-scope-inline>
94//! - Preact Signals: <https://github.com/preactjs/signals>
95
96extern crate self as maud_extensions;
97
98mod slot;
99
100pub use maud_extensions_macros::{css, js};
101pub use slot::{Slot, Slots};
102
103#[cfg(feature = "components")]
104pub use maud_extensions_macros::Component;
105#[cfg(feature = "components")]
106pub use maud_extensions_macros::component;
107
108#[doc(hidden)]
109pub use bon;
110
111/// Hidden render hook used by the component builder render path.
112#[doc(hidden)]
113pub trait ComponentRender {
114    /// Renders the fully assembled component, including any impl-local facets.
115    fn __mx_render(&self) -> maud::Markup;
116}