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}