ftml_viewer_components/components/
documents.rs

1use super::Gotto;
2use super::TOCSource;
3use crate::iterate;
4use crate::FTMLDocumentSetup;
5use flams_ontology::uris::NarrativeURI;
6use flams_ontology::uris::{DocumentElementURI, DocumentURI, NameStep};
7use flams_web_utils::components::wait_local;
8use flams_web_utils::{do_css, inject_css};
9use leptos::prelude::*;
10use leptos_posthoc::DomStringCont;
11
12#[cfg(feature = "omdoc")]
13#[component]
14pub fn DocumentFromURI(
15    uri: DocumentURI,
16    #[prop(optional, into)] toc: TOCSource,
17    #[prop(optional, into)] gottos: Vec<Gotto>,
18    #[prop(optional)] omdoc: crate::components::omdoc::OMDocSource,
19) -> impl IntoView {
20    wait_local(
21        move || {
22            tracing::info!("fetching {uri}");
23            let fut = crate::remote::server_config.full_doc(uri.clone());
24            async move { fut.await.ok() }
25        },
26        move |(uri, css, html)| {
27            for c in css {
28                do_css(c);
29            }
30            view!(<DocumentString html uri toc=toc.clone() gottos=gottos.clone() omdoc=omdoc.clone()/>)
31        },
32        "Error loading document reference".to_string(),
33    )
34}
35
36#[component]
37pub fn FragmentFromURI(uri: DocumentElementURI) -> impl IntoView {
38    let uricl = uri.clone();
39    wait_local(
40        move || {
41            tracing::info!("fetching {uri}");
42            let fut = crate::remote::server_config.paragraph(uri.clone());
43            async move { fut.await.ok() }
44        },
45        move |(_, css, html)| {
46            for c in css {
47                do_css(c);
48            }
49            view!(<FragmentString html uri=uricl.clone()/>)
50        },
51        "Error loading document fragment".to_string(),
52    )
53}
54
55#[cfg(not(feature = "omdoc"))]
56#[component]
57pub fn DocumentFromURI(
58    uri: DocumentURI,
59    #[prop(optional, into)] toc: TOCSource,
60    #[prop(optional, into)] gottos: Vec<Gotto>,
61) -> impl IntoView {
62    wait_local(
63        move || {
64            tracing::info!("fetching {uri}");
65            let fut = crate::remote::server_config.full_doc(uri.clone());
66            async move { fut.await.ok() }
67        },
68        move |(uri, css, html)| {
69            for c in css {
70                do_css(c);
71            }
72            view!(<DocumentString html uri gottos=gottos.clone() toc=toc.clone()/>)
73        },
74        "Error loading document reference".to_string(),
75    )
76}
77
78#[component]
79pub fn FragmentString(
80    html: String,
81    #[prop(optional)] uri: Option<DocumentElementURI>,
82) -> impl IntoView {
83    use leptos::context::Provider;
84    use leptos::either::EitherOf3;
85    let name = uri.as_ref().map(|uri| uri.name().last_name().clone());
86    let needs_suffix = uri
87        .as_ref()
88        .map(|uri| uri.name().steps().len() > 1)
89        .unwrap_or_default();
90    let doc = uri
91        .as_ref()
92        .map_or_else(DocumentURI::no_doc, |d| d.document().clone());
93    view! {<FTMLDocumentSetup uri=doc>{
94        match name {
95            Some(name) if needs_suffix => {
96                let nuri = NarrativeURI::Element(flams_utils::unwrap!(uri).parent());
97                EitherOf3::A(view!{
98                    <Provider value=ForcedName(Some(name))>
99                    <Provider value=nuri>
100                        <DomStringCont html cont=iterate/>
101                    </Provider>
102                    </Provider>
103                })
104            },
105            Some(name) => EitherOf3::B(view!{
106                <Provider value=ForcedName(Some(name))>
107                    <DomStringCont html cont=iterate/>
108                </Provider>
109            }),
110            _ => EitherOf3::C(view!{
111                <DomStringCont html cont=iterate/>
112            })
113        }
114    }</FTMLDocumentSetup>}
115}
116
117#[derive(Clone, Debug, Default)]
118pub struct ForcedName(Option<NameStep>);
119impl ForcedName {
120    pub fn update(&self, uri: &DocumentElementURI) -> DocumentElementURI {
121        match self.0.as_ref() {
122            Some(n) => {
123                let name = uri.name().clone();
124                let doc = uri.document().clone();
125                doc & name.with_last_name(n.clone())
126            }
127            _ => uri.clone(),
128        }
129    }
130}
131
132#[cfg(feature = "omdoc")]
133#[component]
134pub fn DocumentString(
135    html: String,
136    #[prop(optional)] uri: Option<DocumentURI>,
137    #[prop(optional, into)] toc: TOCSource,
138    #[prop(optional, into)] gottos: Vec<Gotto>,
139    #[prop(optional)] omdoc: crate::components::omdoc::OMDocSource,
140) -> impl IntoView {
141    use thaw::Flex;
142    let uri = uri.unwrap_or_else(DocumentURI::no_doc);
143    let burger = !matches!(
144        (&toc, &omdoc),
145        (TOCSource::None, crate::components::omdoc::OMDocSource::None)
146    );
147    view! {<FTMLDocumentSetup uri><Flex>
148        <div><DomStringCont html cont=iterate/></div>
149        {if burger {
150            Some(do_toc_sidebar(toc,gottos,omdoc))
151        } else {None}}
152    </Flex></FTMLDocumentSetup>
153    }
154}
155
156#[cfg(not(feature = "omdoc"))]
157#[component]
158pub fn DocumentString(
159    html: String,
160    #[prop(optional)] uri: Option<DocumentURI>,
161    #[prop(optional, into)] toc: TOCSource,
162    #[prop(optional, into)] gottos: Vec<Gotto>,
163) -> impl IntoView {
164    use thaw::Flex;
165    let uri = uri.unwrap_or_else(DocumentURI::no_doc);
166    let burger = !matches!(toc, TOCSource::None);
167    view! {<FTMLDocumentSetup uri><Flex>
168        <div><DomStringCont html cont=iterate/></div>
169        {if burger {
170            Some(do_toc_sidebar(toc,gottos))
171        } else {None}}
172    </Flex></FTMLDocumentSetup>
173    }
174}
175
176#[cfg(feature = "omdoc")]
177fn do_toc_sidebar(
178    toc: TOCSource,
179    gottos: Vec<Gotto>,
180    omdoc: crate::components::omdoc::OMDocSource,
181) -> impl IntoView {
182    inject_css("ftml-toc", include_str!("./toc.css"));
183    //use flams_web_utils::components::Burger;
184    use flams_web_utils::components::ClientOnly;
185    use thaw::{Button, ButtonShape, ButtonSize, Scrollbar};
186    let visible = RwSignal::new(true);
187    let display = Memo::new(move |_| {
188        if visible.get() {
189            "ftml-toc-visible"
190        } else {
191            "ftml-toc-invisible"
192        }
193    });
194
195    let hl_option: RwSignal<crate::HighlightOption> = expect_context();
196    let value = RwSignal::new(hl_option.get_untracked().as_str().to_string());
197    Effect::new(move || {
198        if let Some(v) = crate::HighlightOption::from_str(&value.get()) {
199            if hl_option.get_untracked() != v {
200                hl_option.set(v);
201            }
202        }
203    });
204    use thaw::Select;
205    let select = move || {
206        if hl_option.get() == crate::HighlightOption::None {
207            None
208        } else {
209            Some(
210                view!("Symbol Highlighting: "<Select value default_value=value.get_untracked() size=thaw::SelectSize::Small>
211            <option class="ftml-comp">{crate::HighlightOption::Colored.as_str()}</option>
212            <option class="ftml-comp-subtle">{crate::HighlightOption::Subtle.as_str()}</option>
213            <option>{crate::HighlightOption::Off.as_str()}</option>
214        </Select>),
215            )
216        }
217    };
218
219    crate::components::do_toc(toc, gottos, move |v| {
220        view! {<div class="ftml-toc-sidebar">
221        <ClientOnly>
222            //<div style="width:0;height:0;margin-left:auto;">
223            //    <div style="position:fixed">
224            //<div style="max-height:600px">
225            //        <InlineDrawer open=visible position=DrawerPosition::Right>
226            //        <DrawerBody>
227                        <Button
228                            //appearance=ButtonAppearance::Subtle
229                            shape=ButtonShape::Circular
230                            size=ButtonSize::Small
231                            on_click=move |_| visible.set(!visible.get_untracked())
232                        >{move || if visible.get() {"⌃"} else {"⌄"}}</Button>
233                        <div class=display>
234                        {select}
235                        {crate::components::omdoc::do_omdoc(omdoc)}
236                        <Scrollbar style="width:fit-content;max-height:575px;">{v}</Scrollbar>
237                        </div>
238            //        </DrawerBody>
239            //        </InlineDrawer>
240            //</div>
241            //    </div>
242            //</div>
243        </ClientOnly>
244        //<Burger>{crate::components::omdoc::do_omdoc(omdoc)}{v}</Burger>
245        </div>}
246    })
247}
248
249#[cfg(not(feature = "omdoc"))]
250fn do_toc_sidebar(toc: crate::components::TOCSource, gottos: Vec<Gotto>) -> impl IntoView {
251    //use flams_web_utils::components::Burger;
252    use flams_web_utils::components::ClientOnly;
253    use thaw::{
254        Button, ButtonAppearance, ButtonShape, ButtonSize, DrawerBody, DrawerPosition,
255        InlineDrawer, Scrollbar,
256    };
257    inject_css("ftml-toc", include_str!("./toc.css"));
258    let visible = RwSignal::new(true);
259    let display = Memo::new(move |_| {
260        if visible.get() {
261            "ftml-toc-visible"
262        } else {
263            "ftml-toc-invisible"
264        }
265    });
266
267    let hl_option: RwSignal<crate::HighlightOption> = expect_context();
268    let value = RwSignal::new(hl_option.get_untracked().as_str().to_string());
269    Effect::new(move || {
270        if let Some(v) = crate::HighlightOption::from_str(&value.get()) {
271            if hl_option.get_untracked() != v {
272                hl_option.set(v);
273            }
274        }
275    });
276    use thaw::Select;
277    let select = move || {
278        if hl_option.get() == crate::HighlightOption::None {
279            None
280        } else {
281            Some(view!(<Select value size=thaw::SelectSize::Small>
282            <option>{crate::HighlightOption::Colored.as_str()}</option>
283            <option>{crate::HighlightOption::Subtle.as_str()}</option>
284            <option>{crate::HighlightOption::Off.as_str()}</option>
285        </Select>))
286        }
287    };
288
289    crate::components::do_toc(toc, gottos, move |v| {
290        view! {<div class="ftml-toc-sidebar">
291            <ClientOnly>
292                <Button
293                    shape=ButtonShape::Circular
294                    size=ButtonSize::Small
295                    on_click=move |_| visible.set(!visible.get_untracked())
296                >{move || if visible.get() {"⌃"} else {"⌄"}}</Button>
297                <div class=display>{select}
298                <Scrollbar style="width:fit-content;max-height:575px;">{v}</Scrollbar>
299                </div>
300            </ClientOnly>
301        </div>}
302    })
303}