ftml/render/html/element/tabs.rs
1/*
2 * render/html/element/tabs.rs
3 *
4 * ftml - Library to parse Wikidot text
5 * Copyright (C) 2019-2026 Wikijump Team
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21use super::prelude::*;
22use crate::tree::Tab;
23use std::iter;
24
25pub fn render_tabview(ctx: &mut HtmlContext, tabs: &[Tab]) {
26 debug!("Rendering tabview (tabs {})", tabs.len());
27
28 // Generate IDs for each tab
29 let button_ids = generate_ids(ctx.random(), tabs.len());
30 let tab_ids = generate_ids(ctx.random(), tabs.len());
31
32 // Entire tab view
33 ctx.html()
34 .element("wj-tabs")
35 .attr(attr!(
36 "class" => "wj-tabs",
37 ))
38 .inner(|ctx| {
39 // Tab buttons
40 ctx.html()
41 .div()
42 .attr(attr!(
43 "class" => "wj-tabs-button-list",
44 "role" => "tablist",
45 ))
46 .inner(|ctx| {
47 for (i, tab) in tabs.iter().enumerate() {
48 let (tab_selected, tab_index) = if i == 0 {
49 ("true", "0")
50 } else {
51 ("false", "-1")
52 };
53
54 // Each tab button
55 ctx.html()
56 .element("wj-tabs-button")
57 .attr(attr!(
58 "class" => "wj-tabs-button",
59 "id" => &button_ids[i],
60 "role" => "tab",
61 "aria-label" => &tab.label,
62 "aria-selected" => tab_selected,
63 "aria-controls" => &tab_ids[i],
64 "tabindex" => tab_index,
65 ))
66 .contents(&tab.label);
67 }
68 });
69
70 // Tab panels
71 ctx.html()
72 .div()
73 .attr(attr!(
74 "class" => "wj-tabs-panel-list",
75 ))
76 .inner(|ctx| {
77 for (i, tab) in tabs.iter().enumerate() {
78 // Each tab panel
79 ctx.html()
80 .div()
81 .attr(attr!(
82 "class" => "wj-tabs-panel",
83 "id" => &tab_ids[i],
84 "role" => "tabpanel",
85 "aria-labelledby" => &button_ids[i],
86 "tabindex" => "0",
87 "hidden"; if i > 0,
88 ))
89 .contents(&tab.elements);
90 }
91 });
92 });
93}
94
95fn generate_ids(random: &mut Random, len: usize) -> Vec<String> {
96 iter::repeat_n((), len)
97 .map(|_| random.generate_html_id())
98 .collect()
99}