Skip to main content

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}