1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! Maud page templates and template registration.
//!
//! # Page templates (`templates.rs`)
//!
//! In Lariv, a "page" is a Rust struct implementing [`RenderTemplate`](crate::template::RenderTemplate).
//! Pages return [`maud::Markup`] — HTML built at compile time, not from external template files
//! (though the website plugin also supports Minijinja for CMS content).
//!
//! # Basic page
//!
//! ```ignore
//! use maud::{Markup, html};
//! use lariv_rs::{
//! components::ShellChrome,
//! template::RenderTemplate,
//! };
//!
//! pub struct GreetingPage {
//! pub name: String,
//! }
//!
//! impl RenderTemplate for GreetingPage {
//! fn render(&self, chrome: &ShellChrome) -> Markup {
//! html! {
//! div class="container mx-auto" {
//! h1 class="text-2xl font-bold" { "Hello, " (self.name) "!" }
//! }
//! }
//! }
//! }
//! ```
//!
//! # HTMX partials
//!
//! For pages that support HTMX app-layout navigation, also implement
//! [`RenderAppPane`](crate::template::RenderAppPane):
//!
//! ```ignore
//! impl RenderAppPane for GreetingPage {
//! fn render_pane(&self) -> crate::components::AppLayoutHtml {
//! crate::components::layout_sidebar(crate::components::LayoutSidebar {
//! sidebar: my_sidebar(),
//! breadcrumbs: crate::components::breadcrumbs(&[crate::components::Crumb {
//! label: "My App",
//! href: None,
//! }]),
//! content: self.page_body(),
//! })
//! }
//!
//! fn render_main(&self) -> crate::components::MainContentHtml {
//! crate::components::layout_main(crate::components::LayoutMain {
//! breadcrumbs: maud::Markup::default(),
//! content: self.page_body(),
//! })
//! }
//! }
//! ```
//!
//! Handlers call [`html_built_page_or_app_layout`](crate::web::html_built_page_or_app_layout)
//! to pick full document vs partial based on the HTMX request.
//!
//! # Registering templates
//!
//! Use [`define_register_items!`](crate::capability::define_register_items) in `templates.rs`:
//!
//! ```ignore
//! use lariv_rs::capability::define_register_items;
//! use lariv_rs::template::{TemplateCapability, TemplateOf, TemplateRegistrar};
//!
//! pub struct GreetingPageTag;
//!
//! define_register_items! {
//! plugin: MyPluginTag;
//! capability: TemplateCapability;
//! trait: TemplateRegistrar;
//! method: register_templates;
//! wrapper: TemplateOf;
//! bounds: [Clone];
//! hook: Hook;
//! items: [
//! GreetingIdx: GreetingPageTag => GreetingPage,
//! ]
//! }
//! ```
//!
//! Add `templates(templates::Hook)` to [`define_plugin_install!`](crate::plugin_install::define_plugin_install).
//!
//! # Replacing another plugin's template
//!
//! Addon plugins can override pages registered by another plugin using
//! [`define_replace_templates!`](crate::capability::define_replace_templates) with a compile-time
//! index (see the `signup` plugin for login page patches).
//!
//! # Shell chrome and slots
//!
//! Full pages wrap content in navigation chrome from [`ShellChrome`](crate::components::ShellChrome).
//! Plugins contribute topbar/head fragments via `define_register_items!` on
//! [`SlotCapability`](crate::components::slots::SlotCapability) — see the dashboard plugin
//! for topbar buttons and user dropdown examples.
//!
//! # UI building blocks
//!
//! Compose pages from [`crate::components`] builders — fields, inputs, tables, forms, and layout
//! containers. See [`super::components`].