patternfly_yew/components/
date.rs1use crate::prelude::{
2 CalendarView, InputGroup, InputGroupItem, Popover, PopoverBody, PopoverContext, TextInput,
3};
4use chrono::{Local, NaiveDate, Weekday};
5use yew::prelude::*;
6
7#[derive(Clone, PartialEq, Properties)]
9pub struct DatePickerProperties {
10 #[prop_or_default]
12 pub disabled: bool,
13 #[prop_or_default]
15 pub onchange: Callback<NaiveDate>,
16 #[prop_or(String::from("YYYY-MM-DD"))]
18 pub placeholder: String,
19 #[prop_or_default]
20 pub rangestart: Option<NaiveDate>,
21 #[prop_or_default]
23 pub value: Option<NaiveDate>,
24 #[prop_or(Weekday::Mon)]
26 pub weekday_start: Weekday,
27}
28
29#[function_component(DatePicker)]
39pub fn date_picker(props: &DatePickerProperties) -> Html {
40 let value = use_state_eq(|| props.value);
41 let string_value =
42 use_state_eq(|| props.value.map(|date| date.to_string()).unwrap_or_default());
43
44 let callback_change_value = {
45 let onchange = props.onchange.clone();
46 use_callback(
47 (value.clone(), string_value.clone()),
48 move |new_date: NaiveDate, (value, string_value)| {
49 value.set(Some(new_date));
50 string_value.set(new_date.to_string());
51 onchange.emit(new_date);
52 },
53 )
54 };
55
56 let target = html! {
57 <button
58 class="pf-v5-c-button pf-m-control"
59 type="button"
60 aria-label="Toggle date picker"
61 disabled={props.disabled}
62 >
63 <i class="fas fa-calendar-alt" aria-hidden="true"></i>
64 </button>
65 };
66
67 let body = html_nested! (
68 <PopoverBody> <Body
72 date={value.unwrap_or_else(|| Local::now().date_naive())}
73 weekday_start={props.weekday_start}
74 rangestart={props.rangestart}
75 onchange={callback_change_value}
76 /> </PopoverBody>
77 );
78
79 let input_change = use_callback(string_value.clone(), |value, string_value| {
81 string_value.set(value);
82 });
83 {
85 let onchange = props.onchange.clone();
86 use_effect_with(
87 ((*string_value).clone(), value.clone()),
88 move |(string_value, value)| {
89 let new = match NaiveDate::parse_from_str(string_value, "%Y-%m-%d") {
90 Ok(v) => Some(v),
91 Err(_err) => None,
93 };
94
95 value.set(new);
96 if let Some(new) = new {
97 onchange.emit(new);
98 }
99 },
100 );
101 }
102
103 let input = html! (
105 <TextInput
106 onchange={input_change}
107 disabled={props.disabled}
108 value={(*string_value).clone()}
109 placeholder={props.placeholder.clone()}
110 />
111 );
112
113 html! {
114 <div class="pf-v5-c-date-picker">
115 <div class="pf-v5-c-date-picker__input">
116 <InputGroup>
117 <InputGroupItem>
118 {input}
119 </InputGroupItem>
120 <InputGroupItem>
121 <Popover
122 {target} {body}
123 no_padding=true
124 no_close=true
125 width_auto=true
126 />
127 </InputGroupItem>
128 </InputGroup>
129 </div>
130 </div>
131 }
132}
133
134#[derive(PartialEq, Properties)]
136struct BodyProperties {
137 date: NaiveDate,
138 weekday_start: Weekday,
139 rangestart: Option<NaiveDate>,
140 onchange: Callback<NaiveDate>,
141}
142
143#[function_component(Body)]
144fn body(props: &BodyProperties) -> Html {
145 let context = use_context::<PopoverContext>();
146 let onchange = use_callback(
147 (context, props.onchange.clone()),
148 |value, (context, callback)| {
149 if let Some(context) = context {
150 context.close();
151 }
152 callback.emit(value);
153 },
154 );
155
156 html!(
157 <CalendarView
158 date={props.date}
159 weekday_start={props.weekday_start}
160 rangestart={props.rangestart}
161 {onchange}
162 />
163 )
164}