yew_bootstrap/component/
modal.rs

1use yew::prelude::*;
2
3/// Represents the optional size of a Modal dialog, described [here](https://getbootstrap.com/docs/5.1/components/modal/#optional-sizes)
4#[derive(Clone, PartialEq, Eq)]
5pub enum ModalSize {
6    ExtraLarge,
7    Large,
8    Normal,
9    Small,
10}
11
12impl Default for ModalSize {
13    fn default() -> Self {
14        ModalSize::Normal
15    }
16}
17
18/// # Modal dialog
19/// Modal dialog, parent of [ModalHeader], [ModalBody] and [ModalFooter].
20/// 
21/// See [ModalProps] for a listing of properties
22/// 
23/// ## Example
24/// ```rust
25/// use yew::prelude::*;
26/// use yew_bootstrap::component::{Modal, ModalHeader, ModalBody, ModalFooter, Button, ModalSize};
27/// use yew_bootstrap::util::Color;
28/// fn test() -> Html {
29///     html!{
30///         <Modal id="ExampleModal" size={ModalSize::Large}> // size defaults to Normal
31///             <ModalHeader title="Modal title" id="ExampleModal"/>
32///             <ModalBody>
33///                 <p>{"Modal body text goes here."}</p>
34///             </ModalBody>
35///             <ModalFooter>
36///                 <Button style={ Color::Secondary } modal_dismiss={ true }>{ "Close" }</Button>
37///                 <Button style={ Color::Primary }>{ "Save changes" }</Button>
38///             </ModalFooter>
39///         </Modal>
40///     }
41/// }
42/// ```
43pub struct Modal { }
44
45/// # Header for a [Modal] dialog
46/// See [ModalHeaderProps] for a listing of properties
47pub struct ModalHeader { }
48
49/// # Body for a [Modal] dialog
50/// See [ModalBodyProps] for a listing of properties
51pub struct ModalBody { }
52
53/// # Footer for a [Modal] dialog
54/// See [ModalFooterProps] for a listing of properties
55pub struct ModalFooter { }
56
57/// Properties for [ModalFooter]
58#[derive(Properties, Clone, PartialEq)]
59pub struct ModalFooterProps {
60    #[prop_or_default]
61    pub children: Children
62}
63
64impl Component for ModalFooter {
65    type Message = ();
66    type Properties = ModalFooterProps;
67
68    fn create(_ctx: &Context<Self>) -> Self {
69        Self {}
70    }
71
72    fn view(&self, ctx: &Context<Self>) -> Html {
73        let props = ctx.props();
74
75        html! {
76            <div class="modal-footer">
77                { for props.children.iter() }
78            </div>
79        }
80    }
81}
82
83/// Properties for [ModalHeader]
84#[derive(Properties, Clone, PartialEq)]
85pub struct ModalHeaderProps {
86    /// Title for the Modal dialog
87    #[prop_or_default]
88    pub title: String,
89
90    /// required for triggering open/close
91    #[prop_or_default]
92    pub id: String,
93}
94
95impl Component for ModalHeader {
96    type Message = ();
97    type Properties = ModalHeaderProps;
98
99    fn create(_ctx: &Context<Self>) -> Self {
100        Self {}
101    }
102
103    fn view(&self, ctx: &Context<Self>) -> Html {
104        let props = ctx.props();
105
106        html! {
107            <div class="modal-header">
108                <h5 class="modal-title" id={format!("#{}", props.id.clone())}>{props.title.clone()}</h5>
109                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
110            </div>
111        }
112    }
113}
114
115/// Properties for [ModalBody]
116#[derive(Properties, Clone, PartialEq)]
117pub struct ModalBodyProps {
118    #[prop_or_default]
119    pub children: Children
120}
121
122impl Component for ModalBody {
123    type Message = ();
124    type Properties = ModalBodyProps;
125
126    fn create(_ctx: &Context<Self>) -> Self {
127        Self {}
128    }
129
130    fn view(&self, ctx: &Context<Self>) -> Html {
131        let props = ctx.props();
132
133        html! {
134            <div class="modal-body">
135                { for props.children.iter() }
136            </div>
137        }
138    }
139}
140
141/// Properties for Modal
142#[derive(Properties, Clone, PartialEq)]
143pub struct ModalProps {
144    #[prop_or_default]
145    pub title: String,
146    /// required for triggering open/close
147    #[prop_or_default]
148    pub id: String,
149    /// modal body, typically [ModalHeader], [ModalBody] or [ModalFooter]
150    #[prop_or_default]
151    pub children: Children,
152    /// Size of the modal
153    #[prop_or_default]
154    pub size: ModalSize,
155}
156
157impl Component for Modal {
158    type Message = ();
159    type Properties = ModalProps;
160
161    fn create(_ctx: &Context<Self>) -> Self {
162        Self {}
163    }
164
165    fn view(&self, ctx: &Context<Self>) -> Html {
166        let props = ctx.props();
167
168        let mut dialog_classes = Classes::new();
169        dialog_classes.push("modal-dialog");
170
171        match props.size {
172            ModalSize::ExtraLarge => dialog_classes.push("modal-xl"),
173            ModalSize::Large => dialog_classes.push("modal-lg"),
174            ModalSize::Small => dialog_classes.push("modal-sm"),
175            _ => (),
176        }
177
178        html! {
179            <div class="modal" tabindex="-1" id={props.id.clone()}>
180                <div class={dialog_classes}>
181                    <div class="modal-content">
182                        { for props.children.iter() }
183                    </div>
184                </div>
185            </div>
186        }
187    }
188}