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-v6-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" style="line-height: 1.5;"></i>
64 </button>
65 };
66
67 let body = html_nested! (
68 <PopoverBody>
72 <Body
73 date={value.unwrap_or_else(|| Local::now().date_naive())}
74 weekday_start={props.weekday_start}
75 rangestart={props.rangestart}
76 onchange={callback_change_value}
77 />
78 </PopoverBody>
79 );
80
81 let input_change = use_callback(string_value.clone(), |value, string_value| {
83 string_value.set(value);
84 });
85 {
87 let onchange = props.onchange.clone();
88 use_effect_with(
89 ((*string_value).clone(), value.clone()),
90 move |(string_value, value)| {
91 let new = NaiveDate::parse_from_str(string_value, "%Y-%m-%d").ok();
93
94 value.set(new);
95 if let Some(new) = new {
96 onchange.emit(new);
97 }
98 },
99 );
100 }
101
102 let input = html! (
104 <TextInput
105 onchange={input_change}
106 disabled={props.disabled}
107 value={(*string_value).clone()}
108 placeholder={props.placeholder.clone()}
109 />
110 );
111
112 html! {
113 <div class="pf-v6-c-date-picker">
114 <div class="pf-v6-c-date-picker__input">
115 <InputGroup>
116 <InputGroupItem>
117 {input}
118 </InputGroupItem>
119 <InputGroupItem>
120 <Popover
121 {target} {body}
122 no_padding=true
123 no_close=true
124 width_auto=true
125 />
126 </InputGroupItem>
127 </InputGroup>
128 </div>
129 </div>
130 }
131}
132
133#[derive(PartialEq, Properties)]
135struct BodyProperties {
136 date: NaiveDate,
137 weekday_start: Weekday,
138 rangestart: Option<NaiveDate>,
139 onchange: Callback<NaiveDate>,
140}
141
142#[function_component(Body)]
143fn body(props: &BodyProperties) -> Html {
144 let context = use_context::<PopoverContext>();
145 let onchange = use_callback(
146 (context, props.onchange.clone()),
147 |value, (context, callback)| {
148 if let Some(context) = context {
149 context.close();
150 }
151 callback.emit(value);
152 },
153 );
154
155 html!(
156 <CalendarView
157 date={props.date}
158 weekday_start={props.weekday_start}
159 rangestart={props.rangestart}
160 {onchange}
161 />
162 )
163}