ftml/render/html/element/
collapsible.rs

1/*
2 * render/html/element/collapsible.rs
3 *
4 * ftml - Library to parse Wikidot text
5 * Copyright (C) 2019-2025 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::{AttributeMap, Element};
23
24#[derive(Debug, Copy, Clone)]
25pub struct Collapsible<'a> {
26    elements: &'a [Element<'a>],
27    attributes: &'a AttributeMap<'a>,
28    start_open: bool,
29    show_text: Option<&'a str>,
30    hide_text: Option<&'a str>,
31    show_top: bool,
32    show_bottom: bool,
33}
34
35impl<'a> Collapsible<'a> {
36    #[inline]
37    pub fn new(
38        elements: &'a [Element<'a>],
39        attributes: &'a AttributeMap<'a>,
40        start_open: bool,
41        show_text: Option<&'a str>,
42        hide_text: Option<&'a str>,
43        show_top: bool,
44        show_bottom: bool,
45    ) -> Self {
46        Collapsible {
47            elements,
48            attributes,
49            start_open,
50            show_text,
51            hide_text,
52            show_top,
53            show_bottom,
54        }
55    }
56}
57
58pub fn render_collapsible(ctx: &mut HtmlContext, collapsible: Collapsible) {
59    let Collapsible {
60        elements,
61        attributes,
62        start_open,
63        show_text,
64        hide_text,
65        show_top,
66        show_bottom,
67    } = collapsible;
68
69    debug!(
70        "Rendering collapsible (elements length {}, start-open {}, show-text {}, hide-text {}, show-top {}, show-bottom {})",
71        elements.len(),
72        start_open,
73        show_text.unwrap_or("<default>"),
74        hide_text.unwrap_or("<default>"),
75        show_top,
76        show_bottom,
77    );
78
79    let show_text = show_text
80        .unwrap_or_else(|| ctx.handle().get_message(ctx.language(), "collapsible-open"));
81
82    let hide_text = hide_text
83        .unwrap_or_else(|| ctx.handle().get_message(ctx.language(), "collapsible-hide"));
84
85    ctx.html()
86        .details()
87        .attr(attr!(
88            "class" => "wj-collapsible",
89            "open"; if start_open,
90            "data-show-top"; if show_top,
91            "data-show-bottom"; if show_bottom;;
92            attributes,
93        ))
94        .inner(|ctx| {
95            // Open/close button
96            ctx.html()
97                .summary()
98                .attr(attr!(
99                    "class" => "wj-collapsible-button wj-collapsible-button-top",
100                ))
101                .inner(|ctx| {
102                    // Block is folded text
103                    ctx.html()
104                        .span()
105                        .attr(attr!("class" => "wj-collapsible-show-text"))
106                        .contents(show_text);
107
108                    // Block is unfolded text
109                    ctx.html()
110                        .span()
111                        .attr(attr!("class" => "wj-collapsible-hide-text"))
112                        .contents(hide_text);
113                });
114
115            // Content block
116            ctx.html()
117                .div()
118                .attr(attr!("class" => "wj-collapsible-content"))
119                .contents(elements);
120
121            // Bottom open/close button
122            if show_bottom {
123                ctx.html()
124                    .element("wj-collapsible-button-bottom")
125                    .attr(attr!(
126                        "class" => "wj-collapsible-button wj-collapsible-button-bottom",
127                    ))
128                    .inner(|ctx| {
129                        // Block is unfolded text
130                        ctx.html()
131                            .span()
132                            .attr(attr!("class" => "wj-collapsible-hide-text"))
133                            .contents(hide_text);
134                    });
135            }
136        });
137}