#leptos #component #modal #basic #success #status #modals

h_modals

A leptos component library using tailwindcss for basic modals

7 releases

0.2.6 May 13, 2024
0.2.5 May 13, 2024
0.2.2 Apr 4, 2024
0.2.1 Feb 19, 2024
0.1.9 Feb 16, 2024

#1806 in Web programming

MIT license

22KB
265 lines

status modal usage:

let modal = create_rw_signal(false);

view! {
    <button class = "btn btn-sm" on:click = move |_| modal.set(true)>Open Modal</button>

    <StatusModal
        signal=modal
        title="TITLE!".to_string()
        description="The description goes here.".to_string()
        position=Position::TopCenter
        status=ComponentStatus::Success
    />
}

confirm modal usage:

let signal = create_rw_signal(false);

let action = create_action(move |_input: &()| async move {
    let result = server_function().await;
    match result {
        Ok(result) => {
            log::info!("Action Successful!");
            signal.set(false); // Close the modal after successful action.
            return result
        },
        Err(err) => {
            log::error!("Server Function Error: {:?}", err);
            signal.set(false); // Close the modal after unsuccessful action.
            return format!("Server Function Error: {:?}", err);
        }
    }
});

let pending_signal = action.pending(); // Renders a spinner when the action is still pending.

let confirm_modal_fn = move || {
    action.dispatch(()); // Calls the actual action inside a closure function.
};

view! {
    <button class = "btn btn-sm" on:click = move |_| signal.set(true)>Open Modal</button>
    <ConfirmModal
        signal = signal
        title = "APPROVE?".to_string()
        description = "Are you sure you want to approve?".to_string()
        function = confirm_modal_fn
        pending_signal = pending_signal
    />
}

pub async fn server_function() -> Result<String, ServerFnError>{
    use gloo_timers::future::sleep;
    use std::time::Duration;

    log::info!("Server Function!");

    sleep(Duration::from_secs(1)).await; // Add a delay to simulate pending data.

    return Ok(String::from("This came from a server function."))

}

what goes inside the confirm modal:


let position_class = match custom_position_class {
    None => {
        match position {
            Some(Position::TopLeft) => "items-start justify-start".to_string(),
            Some(Position::TopCenter) => "items-start justify-center".to_string(),
            Some(Position::TopRight) => "items-start justify-end".to_string(),
            Some(Position::Center) => "items-center justify-center".to_string(),
            Some(Position::BottomLeft) => "items-end justify-start".to_string(),
            Some(Position::BottomCenter) => "items-end justify-center".to_string(),
            Some(Position::BottomRight) => "items-end justify-end".to_string(),
            _ => "items-center justify-center".to_string(),
        }
    },
    Some(custom_class) => custom_class,
};

let on_click = move |_| function();

view! {
    <Show when=move || signal.get() fallback=|| ()>
        <div class = "blur-bg">
            <div class=format!("sticky top-0 flex h-screen {}", position_class.clone())>
                <div class=format!("flex flex-col gap-4 m-2 modal-box {}", status_class)>
                    <h3 class="font-bold text-2xl">{title.clone()}</h3>
                    <p class="py-4 text-lg">{description.clone()}</p>
                    <div class="modal-action">
                        <button class="btn btn-error btn-sm rounded" prop:disabled=pending_signal title="Cancel" on:click = move |_| signal.set(false)>Cancel</button>
                        {
                            match pending_signal.get() {
                                true => view! {
                                    <button class="btn btn-success btn-sm rounded" title="Confirm"><span class="loading loading-spinner loading-sm"></span></button>
                                }.into_any(),
                                false => view! {
                                    <button class="btn btn-success btn-sm rounded" title="Confirm" on:click = on_click.clone()>Confirm</button>
                                }.into_any(),
                            }
                        }

                    </div>
                </div>
            </div>
        </div>
    </Show>
}

what goes inside the status modal:


let status_class = match status {
    Some(ComponentStatus::Info) => "bg-info",
    Some(ComponentStatus::Success) => "bg-success",
    Some(ComponentStatus::Neutral) => "bg-neutral",
    Some(ComponentStatus::Warning) => "bg-warning",
    Some(ComponentStatus::Error) => "bg-error",
    _ => "",
};

let text_header_class = match text_color {
    Some(ComponentStatus::Info) => "text-info",
    Some(ComponentStatus::Success) => "text-success",
    Some(ComponentStatus::Neutral) => "text-neutral",
    Some(ComponentStatus::Warning) => "text-warning",
    Some(ComponentStatus::Error) => "text-error",
    _ => "text-black",
};

let text_desc_class = match text_color {
    Some(ComponentStatus::Info) => "text-black",
    Some(ComponentStatus::Success) => "text-black",
    Some(ComponentStatus::Neutral) => "",
    Some(ComponentStatus::Warning) => "text-black",
    Some(ComponentStatus::Error) => "text-black",
    _ => "",
};

let button_class = match button_status {
    Some(ComponentStatus::Info) => "btn btn-sm rounded btn-info",
    Some(ComponentStatus::Success) => "btn btn-sm rounded btn-success",
    Some(ComponentStatus::Neutral) => "btn btn-sm rounded",
    Some(ComponentStatus::Warning) => "btn btn-sm rounded btn-warning",
    Some(ComponentStatus::Error) => "btn btn-sm rounded btn-error",
    _ => "btn btn-sm rounded",
};

let position_class = match custom_position_class {
    None => {
        match position {
            Some(Position::TopLeft) => "items-start justify-start".to_string(),
            Some(Position::TopCenter) => "items-start justify-center".to_string(),
            Some(Position::TopRight) => "items-start justify-end".to_string(),
            Some(Position::Center) => "items-center justify-center".to_string(),
            Some(Position::BottomLeft) => "items-end justify-start".to_string(),
            Some(Position::BottomCenter) => "items-end justify-center".to_string(),
            Some(Position::BottomRight) => "items-end justify-end".to_string(),
            _ => "items-center justify-center".to_string(),
        }
    },
    Some(custom_class) => custom_class,
};

view! {
    <Show when=move || signal.get() fallback=|| ()>
        <div class = "blur-bg">
            <div class=format!("sticky top-0 flex h-screen {}", position_class.clone())>
                <div class=format!("flex flex-col gap-4 m-2 modal-box {}", status_class)>
                    <h3 class=format!("font-bold text-2xl {}", text_header_class)>{title.clone()}</h3>
                    <p class=format!("py-4 text-lg{}", text_desc_class)>{description.clone()}</p>
                    <div class="modal-action">
                        <button class={button_class} title="Close" on:click = move |_| signal.set(false)>Close</button>
                    </div>
                </div>
            </div>
        </div>
    </Show>
}

Dependencies

~21–33MB
~501K SLoC