speki-app 0.1.0

ontological flashcard app
pub mod backside;
pub mod card_mastery;
pub mod cardref;
pub mod dropdown;
mod filtereditor;
pub mod frontside;

pub use backside::BackPut;
pub use cardref::CardRef;
pub use dropdown::DropDownMenu;
pub use filtereditor::*;
pub use frontside::{CardTy, FrontPut};
use speki_core::{card::CardId, collection::DynCard, set::SetExpr};

use dioxus::prelude::*;

use crate::{
    overlays::{
        card_selector::{CardSelector, MyClosure},
        cardviewer::CardViewer,
        OverlayEnum,
    },
    APP,
};

#[component]
pub fn RenderDependents(card_id: CardId, hidden: bool) -> Element {
    let show_graph = if !hidden {
        "opacity-100 visible"
    } else {
        "opacity-0 invisible"
    };

    let max_limit = 10;

    let (deps, qty): (Vec<(String, CardId)>, usize) = {
        let dep_ids = APP.read().inner().provider.cards.all_dependents(card_id);
        let qty = dep_ids.len();

        if dep_ids.len() > max_limit {
            (vec![], qty)
        } else {
            (
                dep_ids
                    .into_iter()
                    .map(|dep| {
                        (
                            APP.read()
                                .try_load_card(dep)
                                .map(|card| card.name().to_string())
                                .unwrap_or("<deleted card>".to_string()),
                            dep,
                        )
                    })
                    .collect(),
                qty,
            )
        }
    };

    let too_many = qty > max_limit;

    let children = rsx! {if too_many {
        button {
            class: "mb-1 p-1 bg-gray-100 rounded-md text-left",
            onclick: move|_|{
                let set = SetExpr::union_with(vec![DynCard::Dependents(card_id)]);
                let props = CardSelector::new(false, Default::default()).with_set(set);
                OverlayEnum::CardSelector(props).append();
            },
            "view {qty} dependents"
        }
    } else {
        for (name, id) in deps {
            button {
                class: "mb-1 p-1 bg-gray-100 rounded-md text-left",
                onclick: move|_|{
                    OverlayEnum::new_edit_card(id).append();
                },
                "{name}"
            }
        }
    }};

    rsx! {
        div {
            class: "flex flex-col {show_graph} w-full h-auto bg-white p-2 shadow-md rounded-md overflow-y-auto",

            div {
                class: "flex items-center justify-between mb-2",

                SectionWithTitle {
                    title: "Dependents".to_string(),
                    on_add: move |_| {
                        let props = CardViewer::new().with_dependency(card_id);
                        OverlayEnum::CardViewer(props).append();
                    },
                    children
                 }
            }

        }
    }
}

pub fn set_card_link(text: Signal<String>, alias: bool) {
    let mut eval = document::eval(
        r#"
        const sel = window.getSelection();
        dioxus.send(sel ? sel.toString() : "NO_SELECTION");
    "#,
    );

    spawn(async move {
        if let Ok(val) = eval.recv::<String>().await {
            if val.len() < 2 {
                return;
            }

            let theval = val.clone();
            let f = MyClosure::new(move |card: CardId| {
                let s = if alias {
                    format!("[[{}|{}]]", card, val)
                } else {
                    format!("[[{}]]", card)
                };
                text.clone().set(text.cloned().replace(&val, &s));
            });

            let props = CardSelector::new(false, vec![])
                .new_on_card_selected(f, true)
                .with_default_search(theval)
                .with_allow_new(true);
            OverlayEnum::CardSelector(props).append();
        }
    });
}

#[component]
pub fn SectionWithTitle(
    title: String,
    on_add: Option<EventHandler<()>>,
    children: Element,
    tooltip: Option<&'static str>,
) -> Element {
    let tooltip = tooltip.unwrap_or_default();
    rsx! {
        div {
            class: "flex flex-col",

            div {
                class: "flex items-center mb-1",
                h4 {
                    class: "font-bold",
                    title: tooltip,
                    "{title}"
                }
                if let Some(add) = on_add {
                    button {
                        class: "ml-4 p-1 hover:bg-gray-200 hover:border-gray-400 border border-transparent rounded-md transition-colors",
                        onclick: move |_| add.call(()),
                        ""
                    }
                }
            }

            div {
                class: "mt-2",
                {children}
            }
        }
    }
}