maud_ui/primitives/
resizable.rs1use maud::{html, Markup};
4
5#[derive(Clone, Debug)]
7pub struct Panel {
8 pub content: Markup,
9 pub default_size: f64,
10 pub min_size: Option<f64>,
11}
12
13#[derive(Clone, Debug)]
15pub enum Direction {
16 Horizontal,
17 Vertical,
18}
19
20impl Direction {
21 fn as_str(&self) -> &'static str {
22 match self {
23 Direction::Horizontal => "horizontal",
24 Direction::Vertical => "vertical",
25 }
26 }
27
28 fn aria_orientation(&self) -> &'static str {
29 match self {
30 Direction::Horizontal => "horizontal",
31 Direction::Vertical => "vertical",
32 }
33 }
34}
35
36#[derive(Clone, Debug)]
38pub struct Props {
39 pub id: String,
40 pub panels: Vec<Panel>,
41 pub direction: Direction,
42}
43
44impl Default for Props {
45 fn default() -> Self {
46 Self {
47 id: "resizable".to_string(),
48 panels: vec![],
49 direction: Direction::Horizontal,
50 }
51 }
52}
53
54pub fn render(props: Props) -> Markup {
56 let dir = props.direction.as_str();
57 let orientation = props.direction.aria_orientation();
58 let panel_count = props.panels.len();
59
60 html! {
61 div class=(format!("mui-resizable mui-resizable--{}", dir))
62 data-mui="resizable"
63 data-direction=(dir)
64 id=(props.id)
65 {
66 @for (i, panel) in props.panels.iter().enumerate() {
67 @let min = panel.min_size.unwrap_or(10.0);
68 div class="mui-resizable__panel"
69 style=(format!("flex: {} 1 0%", panel.default_size))
70 data-min-size=(format!("{}", min))
71 {
72 (panel.content)
73 }
74 @if i < panel_count - 1 {
75 div class="mui-resizable__handle"
76 data-index=(format!("{}", i))
77 tabindex="0"
78 role="separator"
79 aria-orientation=(orientation)
80 aria-valuenow=(format!("{}", panel.default_size))
81 {
82 div class="mui-resizable__handle-bar" {}
83 }
84 }
85 }
86 }
87 }
88}
89
90pub fn showcase() -> Markup {
92 html! {
93 div class="mui-showcase__grid" {
94 div {
96 h3 class="mui-showcase__caption" { "Sidebar + Content" }
97 (render(Props {
98 id: "demo-resize-h2".to_string(),
99 direction: Direction::Horizontal,
100 panels: vec![
101 Panel {
102 content: html! {
103 div class="mui-resizable__demo-content" {
104 h4 { "Navigation" }
105 ul class="mui-resizable__demo-nav" {
106 li { a class="active" href="#" { "Dashboard" } }
107 li { a href="#" { "Projects" } }
108 li { a href="#" { "Team" } }
109 li { a href="#" { "Settings" } }
110 li { a href="#" { "Analytics" } }
111 }
112 }
113 },
114 default_size: 30.0,
115 min_size: Some(15.0),
116 },
117 Panel {
118 content: html! {
119 div class="mui-resizable__demo-content" {
120 h4 { "Dashboard" }
121 p { "Welcome back. Here is an overview of your recent activity, key metrics, and pending tasks. Drag the handle to resize the sidebar." }
122 }
123 },
124 default_size: 70.0,
125 min_size: Some(30.0),
126 },
127 ],
128 }))
129 }
130
131 div {
133 h3 class="mui-showcase__caption" { "Three-column Layout" }
134 (render(Props {
135 id: "demo-resize-h3".to_string(),
136 direction: Direction::Horizontal,
137 panels: vec![
138 Panel {
139 content: html! {
140 div class="mui-resizable__demo-content" {
141 h4 { "Explorer" }
142 ul class="mui-resizable__demo-nav" {
143 li { a class="active" href="#" { "src/" } }
144 li { a href="#" { "tests/" } }
145 li { a href="#" { "docs/" } }
146 li { a href="#" { "Cargo.toml" } }
147 }
148 }
149 },
150 default_size: 20.0,
151 min_size: Some(10.0),
152 },
153 Panel {
154 content: html! {
155 div class="mui-resizable__demo-content" {
156 h4 { "Editor" }
157 p { "Select a file from the explorer to view its contents here. This center panel occupies the majority of the available space." }
158 }
159 },
160 default_size: 55.0,
161 min_size: Some(20.0),
162 },
163 Panel {
164 content: html! {
165 div class="mui-resizable__demo-content" {
166 h4 { "Inspector" }
167 p { "Properties and metadata for the selected item will appear in this panel." }
168 }
169 },
170 default_size: 25.0,
171 min_size: Some(10.0),
172 },
173 ],
174 }))
175 }
176 }
177 }
178}