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