1use ironhtml::typed::{Document, Element};
10use ironhtml_bootstrap::{
11 accordion, alerts, badge, breadcrumb, buttons, cards, carousel, close_button, collapse,
12 dropdown, list_group, modal, navbar, offcanvas, pagination, placeholder, progress, spinner,
13 toast, tooltip, Color, NavbarExpand, Size,
14};
15use ironhtml_elements::{
16 Body, Br, Button, Div, Form, Head, Html, Input, Li, Link, Main, Meta, Nav, Ol, Script, Section,
17 Span, Style, Title, Ul, A, H1, H2, H4, H5, P,
18};
19
20extern crate alloc;
21use alloc::vec;
22
23struct SidebarItem {
29 href: &'static str,
30 label: &'static str,
31 active: bool,
32}
33
34impl SidebarItem {
35 const fn new(href: &'static str, label: &'static str) -> Self {
36 Self {
37 href,
38 label,
39 active: false,
40 }
41 }
42
43 const fn active(mut self) -> Self {
44 self.active = true;
45 self
46 }
47}
48
49fn docs_navbar() -> Element<Nav> {
55 Element::<Nav>::new()
56 .class("navbar navbar-expand-lg bg-dark navbar-dark sticky-top")
57 .child::<Div, _>(|d| {
58 d.class("container-fluid")
59 .child::<A, _>(|a| {
60 a.class("navbar-brand")
61 .attr("href", "/")
62 .child::<Span, _>(|s| s.class("me-2").text("📘"))
63 .text("Bootstrap Docs")
64 })
65 .child::<Button, _>(|b| {
66 b.class("navbar-toggler")
67 .attr("type", "button")
68 .attr("data-bs-toggle", "collapse")
69 .attr("data-bs-target", "#navbarSearch")
70 .child::<Span, _>(|s| s.class("navbar-toggler-icon"))
71 })
72 .child::<Div, _>(|d| {
73 d.class("collapse navbar-collapse")
74 .attr("id", "navbarSearch")
75 .child::<Ul, _>(|ul| {
76 ul.class("navbar-nav me-auto")
77 .child::<Li, _>(|li| {
78 li.class("nav-item").child::<A, _>(|a| {
79 a.class("nav-link")
80 .attr("href", "#")
81 .text("Getting Started")
82 })
83 })
84 .child::<Li, _>(|li| {
85 li.class("nav-item").child::<A, _>(|a| {
86 a.class("nav-link active")
87 .attr("href", "#")
88 .text("Components")
89 })
90 })
91 .child::<Li, _>(|li| {
92 li.class("nav-item").child::<A, _>(|a| {
93 a.class("nav-link").attr("href", "#").text("Utilities")
94 })
95 })
96 })
97 .child::<Form, _>(|f| {
98 f.class("d-flex")
99 .attr("role", "search")
100 .child::<Input, _>(|i| {
101 i.class("form-control me-2")
102 .attr("type", "search")
103 .attr("placeholder", "Search...")
104 })
105 })
106 })
107 })
108}
109
110fn docs_sidebar(items: &[SidebarItem]) -> Element<Nav> {
112 Element::<Nav>::new()
113 .class("sidebar bg-body-tertiary p-3")
114 .attr("style", "width: 280px; min-height: 100vh;")
115 .child::<H5, _>(|h| h.class("mb-3 text-muted").text("Components"))
116 .child::<Ul, _>(|ul| {
117 items
118 .iter()
119 .fold(ul.class("nav nav-pills flex-column"), |ul, item| {
120 ul.child::<Li, _>(|li| {
121 let link_class = if item.active {
122 "nav-link active"
123 } else {
124 "nav-link text-dark"
125 };
126 li.class("nav-item").child::<A, _>(|a| {
127 a.class(link_class).attr("href", item.href).text(item.label)
128 })
129 })
130 })
131 })
132}
133
134fn accordion_section() -> Element<Section> {
140 Element::<Section>::new()
141 .attr("id", "accordion")
142 .class("mb-5")
143 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Accordion"))
144 .child::<P, _>(|p| {
145 p.class("lead").text(
146 "Build vertically collapsing accordions in combination with our Collapse JavaScript plugin.",
147 )
148 })
149 .child::<H4, _>(|h| h.class("mt-4").text("Basic Example"))
151 .child::<Div, _>(|d| {
152 d.class("bd-example mb-3").child::<Div, _>(|_| {
153 let items = vec![
154 accordion::AccordionItem {
155 id: "one".into(),
156 header: "Accordion Item #1".into(),
157 content: "This is the first item's accordion body.".into(),
158 expanded: true,
159 },
160 accordion::AccordionItem {
161 id: "two".into(),
162 header: "Accordion Item #2".into(),
163 content: "This is the second item's accordion body.".into(),
164 expanded: false,
165 },
166 accordion::AccordionItem {
167 id: "three".into(),
168 header: "Accordion Item #3".into(),
169 content: "This is the third item's accordion body.".into(),
170 expanded: false,
171 },
172 ];
173 accordion::accordion("accordionExample", &items)
174 })
175 })
176 .child::<H4, _>(|h| h.class("mt-4").text("Flush"))
178 .child::<P, _>(|p| {
179 p.text("Add .accordion-flush to remove borders and rounded corners.")
180 })
181 .child::<Div, _>(|d| {
182 d.class("bd-example mb-3").child::<Div, _>(|_| {
183 let items = vec![
184 accordion::AccordionItem {
185 id: "f1".into(),
186 header: "Accordion Item #1".into(),
187 content: "Flush accordion content here.".into(),
188 expanded: false,
189 },
190 accordion::AccordionItem {
191 id: "f2".into(),
192 header: "Accordion Item #2".into(),
193 content: "More flush accordion content.".into(),
194 expanded: false,
195 },
196 ];
197 accordion::accordion_flush("accordionFlush", &items)
198 })
199 })
200}
201
202fn alerts_section() -> Element<Section> {
204 Element::<Section>::new()
205 .attr("id", "alerts")
206 .class("mb-5")
207 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Alerts"))
208 .child::<P, _>(|p| {
209 p.class("lead")
210 .text("Provide contextual feedback messages for typical user actions.")
211 })
212 .child::<H4, _>(|h| h.class("mt-4").text("Examples"))
214 .child::<Div, _>(|d| {
215 d.class("bd-example mb-3")
216 .child::<Div, _>(|_| alerts::alert(Color::Primary, "A simple primary alert!"))
217 .child::<Div, _>(|_| alerts::alert(Color::Secondary, "A simple secondary alert!"))
218 .child::<Div, _>(|_| alerts::alert(Color::Success, "A simple success alert!"))
219 .child::<Div, _>(|_| alerts::alert(Color::Danger, "A simple danger alert!"))
220 .child::<Div, _>(|_| alerts::alert(Color::Warning, "A simple warning alert!"))
221 .child::<Div, _>(|_| alerts::alert(Color::Info, "A simple info alert!"))
222 })
223 .child::<H4, _>(|h| h.class("mt-4").text("Dismissing"))
225 .child::<P, _>(|p| p.text("Add a dismiss button and the .alert-dismissible class."))
226 .child::<Div, _>(|d| {
227 d.class("bd-example mb-3").child::<Div, _>(|_| {
228 alerts::alert_dismissible(Color::Warning, "Holy guacamole! You should check this.")
229 })
230 })
231 .child::<H4, _>(|h| h.class("mt-4").text("Additional Content"))
233 .child::<Div, _>(|d| {
234 d.class("bd-example mb-3").child::<Div, _>(|_| {
235 alerts::alert_with_heading(
236 Color::Success,
237 "Well done!",
238 "You successfully read this important alert message.",
239 "",
240 )
241 })
242 })
243}
244
245fn badges_section() -> Element<Section> {
247 Element::<Section>::new()
248 .attr("id", "badges")
249 .class("mb-5")
250 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Badges"))
251 .child::<P, _>(|p| p.class("lead").text("Small count and labeling component."))
252 .child::<H4, _>(|h| h.class("mt-4").text("Background Colors"))
254 .child::<Div, _>(|d| {
255 d.class("bd-example mb-3")
256 .child::<Span, _>(|_| badge::badge(Color::Primary, "Primary"))
257 .text(" ")
258 .child::<Span, _>(|_| badge::badge(Color::Secondary, "Secondary"))
259 .text(" ")
260 .child::<Span, _>(|_| badge::badge(Color::Success, "Success"))
261 .text(" ")
262 .child::<Span, _>(|_| badge::badge(Color::Danger, "Danger"))
263 .text(" ")
264 .child::<Span, _>(|_| badge::badge(Color::Warning, "Warning"))
265 .text(" ")
266 .child::<Span, _>(|_| badge::badge(Color::Info, "Info"))
267 })
268 .child::<H4, _>(|h| h.class("mt-4").text("Pill Badges"))
270 .child::<Div, _>(|d| {
271 d.class("bd-example mb-3")
272 .child::<Span, _>(|_| badge::badge_pill(Color::Primary, "Primary"))
273 .text(" ")
274 .child::<Span, _>(|_| badge::badge_pill(Color::Success, "Success"))
275 .text(" ")
276 .child::<Span, _>(|_| badge::badge_pill(Color::Danger, "99+"))
277 })
278}
279
280fn breadcrumb_section() -> Element<Section> {
282 Element::<Section>::new()
283 .attr("id", "breadcrumb")
284 .class("mb-5")
285 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Breadcrumb"))
286 .child::<P, _>(|p| {
287 p.class("lead")
288 .text("Indicate the current page's location within a navigational hierarchy.")
289 })
290 .child::<H4, _>(|h| h.class("mt-4").text("Example"))
291 .child::<Div, _>(|d| {
292 d.class("bd-example mb-3").child::<Nav, _>(|_| {
293 let items = vec![
294 breadcrumb::BreadcrumbItem::link("Home", "/"),
295 breadcrumb::BreadcrumbItem::link("Library", "/library"),
296 breadcrumb::BreadcrumbItem::active("Data"),
297 ];
298 breadcrumb::breadcrumb(&items)
299 })
300 })
301 .child::<Div, _>(|d| {
302 d.class("bd-example mb-3").child::<Nav, _>(|_| {
303 let items = vec![breadcrumb::BreadcrumbItem::active("Home")];
304 breadcrumb::breadcrumb(&items)
305 })
306 })
307}
308
309fn buttons_section() -> Element<Section> {
311 Element::<Section>::new()
312 .attr("id", "buttons")
313 .class("mb-5")
314 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Buttons"))
315 .child::<P, _>(|p| {
316 p.class("lead").text(
317 "Use Bootstrap's custom button styles for actions in forms, dialogs, and more.",
318 )
319 })
320 .child::<H4, _>(|h| h.class("mt-4").text("Base Class"))
322 .child::<Div, _>(|d| {
323 d.class("bd-example mb-3")
324 .child::<Button, _>(|_| buttons::btn(Color::Primary, "Primary"))
325 .text(" ")
326 .child::<Button, _>(|_| buttons::btn(Color::Secondary, "Secondary"))
327 .text(" ")
328 .child::<Button, _>(|_| buttons::btn(Color::Success, "Success"))
329 .text(" ")
330 .child::<Button, _>(|_| buttons::btn(Color::Danger, "Danger"))
331 .text(" ")
332 .child::<Button, _>(|_| buttons::btn(Color::Warning, "Warning"))
333 .text(" ")
334 .child::<Button, _>(|_| buttons::btn(Color::Info, "Info"))
335 .text(" ")
336 .child::<Button, _>(|_| buttons::btn(Color::Light, "Light"))
337 .text(" ")
338 .child::<Button, _>(|_| buttons::btn(Color::Dark, "Dark"))
339 })
340 .child::<H4, _>(|h| h.class("mt-4").text("Outline Buttons"))
342 .child::<Div, _>(|d| {
343 d.class("bd-example mb-3")
344 .child::<Button, _>(|_| buttons::btn_outline(Color::Primary, "Primary"))
345 .text(" ")
346 .child::<Button, _>(|_| buttons::btn_outline(Color::Secondary, "Secondary"))
347 .text(" ")
348 .child::<Button, _>(|_| buttons::btn_outline(Color::Success, "Success"))
349 .text(" ")
350 .child::<Button, _>(|_| buttons::btn_outline(Color::Danger, "Danger"))
351 })
352 .child::<H4, _>(|h| h.class("mt-4").text("Sizes"))
354 .child::<Div, _>(|d| {
355 d.class("bd-example mb-3")
356 .child::<Button, _>(|_| buttons::btn_sized(Color::Primary, Size::Large, "Large"))
357 .text(" ")
358 .child::<Button, _>(|_| buttons::btn(Color::Primary, "Default"))
359 .text(" ")
360 .child::<Button, _>(|_| buttons::btn_sized(Color::Primary, Size::Small, "Small"))
361 })
362 .child::<H4, _>(|h| h.class("mt-4").text("Disabled State"))
364 .child::<Div, _>(|d| {
365 d.class("bd-example mb-3")
366 .child::<Button, _>(|_| buttons::btn_disabled(Color::Primary, "Disabled"))
367 .text(" ")
368 .child::<Button, _>(|_| buttons::btn_disabled(Color::Secondary, "Disabled"))
369 })
370}
371
372fn cards_section() -> Element<Section> {
374 Element::<Section>::new()
375 .attr("id", "cards")
376 .class("mb-5")
377 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Cards"))
378 .child::<P, _>(|p| {
379 p.class("lead").text(
380 "Bootstrap's cards provide a flexible and extensible content container.",
381 )
382 })
383 .child::<H4, _>(|h| h.class("mt-4").text("Example"))
385 .child::<Div, _>(|d| {
386 d.class("bd-example mb-3")
387 .attr("style", "max-width: 18rem;")
388 .child::<Div, _>(|_| {
389 cards::card_simple(
390 "Card title",
391 "Some quick example text to build on the card title and make up the bulk of the card's content.",
392 "Go somewhere",
393 "#",
394 )
395 })
396 })
397 .child::<H4, _>(|h| h.class("mt-4").text("Image Caps"))
399 .child::<Div, _>(|d| {
400 d.class("bd-example mb-3")
401 .attr("style", "max-width: 18rem;")
402 .child::<Div, _>(|_| {
403 cards::card_with_image(
404 "https://via.placeholder.com/286x180",
405 "Card image cap",
406 "Card title",
407 "This is a wider card with supporting text below.",
408 )
409 })
410 })
411 .child::<H4, _>(|h| h.class("mt-4").text("Header and Footer"))
413 .child::<Div, _>(|d| {
414 d.class("bd-example mb-3")
415 .attr("style", "max-width: 18rem;")
416 .child::<Div, _>(|_| {
417 cards::card_with_header_footer("Featured", "Footer text", |body| {
418 body.child::<H5, _>(|h| h.class("card-title").text("Special title treatment"))
419 .child::<P, _>(|p| {
420 p.class("card-text").text(
421 "With supporting text below as a natural lead-in to additional content.",
422 )
423 })
424 })
425 })
426 })
427}
428
429fn list_group_section() -> Element<Section> {
431 Element::<Section>::new()
432 .attr("id", "list-group")
433 .class("mb-5")
434 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("List Group"))
435 .child::<P, _>(|p| {
436 p.class("lead")
437 .text("List groups are a flexible and powerful component for displaying lists.")
438 })
439 .child::<H4, _>(|h| h.class("mt-4").text("Basic Example"))
441 .child::<Div, _>(|d| {
442 d.class("bd-example mb-3")
443 .attr("style", "max-width: 400px;")
444 .child::<Ul, _>(|_| {
445 list_group::list_group(&["An item", "A second item", "A third item"])
446 })
447 })
448 .child::<H4, _>(|h| h.class("mt-4").text("Links and Buttons"))
450 .child::<Div, _>(|d| {
451 d.class("bd-example mb-3")
452 .attr("style", "max-width: 400px;")
453 .child::<Div, _>(|_| {
454 let items = vec![
455 list_group::ListGroupLink::new("The current link item", "#").active(),
456 list_group::ListGroupLink::new("A second link item", "#"),
457 list_group::ListGroupLink::new("A third link item", "#"),
458 list_group::ListGroupLink::new("A disabled link item", "#").disabled(),
459 ];
460 list_group::list_group_links(&items)
461 })
462 })
463 .child::<H4, _>(|h| h.class("mt-4").text("Flush"))
465 .child::<Div, _>(|d| {
466 d.class("bd-example mb-3")
467 .attr("style", "max-width: 400px;")
468 .child::<Ul, _>(|_| {
469 list_group::list_group_flush(&["An item", "A second item", "A third item"])
470 })
471 })
472 .child::<H4, _>(|h| h.class("mt-4").text("Numbered"))
474 .child::<Div, _>(|d| {
475 d.class("bd-example mb-3")
476 .attr("style", "max-width: 400px;")
477 .child::<Ol, _>(|_| {
478 list_group::list_group_numbered(&["A list item", "A list item", "A list item"])
479 })
480 })
481}
482
483fn progress_section() -> Element<Section> {
485 Element::<Section>::new()
486 .attr("id", "progress")
487 .class("mb-5")
488 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Progress"))
489 .child::<P, _>(|p| {
490 p.class("lead")
491 .text("Documentation and examples for using Bootstrap custom progress bars.")
492 })
493 .child::<H4, _>(|h| h.class("mt-4").text("Example"))
495 .child::<Div, _>(|d| {
496 d.class("bd-example mb-3")
497 .child::<Div, _>(|d| d.class("mb-2").child::<Div, _>(|_| progress::progress(0)))
498 .child::<Div, _>(|d| d.class("mb-2").child::<Div, _>(|_| progress::progress(25)))
499 .child::<Div, _>(|d| d.class("mb-2").child::<Div, _>(|_| progress::progress(50)))
500 .child::<Div, _>(|d| d.class("mb-2").child::<Div, _>(|_| progress::progress(75)))
501 .child::<Div, _>(|d| d.class("mb-2").child::<Div, _>(|_| progress::progress(100)))
502 })
503 .child::<H4, _>(|h| h.class("mt-4").text("Backgrounds"))
505 .child::<Div, _>(|d| {
506 d.class("bd-example mb-3")
507 .child::<Div, _>(|d| {
508 d.class("mb-2")
509 .child::<Div, _>(|_| progress::progress_colored(25, Color::Success))
510 })
511 .child::<Div, _>(|d| {
512 d.class("mb-2")
513 .child::<Div, _>(|_| progress::progress_colored(50, Color::Info))
514 })
515 .child::<Div, _>(|d| {
516 d.class("mb-2")
517 .child::<Div, _>(|_| progress::progress_colored(75, Color::Warning))
518 })
519 .child::<Div, _>(|d| {
520 d.class("mb-2")
521 .child::<Div, _>(|_| progress::progress_colored(100, Color::Danger))
522 })
523 })
524 .child::<H4, _>(|h| h.class("mt-4").text("Striped"))
526 .child::<Div, _>(|d| {
527 d.class("bd-example mb-3")
528 .child::<Div, _>(|d| {
529 d.class("mb-2")
530 .child::<Div, _>(|_| progress::progress_striped(25, Color::Primary))
531 })
532 .child::<Div, _>(|d| {
533 d.class("mb-2")
534 .child::<Div, _>(|_| progress::progress_striped(50, Color::Success))
535 })
536 })
537 .child::<H4, _>(|h| h.class("mt-4").text("Animated Stripes"))
539 .child::<Div, _>(|d| {
540 d.class("bd-example mb-3")
541 .child::<Div, _>(|_| progress::progress_animated(75, Color::Primary))
542 })
543}
544
545fn spinners_section() -> Element<Section> {
547 Element::<Section>::new()
548 .attr("id", "spinners")
549 .class("mb-5")
550 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Spinners"))
551 .child::<P, _>(|p| {
552 p.class("lead")
553 .text("Indicate the loading state of a component or page with Bootstrap spinners.")
554 })
555 .child::<H4, _>(|h| h.class("mt-4").text("Border Spinner"))
557 .child::<Div, _>(|d| {
558 d.class("bd-example mb-3")
559 .child::<Div, _>(|_| spinner::spinner())
560 })
561 .child::<H4, _>(|h| h.class("mt-4").text("Colors"))
563 .child::<Div, _>(|d| {
564 d.class("bd-example mb-3")
565 .child::<Div, _>(|d| {
566 d.class("d-inline-block me-2")
567 .child::<Div, _>(|_| spinner::spinner_colored(Color::Primary))
568 })
569 .child::<Div, _>(|d| {
570 d.class("d-inline-block me-2")
571 .child::<Div, _>(|_| spinner::spinner_colored(Color::Secondary))
572 })
573 .child::<Div, _>(|d| {
574 d.class("d-inline-block me-2")
575 .child::<Div, _>(|_| spinner::spinner_colored(Color::Success))
576 })
577 .child::<Div, _>(|d| {
578 d.class("d-inline-block me-2")
579 .child::<Div, _>(|_| spinner::spinner_colored(Color::Danger))
580 })
581 .child::<Div, _>(|d| {
582 d.class("d-inline-block me-2")
583 .child::<Div, _>(|_| spinner::spinner_colored(Color::Warning))
584 })
585 .child::<Div, _>(|d| {
586 d.class("d-inline-block me-2")
587 .child::<Div, _>(|_| spinner::spinner_colored(Color::Info))
588 })
589 })
590 .child::<H4, _>(|h| h.class("mt-4").text("Growing Spinner"))
592 .child::<Div, _>(|d| {
593 d.class("bd-example mb-3")
594 .child::<Div, _>(|_| spinner::spinner_grow())
595 })
596 .child::<H4, _>(|h| h.class("mt-4").text("Size"))
598 .child::<Div, _>(|d| {
599 d.class("bd-example mb-3")
600 .child::<Span, _>(|s| s.class("me-2").child::<Span, _>(|_| spinner::spinner_sm()))
601 .child::<Span, _>(|_| spinner::spinner_grow_sm())
602 })
603 .child::<H4, _>(|h| h.class("mt-4").text("Buttons"))
605 .child::<Div, _>(|d| {
606 d.class("bd-example mb-3")
607 .child::<Button, _>(|_| buttons::btn_loading(Color::Primary, "Loading..."))
608 .text(" ")
609 .child::<Button, _>(|_| buttons::btn_loading_grow(Color::Primary, "Loading..."))
610 })
611}
612
613fn navbar_section() -> Element<Section> {
615 Element::<Section>::new()
616 .attr("id", "navbar")
617 .class("mb-5")
618 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Navbar"))
619 .child::<P, _>(|p| {
620 p.class("lead").text(
621 "Documentation and examples for Bootstrap's powerful, responsive navigation header.",
622 )
623 })
624 .child::<H4, _>(|h| h.class("mt-4").text("Example"))
625 .child::<Div, _>(|d| {
626 d.class("bd-example mb-3").child::<Nav, _>(|_| {
627 navbar::navbar("Navbar", NavbarExpand::Lg, "navbarExample", |ul| {
628 ul.child::<Li, _>(|_| navbar::nav_item("/", "Home", true))
629 .child::<Li, _>(|_| navbar::nav_item("/features", "Features", false))
630 .child::<Li, _>(|_| navbar::nav_item("/pricing", "Pricing", false))
631 .child::<Li, _>(|_| navbar::nav_item_disabled("Disabled"))
632 })
633 })
634 })
635}
636
637fn carousel_section() -> Element<Section> {
639 Element::<Section>::new()
640 .attr("id", "carousel")
641 .class("mb-5")
642 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Carousel"))
643 .child::<P, _>(|p| {
644 p.class("lead")
645 .text("A slideshow component for cycling through elements.")
646 })
647 .child::<H4, _>(|h| h.class("mt-4").text("Basic Example"))
648 .child::<Div, _>(|d| {
649 d.class("bd-example mb-3").child::<Div, _>(|_| {
650 let items = vec![
651 carousel::CarouselItem::new(
652 "https://via.placeholder.com/800x400/007bff/ffffff?text=First+Slide",
653 "First slide",
654 )
655 .active()
656 .caption(
657 "First slide label",
658 "Some representative placeholder content.",
659 ),
660 carousel::CarouselItem::new(
661 "https://via.placeholder.com/800x400/6c757d/ffffff?text=Second+Slide",
662 "Second slide",
663 )
664 .caption("Second slide label", "Some more placeholder content."),
665 carousel::CarouselItem::new(
666 "https://via.placeholder.com/800x400/28a745/ffffff?text=Third+Slide",
667 "Third slide",
668 )
669 .caption("Third slide label", "Even more placeholder content."),
670 ];
671 carousel::carousel_with_indicators("carouselExample", &items)
672 })
673 })
674}
675
676fn close_button_section() -> Element<Section> {
678 Element::<Section>::new()
679 .attr("id", "close-button")
680 .class("mb-5")
681 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Close Button"))
682 .child::<P, _>(|p| {
683 p.class("lead")
684 .text("A generic close button for dismissing content like modals and alerts.")
685 })
686 .child::<H4, _>(|h| h.class("mt-4").text("Examples"))
687 .child::<Div, _>(|d| {
688 d.class("bd-example mb-3")
689 .child::<Button, _>(|_| close_button::close_button())
690 })
691 .child::<H4, _>(|h| h.class("mt-4").text("Disabled"))
692 .child::<Div, _>(|d| {
693 d.class("bd-example mb-3")
694 .child::<Button, _>(|_| close_button::close_button_disabled())
695 })
696 .child::<H4, _>(|h| h.class("mt-4").text("Dark Variant"))
697 .child::<Div, _>(|d| {
698 d.class("bd-example mb-3 bg-dark p-3")
699 .child::<Button, _>(|_| close_button::close_button_white())
700 })
701}
702
703fn collapse_section() -> Element<Section> {
705 Element::<Section>::new()
706 .attr("id", "collapse")
707 .class("mb-5")
708 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Collapse"))
709 .child::<P, _>(|p| {
710 p.class("lead")
711 .text("Toggle the visibility of content with a few classes and JavaScript plugins.")
712 })
713 .child::<H4, _>(|h| h.class("mt-4").text("Example"))
714 .child::<Div, _>(|d| {
715 d.class("bd-example mb-3")
716 .child::<P, _>(|p| {
717 p.child::<Button, _>(|_| {
718 collapse::collapse_button("collapseExample", "Toggle content")
719 })
720 .text(" ")
721 .child::<A, _>(|_| collapse::collapse_link("collapseExample", "Link"))
722 })
723 .child::<Div, _>(|_| {
724 collapse::collapse_content("collapseExample", |div| {
725 div.child::<Div, _>(|d| {
726 d.class("card card-body")
727 .text("Some placeholder content for the collapse component.")
728 })
729 })
730 })
731 })
732}
733
734fn dropdown_section() -> Element<Section> {
736 Element::<Section>::new()
737 .attr("id", "dropdowns")
738 .class("mb-5")
739 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Dropdowns"))
740 .child::<P, _>(|p| {
741 p.class("lead")
742 .text("Toggle contextual overlays for displaying lists of links and more.")
743 })
744 .child::<H4, _>(|h| h.class("mt-4").text("Single Button"))
745 .child::<Div, _>(|d| {
746 d.class("bd-example mb-3").child::<Div, _>(|_| {
747 let items = vec![
748 dropdown::DropdownItem::link("Action", "#"),
749 dropdown::DropdownItem::link("Another action", "#"),
750 dropdown::DropdownItem::divider(),
751 dropdown::DropdownItem::link("Separated link", "#"),
752 ];
753 dropdown::dropdown(Color::Primary, "Dropdown button", &items)
754 })
755 })
756 .child::<H4, _>(|h| h.class("mt-4").text("Split Button"))
757 .child::<Div, _>(|d| {
758 d.class("bd-example mb-3").child::<Div, _>(|_| {
759 let items = vec![
760 dropdown::DropdownItem::link("Action", "#"),
761 dropdown::DropdownItem::link("Another action", "#"),
762 dropdown::DropdownItem::divider(),
763 dropdown::DropdownItem::link("Separated link", "#"),
764 ];
765 dropdown::dropdown_split(Color::Success, "Action", "#", &items)
766 })
767 })
768 .child::<H4, _>(|h| h.class("mt-4").text("Directions"))
769 .child::<Div, _>(|d| {
770 let items = vec![dropdown::DropdownItem::link("Action", "#")];
771 d.class("bd-example mb-3")
772 .child::<Div, _>(|d| {
773 d.class("d-inline-block me-2")
774 .child::<Div, _>(|_| dropdown::dropup(Color::Secondary, "Dropup", &items))
775 })
776 .child::<Div, _>(|d| {
777 d.class("d-inline-block me-2")
778 .child::<Div, _>(|_| dropdown::dropend(Color::Secondary, "Dropend", &items))
779 })
780 .child::<Div, _>(|d| {
781 d.class("d-inline-block").child::<Div, _>(|_| {
782 dropdown::dropstart(Color::Secondary, "Dropstart", &items)
783 })
784 })
785 })
786}
787
788fn modal_section() -> Element<Section> {
790 Element::<Section>::new()
791 .attr("id", "modal")
792 .class("mb-5")
793 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Modal"))
794 .child::<P, _>(|p| {
795 p.class("lead")
796 .text("Add dialogs to your site for lightboxes, notifications, or custom content.")
797 })
798 .child::<H4, _>(|h| h.class("mt-4").text("Live Demo"))
799 .child::<Div, _>(|d| {
800 d.class("bd-example mb-3")
801 .child::<Button, _>(|_| {
802 modal::modal_button("exampleModal", Color::Primary, "Launch demo modal")
803 })
804 .child::<Div, _>(|_| {
805 modal::modal_with_footer(
806 "exampleModal",
807 modal::ModalSize::Default,
808 "Modal title",
809 |body| body.text("This is modal body content."),
810 "Save changes",
811 )
812 })
813 })
814 .child::<H4, _>(|h| h.class("mt-4").text("Sizes"))
815 .child::<P, _>(|p| {
816 p.text("Modals come in small, default, large, extra-large, and fullscreen sizes.")
817 })
818 .child::<Div, _>(|d| {
819 d.class("bd-example mb-3")
820 .child::<Button, _>(|_| {
821 modal::modal_button("smallModal", Color::Primary, "Small modal")
822 })
823 .text(" ")
824 .child::<Button, _>(|_| {
825 modal::modal_button("largeModal", Color::Primary, "Large modal")
826 })
827 .text(" ")
828 .child::<Button, _>(|_| {
829 modal::modal_button("xlModal", Color::Primary, "Extra large modal")
830 })
831 })
832}
833
834fn offcanvas_section() -> Element<Section> {
836 Element::<Section>::new()
837 .attr("id", "offcanvas")
838 .class("mb-5")
839 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Offcanvas"))
840 .child::<P, _>(|p| {
841 p.class("lead")
842 .text("Build hidden sidebars into your project for navigation and more.")
843 })
844 .child::<H4, _>(|h| h.class("mt-4").text("Example"))
845 .child::<Div, _>(|d| {
846 d.class("bd-example mb-3")
847 .child::<Button, _>(|_| {
848 offcanvas::offcanvas_button("offcanvasExample", Color::Primary, "Toggle offcanvas")
849 })
850 .child::<Div, _>(|_| {
851 offcanvas::offcanvas(
852 "offcanvasExample",
853 offcanvas::OffcanvasPlacement::Start,
854 "Offcanvas",
855 |body| {
856 body.text("Some text as placeholder. In real life you can have elements here.")
857 },
858 )
859 })
860 })
861 .child::<H4, _>(|h| h.class("mt-4").text("Placement"))
862 .child::<P, _>(|p| {
863 p.text("Offcanvas can be placed on the start, end, top, or bottom of the viewport.")
864 })
865}
866
867fn pagination_section() -> Element<Section> {
869 Element::<Section>::new()
870 .attr("id", "pagination")
871 .class("mb-5")
872 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Pagination"))
873 .child::<P, _>(|p| {
874 p.class("lead")
875 .text("Indicate a series of related content exists across multiple pages.")
876 })
877 .child::<H4, _>(|h| h.class("mt-4").text("Basic Example"))
878 .child::<Div, _>(|d| {
879 d.class("bd-example mb-3").child::<Nav, _>(|_| {
880 let items = vec![
881 pagination::PageItem::page(1, "#").active(),
882 pagination::PageItem::page(2, "#"),
883 pagination::PageItem::page(3, "#"),
884 ];
885 pagination::pagination(&items)
886 })
887 })
888 .child::<H4, _>(|h| h.class("mt-4").text("With Icons"))
889 .child::<Div, _>(|d| {
890 d.class("bd-example mb-3").child::<Nav, _>(|_| {
891 let items = vec![
892 pagination::PageItem::page(1, "#").active(),
893 pagination::PageItem::page(2, "#"),
894 pagination::PageItem::page(3, "#"),
895 ];
896 pagination::pagination_with_arrows(&items, Some("#"), Some("#"))
897 })
898 })
899 .child::<H4, _>(|h| h.class("mt-4").text("Sizes"))
900 .child::<Div, _>(|d| {
901 let items = vec![
902 pagination::PageItem::page(1, "#").active(),
903 pagination::PageItem::page(2, "#"),
904 pagination::PageItem::page(3, "#"),
905 ];
906 d.class("bd-example mb-3")
907 .child::<Nav, _>(|_| {
908 pagination::pagination_sized(&items, pagination::PaginationSize::Large)
909 })
910 .child::<Nav, _>(|_| {
911 pagination::pagination_sized(&items, pagination::PaginationSize::Small)
912 })
913 })
914}
915
916fn placeholder_section() -> Element<Section> {
918 Element::<Section>::new()
919 .attr("id", "placeholders")
920 .class("mb-5")
921 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Placeholders"))
922 .child::<P, _>(|p| {
923 p.class("lead")
924 .text("Use loading placeholders for your components or pages to indicate content is loading.")
925 })
926 .child::<H4, _>(|h| h.class("mt-4").text("Example"))
927 .child::<Div, _>(|d| {
928 d.class("bd-example mb-3")
929 .attr("style", "max-width: 18rem;")
930 .child::<Div, _>(|_| placeholder::placeholder_card())
931 })
932 .child::<H4, _>(|h| h.class("mt-4").text("Width"))
933 .child::<Div, _>(|d| {
934 d.class("bd-example mb-3")
935 .child::<Span, _>(|_| placeholder::placeholder(placeholder::PlaceholderWidth::Col6))
936 .child::<Br, _>(|b| b)
937 .child::<Span, _>(|_| placeholder::placeholder(placeholder::PlaceholderWidth::Col4))
938 .child::<Br, _>(|b| b)
939 .child::<Span, _>(|_| placeholder::placeholder(placeholder::PlaceholderWidth::Col8))
940 })
941 .child::<H4, _>(|h| h.class("mt-4").text("Color"))
942 .child::<Div, _>(|d| {
943 d.class("bd-example mb-3")
944 .child::<Span, _>(|_| {
945 placeholder::placeholder_colored(placeholder::PlaceholderWidth::Col12, Color::Primary)
946 })
947 .child::<Br, _>(|b| b)
948 .child::<Span, _>(|_| {
949 placeholder::placeholder_colored(placeholder::PlaceholderWidth::Col12, Color::Success)
950 })
951 .child::<Br, _>(|b| b)
952 .child::<Span, _>(|_| {
953 placeholder::placeholder_colored(placeholder::PlaceholderWidth::Col12, Color::Danger)
954 })
955 })
956}
957
958fn toast_section() -> Element<Section> {
960 Element::<Section>::new()
961 .attr("id", "toasts")
962 .class("mb-5")
963 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Toasts"))
964 .child::<P, _>(|p| {
965 p.class("lead")
966 .text("Push notifications to your visitors with a toast, a lightweight and customizable alert message.")
967 })
968 .child::<H4, _>(|h| h.class("mt-4").text("Basic"))
969 .child::<Div, _>(|d| {
970 d.class("bd-example mb-3")
971 .child::<Div, _>(|_| toast::toast_show("Hello, world! This is a toast message.", "11 mins ago"))
972 })
973 .child::<H4, _>(|h| h.class("mt-4").text("Color Schemes"))
974 .child::<Div, _>(|d| {
975 d.class("bd-example mb-3")
976 .child::<Div, _>(|d| {
977 d.class("mb-2")
978 .child::<Div, _>(|_| toast::toast_colored(Color::Primary, "Primary toast message."))
979 })
980 .child::<Div, _>(|d| {
981 d.class("mb-2")
982 .child::<Div, _>(|_| toast::toast_colored(Color::Success, "Success toast message."))
983 })
984 .child::<Div, _>(|d| {
985 d.class("mb-2")
986 .child::<Div, _>(|_| toast::toast_colored(Color::Danger, "Danger toast message."))
987 })
988 })
989}
990
991fn tooltip_section() -> Element<Section> {
993 Element::<Section>::new()
994 .attr("id", "tooltips")
995 .class("mb-5")
996 .child::<H2, _>(|h| h.class("border-bottom pb-2").text("Tooltips"))
997 .child::<P, _>(|p| {
998 p.class("lead")
999 .text("Tooltips and popovers powered by CSS and JavaScript.")
1000 })
1001 .child::<H4, _>(|h| h.class("mt-4").text("Tooltips"))
1002 .child::<Div, _>(|d| {
1003 d.class("bd-example mb-3")
1004 .child::<Button, _>(|_| {
1005 tooltip::tooltip_button(
1006 Color::Secondary,
1007 "Tooltip on top",
1008 "Tooltip on top",
1009 tooltip::Placement::Top,
1010 )
1011 })
1012 .text(" ")
1013 .child::<Button, _>(|_| {
1014 tooltip::tooltip_button(
1015 Color::Secondary,
1016 "Tooltip on right",
1017 "Tooltip on right",
1018 tooltip::Placement::Right,
1019 )
1020 })
1021 .text(" ")
1022 .child::<Button, _>(|_| {
1023 tooltip::tooltip_button(
1024 Color::Secondary,
1025 "Tooltip on bottom",
1026 "Tooltip on bottom",
1027 tooltip::Placement::Bottom,
1028 )
1029 })
1030 .text(" ")
1031 .child::<Button, _>(|_| {
1032 tooltip::tooltip_button(
1033 Color::Secondary,
1034 "Tooltip on left",
1035 "Tooltip on left",
1036 tooltip::Placement::Left,
1037 )
1038 })
1039 })
1040 .child::<H4, _>(|h| h.class("mt-4").text("Popovers"))
1041 .child::<Div, _>(|d| {
1042 d.class("bd-example mb-3").child::<Button, _>(|_| {
1043 tooltip::popover_button(
1044 Color::Danger,
1045 "Click to toggle popover",
1046 "Popover title",
1047 "And here's some amazing content. It's very engaging.",
1048 tooltip::Placement::Right,
1049 )
1050 })
1051 })
1052}
1053
1054fn sidebar_items() -> alloc::vec::Vec<SidebarItem> {
1060 alloc::vec![
1061 SidebarItem::new("#accordion", "Accordion"),
1062 SidebarItem::new("#alerts", "Alerts").active(),
1063 SidebarItem::new("#badges", "Badges"),
1064 SidebarItem::new("#breadcrumb", "Breadcrumb"),
1065 SidebarItem::new("#buttons", "Buttons"),
1066 SidebarItem::new("#cards", "Cards"),
1067 SidebarItem::new("#carousel", "Carousel"),
1068 SidebarItem::new("#close-button", "Close Button"),
1069 SidebarItem::new("#collapse", "Collapse"),
1070 SidebarItem::new("#dropdowns", "Dropdowns"),
1071 SidebarItem::new("#list-group", "List Group"),
1072 SidebarItem::new("#modal", "Modal"),
1073 SidebarItem::new("#navbar", "Navbar"),
1074 SidebarItem::new("#offcanvas", "Offcanvas"),
1075 SidebarItem::new("#pagination", "Pagination"),
1076 SidebarItem::new("#placeholders", "Placeholders"),
1077 SidebarItem::new("#progress", "Progress"),
1078 SidebarItem::new("#spinners", "Spinners"),
1079 SidebarItem::new("#toasts", "Toasts"),
1080 SidebarItem::new("#tooltips", "Tooltips"),
1081 ]
1082}
1083
1084fn build_docs_page() -> Document {
1086 let sidebar_items = sidebar_items();
1087
1088 Document::new()
1089 .doctype()
1090 .root::<Html, _>(|html| {
1091 html.attr("lang", "en")
1092 .attr("data-bs-theme", "light")
1093 .child::<Head, _>(|h| {
1094 h.child::<Meta, _>(|m| m.attr("charset", "UTF-8"))
1095 .child::<Meta, _>(|m| {
1096 m.attr("name", "viewport")
1097 .attr("content", "width=device-width, initial-scale=1")
1098 })
1099 .child::<Title, _>(|t| t.text("Bootstrap Components - Documentation"))
1100 .child::<Link, _>(|l| {
1101 l.attr(
1102 "href",
1103 "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css",
1104 )
1105 .attr("rel", "stylesheet")
1106 })
1107 .child::<Style, _>(|s| {
1108 s.text(
1109 r"
1110 .bd-example {
1111 padding: 1.5rem;
1112 border: 1px solid #dee2e6;
1113 border-radius: 0.375rem;
1114 background-color: #fff;
1115 }
1116 .sidebar {
1117 position: sticky;
1118 top: 56px;
1119 height: calc(100vh - 56px);
1120 overflow-y: auto;
1121 }
1122 section {
1123 scroll-margin-top: 70px;
1124 }
1125 ",
1126 )
1127 })
1128 })
1129 .child::<Body, _>(|body| {
1130 body.child::<Nav, _>(|_| docs_navbar())
1131 .child::<Div, _>(|d| {
1132 d.class("d-flex")
1133 .child::<Nav, _>(|_| docs_sidebar(&sidebar_items))
1135 .child::<Main, _>(|m| {
1137 m.class("flex-grow-1 p-4")
1138 .child::<Div, _>(|d| {
1139 d.class("container-fluid")
1140 .child::<H1, _>(|h| {
1141 h.class("display-5 mb-4").text("Components")
1142 })
1143 .child::<P, _>(|p| {
1144 p.class("lead text-muted mb-5").text(
1145 "Dozens of reusable components built on top of Bootstrap.",
1146 )
1147 })
1148 .child::<Section, _>(|_| accordion_section())
1150 .child::<Section, _>(|_| alerts_section())
1151 .child::<Section, _>(|_| badges_section())
1152 .child::<Section, _>(|_| breadcrumb_section())
1153 .child::<Section, _>(|_| buttons_section())
1154 .child::<Section, _>(|_| cards_section())
1155 .child::<Section, _>(|_| carousel_section())
1156 .child::<Section, _>(|_| close_button_section())
1157 .child::<Section, _>(|_| collapse_section())
1158 .child::<Section, _>(|_| dropdown_section())
1159 .child::<Section, _>(|_| list_group_section())
1160 .child::<Section, _>(|_| modal_section())
1161 .child::<Section, _>(|_| navbar_section())
1162 .child::<Section, _>(|_| offcanvas_section())
1163 .child::<Section, _>(|_| pagination_section())
1164 .child::<Section, _>(|_| placeholder_section())
1165 .child::<Section, _>(|_| progress_section())
1166 .child::<Section, _>(|_| spinners_section())
1167 .child::<Section, _>(|_| toast_section())
1168 .child::<Section, _>(|_| tooltip_section())
1169 })
1170 })
1171 })
1172 .child::<Script, _>(|s| {
1174 s.attr(
1175 "src",
1176 "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js",
1177 )
1178 })
1179 })
1180 })
1181}
1182
1183fn main() {
1184 let doc = build_docs_page();
1185 let html = doc.render();
1186
1187 println!("{html}");
1188
1189 let component_count = 20; let example_count = 50; eprintln!("\n=== Bootstrap Documentation Site ===");
1194 eprintln!("Components documented: {component_count}");
1195 eprintln!("Examples rendered: {example_count}");
1196 eprintln!("HTML size: {} bytes", html.len());
1197 eprintln!("\nThis example demonstrates building a complete documentation site");
1198 eprintln!("similar to getbootstrap.com using ironhtml-bootstrap.");
1199}