orbital_date_pickers/guides/custom_components.rs
1//! Custom day and month renderers (DP-29).
2
3use chrono::Datelike;
4use leptos::prelude::*;
5use orbital_core_components::{
6 default_calendar_day, default_calendar_month_button, CalendarDayProps, CalendarMonthButtonProps,
7};
8use orbital_macros::component_doc;
9
10pub fn weekend_day(props: CalendarDayProps) -> AnyView {
11 let is_weekend = matches!(
12 props.date.weekday(),
13 chrono::Weekday::Sat | chrono::Weekday::Sun
14 );
15 if is_weekend {
16 view! {
17 <div class="orb-picker-custom-day orb-picker-custom-day--weekend">
18 {default_calendar_day(props)}
19 </div>
20 }
21 .into_any()
22 } else {
23 default_calendar_day(props).into_any()
24 }
25}
26
27fn short_month_button(props: CalendarMonthButtonProps) -> impl IntoView {
28 view! {
29 <div class="orb-picker-custom-month">
30 {default_calendar_month_button(CalendarMonthButtonProps {
31 label: props.label.chars().next().unwrap_or('?').to_string(),
32 ..props
33 })}
34 </div>
35 }
36}
37
38/// Customize calendar day cells and month navigation buttons with renderer callbacks.
39///
40/// Pass an [`Arc`] renderer to `appearance.day` on [`DateCalendar`](crate::DateCalendar) or
41/// popover [`DatePicker`](orbital_core_components::DatePicker) surfaces. Start from
42/// [`default_calendar_day`] and [`default_calendar_month_button`] to preserve keyboard
43/// behavior and selection styling, then wrap or extend the default output.
44///
45/// # When to use
46///
47/// - Highlighting weekends, holidays, or blackout dates in the grid
48/// - Compact month/year pickers with abbreviated labels
49/// - Product-specific day badges (dots, counts) on calendar cells
50///
51/// # Usage
52///
53/// 1. Define a function matching [`CalendarDayRenderer`](orbital_core_components::CalendarDayRenderer) (or month button equivalent).
54/// 2. Delegate to the default renderer for baseline interaction, then wrap with extra markup or classes.
55/// 3. Assign `Some(Arc::new(your_renderer))` to `appearance.day` on the calendar or date picker.
56///
57/// # Best Practices
58///
59/// ## Do's
60///
61/// * Call [`default_calendar_day`] inside custom renderers so click and aria behavior stays intact
62/// * Keep custom markup inside the cell boundary — avoid overlapping adjacent days
63/// * Use CSS classes on wrappers rather than inline styles for theme compatibility
64///
65/// ## Don'ts
66///
67/// * Do not replace grid cells with non-interactive elements that block day selection
68/// * Do not fork keyboard navigation — extend defaults instead of reimplementing cells
69///
70/// # Renderer reference
71///
72/// | Hook | Description |
73/// |------|-------------|
74/// | `appearance.day` | Custom day cell renderer on [`DateCalendar`](crate::DateCalendar) |
75/// | [`default_calendar_day`] | Baseline day button with selection and disabled states |
76/// | [`default_calendar_month_button`] | Baseline month/year navigation control |
77///
78/// # Examples
79///
80/// ## Custom day cell
81/// Highlight weekend days with a custom day renderer on [`DateCalendar`].
82/// <!-- preview -->
83/// ```rust
84/// use std::sync::Arc;
85/// use orbital_core_components::CalendarDayRenderer;
86/// use crate::preview::{PickerPreviewExample, PickerPreviewKnobs};
87/// use crate::{DateCalendar, DateCalendarAppearance, DateCalendarBind};
88/// use orbital_base_components::OrbitalDateTime;
89/// let value = RwSignal::new(None::<OrbitalDateTime>);
90/// let day: CalendarDayRenderer = Arc::new(crate::weekend_day);
91/// view! {
92/// <PickerPreviewExample data_testid="date-pickers-custom-components-preview">
93/// <PickerPreviewKnobs />
94/// <DateCalendar
95/// bind=value
96/// appearance=DateCalendarAppearance { day: Some(day), ..Default::default() }
97/// />
98/// </PickerPreviewExample>
99/// }
100/// ```
101#[component_doc(
102 category = "Calendar & Time",
103 preview_slug = "date-pickers-custom-components",
104 preview_label = "Custom Components",
105 preview_icon = icondata::AiCalendarOutlined,
106)]
107#[component]
108pub fn DatePickersCustomComponentsGuide() -> impl IntoView {
109 view! { () }
110}