htmx_components/server/
modal.rs1use super::transition::Transition;
2use super::yc_control::YcControl;
3use crate::server::attrs::Attrs;
4use rscx::{component, html, props};
5
6const MODALS_ID: &str = "modal-live-region";
7pub fn modal_target() -> String {
8 format!("#{}", MODALS_ID)
9}
10
11pub enum ModalSize {
12 Small,
13 Medium,
14 Large,
15 SmallScreen,
16 MediumScreen,
17 Custom(String),
18}
19
20#[props]
21pub struct ModalProps {
22 #[builder(default = ModalSize::Medium)]
23 size: ModalSize,
24
25 #[builder(setter(into))]
26 children: String,
27}
28
29#[component]
30pub fn Modal(props: ModalProps) -> String {
31 html! {
32 <YcControl
33 control="modal"
34 class="relative z-10"
35 role="dialog"
36 aria_labelledby="modal-title"
37 attrs=Attrs::with("aria-modal", "true".into())
38 >
39 <Transition
40 class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
41 enter="ease-out duration-300"
42 enter_from="opacity-0"
43 enter_to="opacity-100"
44 leave="ease-in duration-200"
45 leave_from="opacity-100"
46 leave_to="opacity-0"
47 />
48 <div class="fixed inset-0 z-10 w-screen overflow-y-auto">
49 <div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
50 <Transition
51 class={
52 let m_width = match props.size {
53 ModalSize::Small => "sm:max-w-sm".to_string(),
54 ModalSize::Medium => "sm:max-w-md".to_string(),
55 ModalSize::Large => "sm:max-w-lg".to_string(),
56 ModalSize::SmallScreen => "sm:max-w-screen-sm".to_string(),
57 ModalSize::MediumScreen => "sm:max-w-screen-md".to_string(),
58 ModalSize::Custom(width) => width,
59 };
60 format!("relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 w-full {} sm:p-6", m_width)
61 }
62 attrs=Attrs::with("data-modal-panel", "true".into())
63 enter="ease-out duration-300"
64 enter_from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
65 enter_to="opacity-100 translate-y-0 sm:scale-100"
66 leave="ease-in duration-200"
67 leave_from="opacity-100 translate-y-0 sm:scale-100"
68 leave_to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
69 >
70 <div>
71 {props.children}
72 </div>
73 </Transition>
74 </div>
75 </div>
76 </YcControl>
77 }
78}
79
80#[component]
93pub fn ConfirmDeleteModal() -> String {
94 html! {
95 <Modal>
96 <div class="sm:flex sm:items-start">
97 <div class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
98 <svg class="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
99 <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
100 </svg>
101 </div>
102 <div class="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
103 <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title" data-confirm-delete-title>Deactivate account</h3>
104 <div class="mt-2">
105 <p class="text-sm text-gray-500" data-confirm-delete-message>Are you sure you want to delete this item?.</p>
106 </div>
107 </div>
108 </div>
109 <div class="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
110 <button data-toggle-action="close" data-confirm-action="delete" type="button" class="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto">Delete</button>
111 <button data-toggle-action="close" type="button" class="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto">Cancel</button>
112 </div>
113 </Modal>
114 }
115}
116
117#[component]
123pub fn ModalLiveRegion() -> String {
124 html! {
125 <div id=MODALS_ID>
126 <section data-modal-content>
127 </section>
128 <template id="tpl-confirm-delete-modal">
129 <ConfirmDeleteModal />
130 </template>
131 </div>
132 }
133}