leptos_use/
use_calendar.rs1use crate::core::MaybeRwSignal;
2use chrono::*;
3use default_struct_builder::DefaultBuilder;
4use leptos::prelude::*;
5use std::ops::Deref;
6
7pub fn use_calendar() -> UseCalendarReturn<
75 impl Fn() + Clone + Send + Sync,
76 impl Fn() + Clone + Send + Sync,
77 impl Fn(&NaiveDate) + Clone + Send + Sync,
78 impl Fn() + Clone + Send + Sync,
79> {
80 use_calendar_with_options(UseCalendarOptions::default())
81}
82
83pub fn use_calendar_with_options(
86 options: UseCalendarOptions,
87) -> UseCalendarReturn<
88 impl Fn() + Clone + Send + Sync,
89 impl Fn() + Clone + Send + Sync,
90 impl Fn(&NaiveDate) + Clone + Send + Sync,
91 impl Fn() + Clone + Send + Sync,
92> {
93 let UseCalendarOptions {
94 initial_date: date,
95 first_day_of_the_week,
96 } = options;
97 let (date, _set_date) = date.into_signal();
98
99 let show_date = RwSignal::new(date.get_untracked().unwrap_or(Local::now().date_naive()));
100 Effect::new(move |_| {
101 if let Some(selected_date) = date.get() {
102 let show_date_data = show_date.get_untracked();
103 if selected_date.year() != show_date_data.year()
104 || selected_date.month() != show_date_data.month()
105 {
106 show_date.set(selected_date);
107 }
108 }
109 });
110
111 let dates = Memo::new(move |_| {
112 let show_date = show_date.get();
113 let show_date_month = show_date.month();
114 let mut dates = vec![];
115
116 let mut current_date = show_date;
117 let mut current_weekday_number = None::<u32>;
118 loop {
119 let date = current_date - Days::new(1);
120 if date.month() != show_date_month {
121 if current_weekday_number.is_none() {
122 current_weekday_number = Some(
123 current_date.weekday().days_since(
124 Weekday::try_from(first_day_of_the_week.get() as u8)
125 .unwrap_or(Weekday::Mon),
126 ),
127 );
128 }
129 let weekday_number = current_weekday_number.unwrap();
130 if weekday_number == 0 {
131 break;
132 }
133 current_weekday_number = Some(weekday_number - 1);
134
135 dates.push(CalendarDate::Previous(date));
136 } else {
137 dates.push(CalendarDate::Current(date));
138 }
139 current_date = date;
140 }
141 dates.reverse();
142 dates.push(CalendarDate::Current(show_date));
143 current_date = show_date;
144 current_weekday_number = None;
145 loop {
146 let date = current_date + Days::new(1);
147 if date.month() != show_date_month {
148 if current_weekday_number.is_none() {
149 current_weekday_number = Some(
150 current_date.weekday().days_since(
151 Weekday::try_from(first_day_of_the_week.get() as u8)
152 .unwrap_or(Weekday::Mon),
153 ),
154 );
155 }
156 let weekday_number = current_weekday_number.unwrap();
157 if weekday_number == 6 {
158 break;
159 }
160 current_weekday_number = Some(weekday_number + 1);
161 dates.push(CalendarDate::Next(date));
162 } else {
163 dates.push(CalendarDate::Current(date));
164 }
165 current_date = date;
166 }
167 dates
168 });
169
170 let weekdays = Memo::<Vec<usize>>::new(move |_| {
171 if Weekday::try_from(first_day_of_the_week.get() as u8).is_ok() {
172 let first_weekdays = first_day_of_the_week.get()..7;
173 let last_weekdays = 0..first_day_of_the_week.get();
174 first_weekdays.chain(last_weekdays).collect()
175 } else {
176 (0..7).collect()
177 }
178 });
179
180 UseCalendarReturn {
181 previous_month: move || {
182 show_date.update(|date| {
183 *date = *date - Months::new(1);
184 });
185 },
186 today: move || {
187 show_date.set(Local::now().date_naive());
188 },
189 month_by_date: move |new_date: &NaiveDate| {
190 show_date.set(*new_date);
191 },
192
193 next_month: move || {
194 show_date.update(|date| {
195 *date = *date + Months::new(1);
196 });
197 },
198 weekdays: weekdays.into(),
199 dates: dates.into(),
200 }
201}
202
203#[derive(DefaultBuilder)]
206pub struct UseCalendarOptions {
207 #[builder(into)]
210 pub initial_date: MaybeRwSignal<Option<NaiveDate>>,
211 #[builder(into)]
213 pub first_day_of_the_week: Signal<usize>,
214}
215
216impl Default for UseCalendarOptions {
217 fn default() -> Self {
218 Self {
219 initial_date: Some(Local::now().date_naive()).into(),
220 first_day_of_the_week: 0.into(),
221 }
222 }
223}
224
225pub struct UseCalendarReturn<PreviousMonthFn, TodayFn, MonthByDateFn, NextMonthFn>
228where
229 PreviousMonthFn: Fn() + Clone + Send + Sync,
230 TodayFn: Fn() + Clone + Send + Sync,
231 MonthByDateFn: Fn(&NaiveDate) + Clone + Send + Sync,
232 NextMonthFn: Fn() + Clone + Send + Sync,
233{
234 pub previous_month: PreviousMonthFn,
236 pub today: TodayFn,
238 pub month_by_date: MonthByDateFn,
240 pub next_month: NextMonthFn,
242 pub weekdays: Signal<Vec<usize>>,
244 pub dates: Signal<Vec<CalendarDate>>,
246}
247
248#[derive(Clone, Copy, PartialEq)]
250pub enum CalendarDate {
251 Previous(NaiveDate),
252 Current(NaiveDate),
253 Next(NaiveDate),
254}
255
256impl CalendarDate {
257 pub fn is_other_month(&self) -> bool {
258 match self {
259 CalendarDate::Previous(_) | CalendarDate::Next(_) => true,
260 CalendarDate::Current(_) => false,
261 }
262 }
263 pub fn is_today(&self) -> bool {
264 let date = self.deref();
265 let now_date = Local::now().date_naive();
266 &now_date == date
267 }
268
269 pub fn is_selected(&self, selected_date: &NaiveDate) -> bool {
270 self.deref() == selected_date
271 }
272
273 pub fn is_before(&self, date: &NaiveDate) -> bool {
274 self.deref() < date
275 }
276
277 pub fn is_between(&self, start_date: &NaiveDate, end_date: &NaiveDate) -> bool {
278 let date = self.deref();
279 date >= start_date && date <= end_date
280 }
281
282 pub fn is_between_current_month(&self, start_date: &NaiveDate, end_date: &NaiveDate) -> bool {
283 match self {
284 CalendarDate::Current(date) => date >= start_date && date <= end_date,
285 CalendarDate::Next(date) => date > start_date && date <= end_date,
286 CalendarDate::Previous(date) => date >= start_date && date < end_date,
287 }
288 }
289
290 pub fn is_after(&self, date: &NaiveDate) -> bool {
291 self.deref() > date
292 }
293
294 pub fn is_first_day_of_month(&self) -> bool {
295 let date = self.deref();
296 if let Some(prev_date) = date.pred_opt() {
297 date.month() != prev_date.month()
298 } else {
299 true
300 }
301 }
302
303 pub fn is_last_day_of_month(&self) -> bool {
304 let date = self.deref();
305 if let Some(next_date) = date.succ_opt() {
306 date.month() != next_date.month()
307 } else {
308 true
309 }
310 }
311}
312
313impl Deref for CalendarDate {
314 type Target = NaiveDate;
315
316 fn deref(&self) -> &Self::Target {
317 match self {
318 CalendarDate::Previous(date)
319 | CalendarDate::Current(date)
320 | CalendarDate::Next(date) => date,
321 }
322 }
323}