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" />
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>{ input }</InputGroupItem>
117 <InputGroupItem>
118 <Popover {target} {body} no_padding=true no_close=true width_auto=true />
119 </InputGroupItem>
120 </InputGroup>
121 </div>
122 </div>
123 }
124}
125
126#[derive(PartialEq, Properties)]
128struct BodyProperties {
129 date: NaiveDate,
130 weekday_start: Weekday,
131 rangestart: Option<NaiveDate>,
132 onchange: Callback<NaiveDate>,
133}
134
135#[function_component(Body)]
136fn body(props: &BodyProperties) -> Html {
137 let context = use_context::<PopoverContext>();
138 let onchange = use_callback(
139 (context, props.onchange.clone()),
140 |value, (context, callback)| {
141 if let Some(context) = context {
142 context.close();
143 }
144 callback.emit(value);
145 },
146 );
147
148 html!(
149 <CalendarView
150 date={props.date}
151 weekday_start={props.weekday_start}
152 rangestart={props.rangestart}
153 {onchange}
154 />
155 )
156}