ftml_viewer_components/components/
mod.rs

1pub mod counters;
2pub mod documents;
3pub(crate) mod inputref;
4pub(crate) mod navigation;
5#[cfg(feature = "omdoc")]
6pub mod omdoc;
7pub(crate) mod paragraphs;
8pub mod problem;
9pub(crate) mod proofs;
10pub(crate) mod sections;
11pub(crate) mod terms;
12mod toc;
13
14use flams_ontology::{
15    narration::{
16        paragraphs::{ParagraphFormatting, ParagraphKind},
17        problems::CognitiveDimension,
18        LOKind,
19    },
20    uris::DocumentElementURI,
21};
22use inputref::InInputRef;
23pub use inputref::{IfInputref, InputRef};
24pub use toc::*;
25
26use ftml_extraction::{open::OpenFTMLElement, prelude::FTMLElements};
27use leptos::prelude::*;
28use leptos_posthoc::{DomChildrenCont, DomCont, OriginalNode};
29
30use counters::{LogicalLevel, SectionCounters};
31
32use crate::AllowHovers;
33
34#[component]
35pub fn FTMLComponents(
36    #[prop(optional)] in_math: bool,
37    elements: FTMLElements,
38    orig: OriginalNode,
39) -> impl IntoView {
40    if in_math {
41        leptos::either::Either::Left(do_components::<true>(0, elements, orig))
42    } else {
43        leptos::either::Either::Right(do_components::<false>(0, elements, orig))
44    }
45}
46
47fn do_components<const MATH: bool>(
48    skip: usize,
49    elements: FTMLElements,
50    orig: OriginalNode,
51) -> impl IntoView {
52    if let Some(next) = elements.iter().rev().nth(skip) {
53        //tracing::debug!("Doing {next:?} ({:?})",std::thread::current().id());
54        match next {
55            OpenFTMLElement::Section { uri, .. } => sections::section(uri.clone(), move || {
56                do_components::<MATH>(skip + 1, elements, orig)
57            })
58            .into_any(),
59            OpenFTMLElement::SkipSection => {
60                sections::skip(move || do_components::<MATH>(skip + 1, elements, orig)).into_any()
61            }
62            OpenFTMLElement::Inputref { uri, id } => inputref::inputref(uri.clone(), id).into_any(),
63            OpenFTMLElement::IfInputref(b) => inputref::if_inputref(*b, orig).into_any(),
64            OpenFTMLElement::OpenTerm { term, .. } => {
65                #[cfg(feature = "omdoc")]
66                if MATH {
67                    let term = term.clone();
68                    terms::math_term(skip, elements, orig, term).into_any()
69                } else {
70                    terms::do_term::<_, MATH>(term.clone(), move || {
71                        do_components::<MATH>(skip + 1, elements, orig)
72                    })
73                    .into_any()
74                }
75
76                #[cfg(not(feature = "omdoc"))]
77                terms::do_term::<_, MATH>(term.clone(), move || {
78                    do_components::<MATH>(skip + 1, elements, orig)
79                })
80                .into_any()
81            }
82            OpenFTMLElement::DefComp => terms::do_comp::<_, MATH>(
83                true,
84                move || view!(<DomCont skip_head=true orig=orig.clone() cont=crate::iterate/>),
85            )
86            .into_any(),
87            OpenFTMLElement::Comp | OpenFTMLElement::MainComp if AllowHovers::get() => {
88                terms::do_comp::<_, MATH>(
89                    false,
90                    move || view!(<DomCont skip_head=true orig=orig.clone() cont=crate::iterate/>),
91                )
92                .into_any()
93            }
94            OpenFTMLElement::Comp | OpenFTMLElement::MainComp => {
95                view!(<DomCont skip_head=true orig=orig.clone() cont=crate::iterate/>).into_any()
96            }
97            OpenFTMLElement::Definiendum(_) => terms::do_definiendum::<_, MATH>(move || {
98                do_components::<MATH>(skip + 1, elements, orig)
99            })
100            .into_any(),
101            OpenFTMLElement::Arg(arg) => terms::do_arg(orig, *arg, move |orig| {
102                do_components::<MATH>(skip + 1, elements, orig)
103            })
104            .into_any(),
105            OpenFTMLElement::Problem {
106                uri,
107                autogradable,
108                sub_problem,
109                styles,
110                ..
111            } => {
112                let styles = styles.clone();
113                problem::problem(
114                    &uri.clone(),
115                    *autogradable,
116                    *sub_problem,
117                    styles,
118                    move || do_components::<MATH>(skip + 1, elements, orig),
119                )
120                .into_any()
121            }
122            OpenFTMLElement::ProblemHint => {
123                problem::hint(move || do_components::<MATH>(skip + 1, elements, orig)).into_any()
124            }
125            OpenFTMLElement::ProblemSolution(id) => {
126                let id = id.clone();
127                problem::solution(skip + 1, elements, orig, id).into_any()
128            }
129            OpenFTMLElement::ProblemGradingNote => {
130                problem::gnote(skip + 1, elements, orig).into_any()
131            }
132            OpenFTMLElement::ChoiceBlock { multiple, inline } => {
133                problem::choice_block(*multiple, *inline, move || {
134                    do_components::<MATH>(skip + 1, elements, orig)
135                })
136                .into_any()
137            }
138            OpenFTMLElement::ProblemChoice => problem::problem_choice(move || {
139                do_components::<MATH>(skip + 1, elements.clone(), orig.clone())
140            })
141            .into_any(),
142            OpenFTMLElement::Fillinsol(wd) => problem::fillinsol(*wd).into_any(),
143            OpenFTMLElement::SetSectionLevel(level) => {
144                let in_inputref = use_context::<InInputRef>().map(|i| i.0).unwrap_or(false);
145                update_context::<SectionCounters, _>(|current| {
146                    if !in_inputref && matches!(current.current_level(), LogicalLevel::None) {
147                        current.max = *level;
148                    } else if !in_inputref {
149                        tracing::error!("ftml:set-section-level: Section already started");
150                    }
151                });
152                ().into_any()
153            }
154            OpenFTMLElement::Slide(uri) => paragraphs::slide(uri.clone(), move || {
155                do_components::<MATH>(skip + 1, elements, orig)
156            })
157            .into_any(),
158            OpenFTMLElement::SlideNumber => paragraphs::slide_number().into_any(),
159            OpenFTMLElement::Paragraph {
160                uri,
161                kind: ParagraphKind::Proof,
162                formatting:
163                    formatting @ (ParagraphFormatting::Block | ParagraphFormatting::Collapsed),
164                ..
165            } => proofs::proof(
166                uri.clone(),
167                *formatting == ParagraphFormatting::Collapsed,
168                move || view!(<DomChildrenCont orig cont=crate::iterate />),
169            )
170            .into_any(),
171            OpenFTMLElement::Paragraph {
172                kind: ParagraphKind::SubProof,
173                uri,
174                formatting:
175                    formatting @ (ParagraphFormatting::Block | ParagraphFormatting::Collapsed),
176                ..
177            } => proofs::subproof(
178                uri.clone(),
179                *formatting == ParagraphFormatting::Collapsed,
180                move || view!(<DomChildrenCont orig cont=crate::iterate />),
181            )
182            .into_any(),
183            OpenFTMLElement::Paragraph {
184                kind,
185                formatting: ParagraphFormatting::Block,
186                uri,
187                styles,
188                ..
189            } => paragraphs::paragraph(*kind, uri.clone(), styles.clone(), move || {
190                do_components::<MATH>(skip + 1, elements, orig)
191            })
192            .into_any(),
193            OpenFTMLElement::Paragraph { .. } => {
194                do_components::<MATH>(skip + 1, elements, orig).into_any()
195            }
196            OpenFTMLElement::Title => {
197                sections::title(move || view!(<DomChildrenCont orig cont=crate::iterate />))
198                    .into_any()
199            }
200            OpenFTMLElement::ProofTitle => proofs::proof_title(orig).into_any(),
201            OpenFTMLElement::SubproofTitle => proofs::subproof_title(orig).into_any(),
202            /*OpenFTMLElement::ProofHide(b) =>
203            proofs::proof_hide(
204                *b,
205                move || view!(<DomChildrenCont orig cont=crate::iterate />),
206            )
207            .into_any(),
208            {
209                view!(<DomCont skip_head=true orig cont=crate::iterate/>).into_any()
210            }*/
211            OpenFTMLElement::ProofBody => proofs::proof_body(orig).into_any(),
212            _ => todo!(),
213        }
214    } else {
215        view!(<DomCont skip_head=true orig cont=crate::iterate/>).into_any()
216    }
217}
218
219#[derive(Clone, Debug, PartialEq, Eq)]
220pub struct LOs {
221    pub definitions: Vec<DocumentElementURI>,
222    pub examples: Vec<DocumentElementURI>,
223    pub problems: Vec<(bool, DocumentElementURI, CognitiveDimension)>,
224}
225
226pub(crate) trait IntoLOs {
227    fn lo_sort(self) -> LOs;
228}
229
230impl IntoLOs for Vec<(DocumentElementURI, LOKind)> {
231    fn lo_sort(self) -> LOs {
232        let mut definitions = Vec::new();
233        let mut examples = Vec::new();
234        let mut problems = Vec::new();
235        for (uri, k) in self {
236            match k {
237                LOKind::Definition => definitions.push(uri),
238                LOKind::Example => examples.push(uri),
239                LOKind::Problem(cd) => problems.push((false, uri, cd)),
240                LOKind::SubProblem(cd) => problems.push((true, uri, cd)),
241            }
242        }
243        LOs {
244            definitions,
245            examples,
246            problems,
247        }
248    }
249}