UNPKG

laif-ds

Version:

Design System di Laif con componenti React basati su principi di Atomic Design

280 lines (234 loc) 10.8 kB
# Calendar ## Overview Flexible calendar component built on `react-day-picker`. Supports single, multiple, and range selections, localization, week settings, and rich customization via class names and sub-components. Features custom month/year dropdowns for easy navigation. --- ## Props Props extend `DayPicker` props with additional customization options. | Prop | Type | Default | Description | | ----------------- | ----------------------------------------------------------------------------- | ----------- | ----------------------------------------------- | | `mode` | `"default" \| "single" \| "multiple" \| "range"` | `"default"` | Selection mode. | | `selected` | `Date \| Date[] \| DateRange` | `undefined` | Controlled selection value. | | `defaultMonth` | `Date` | `undefined` | Month to display initially. | | `minDate` | `Date` | `undefined` | Minimum selectable date (also sets startMonth). | | `maxDate` | `Date` | `undefined` | Maximum selectable date (also sets endMonth). | | `disabled` | `Matcher \| Matcher[]` | `undefined` | Disabled dates (combined with minDate/maxDate). | | `showOutsideDays` | `boolean` | `true` | Show days from adjacent months. | | `ISOWeek` | `boolean` | `false` | Use ISO week numbering. | | `fixedWeeks` | `boolean` | `false` | Always show 6 weeks. | | `numberOfMonths` | `number` | `1` | Number of months to display. | | `locale` | `Locale` | `undefined` | Locale object for formatting (from date-fns). | | `weekStartsOn` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `0` | Start of the week (0=Sun, 1=Mon, ...). | | `buttonVariant` | `"default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| "link"` | `"ghost"` | Variant for navigation and day buttons. | | `formatters` | `Partial<DayPickerFormatters>` | `{}` | Custom label formatters. | | `components` | `Partial<DayPickerComponents>` | `{}` | Custom component overrides. | | `className` | `string` | `""` | Additional container classes. | | `classNames` | `Partial<DayPickerClassNames>` | `{}` | Custom class names for internal elements. | | `onSelect` | `(value: Date \| Date[] \| DateRange \| undefined) => void` | `undefined` | Called when selection changes. | | `onDayClick` | `(day: Date) => void` | `undefined` | Called on day click. | | `onMonthChange` | `(month: Date) => void` | `undefined` | Called when the displayed month changes. | --- ## Behavior - **Selection modes**: Supports default (no selection), single date, multiple dates, and date range selection with visual feedback. - **Custom caption**: Features custom month/year dropdowns using AppSelect for easy navigation across months and years. - **Date constraints**: `minDate` and `maxDate` automatically disable dates outside the range and restrict month/year navigation. - **Disabled dates**: The `disabled` prop is combined with `minDate`/`maxDate` constraints into a unified matcher array. - **Navigation**: Previous/next month buttons use `buttonVariant` styling and are disabled when reaching min/max boundaries. - **Year range**: By default, allows navigation 50 years before and after the current year (overridden by minDate/maxDate). - **Localization**: Supply `locale`, `weekStartsOn`, and `formatters` for internationalization support. - **Visual feedback**: Today's date shows a dot indicator, selected dates are highlighted, range selections show start/middle/end styling. - **Accessibility**: Full keyboard navigation and focus management with proper ARIA attributes. --- ## Examples ### Basic ```tsx import { Calendar } from "laif-ds"; export function BasicCalendar() { return ( <Calendar className="border-d-border rounded-md border" showOutsideDays onDayClick={(d) => console.log("day clicked", d)} onMonthChange={(m) => console.log("month", m)} /> ); } ``` ### With Min/Max Date Constraints ```tsx import { Calendar } from "laif-ds"; import { useState } from "react"; export function MinMaxCalendar() { const [date, setDate] = useState<Date | undefined>(new Date()); return ( <Calendar mode="single" selected={date} onSelect={setDate} minDate={new Date(2001, 8, 11)} maxDate={new Date(2026, 11, 10)} className="border-d-border rounded-md border" /> ); } ``` ### Single Selection ```tsx import { useState } from "react"; import { Calendar } from "laif-ds"; export function SingleSelection() { const [date, setDate] = useState<Date | undefined>(new Date()); return ( <div className="flex flex-col gap-2"> <Calendar mode="single" selected={date} onSelect={setDate} className="border-d-border rounded-md border" /> <p className="text-d-secondary-foreground text-center text-sm"> {date ? date.toDateString() : "Nessuna data selezionata"} </p> </div> ); } ``` ### Multiple Selection ```tsx import { useState } from "react"; import { Calendar } from "laif-ds"; export function MultipleSelection() { const [dates, setDates] = useState<Date[] | undefined>([new Date()]); return ( <div className="flex flex-col gap-2"> <Calendar mode="multiple" selected={dates} onSelect={setDates} className="border-d-border rounded-md border" /> <p className="text-d-secondary-foreground text-center text-sm"> {dates && dates.length > 0 ? `${dates.length} date selezionate` : "Nessuna data selezionata"} </p> </div> ); } ``` ### Range Selection ```tsx import { useState } from "react"; import { Calendar } from "laif-ds"; import type { DateRange } from "react-day-picker"; export function RangeSelection() { const [range, setRange] = useState<DateRange | undefined>({ from: new Date(), to: new Date(), }); return ( <div className="flex flex-col gap-2"> <Calendar mode="range" selected={range} onSelect={setRange} className="border-d-border rounded-md border" numberOfMonths={2} showOutsideDays /> <p className="text-d-secondary-foreground text-center text-sm"> {range?.from && range?.to ? `${range.from.toDateString()} - ${range.to.toDateString()}` : range?.from ? `Da ${range.from.toDateString()}` : "Seleziona un intervallo"} </p> </div> ); } ``` ### Disabled Dates ```tsx import { useState } from "react"; import { Calendar } from "laif-ds"; import { addDays } from "date-fns"; export function DisabledDates() { const [date, setDate] = useState<Date | undefined>(new Date()); const disabledDays = [ { from: addDays(new Date(), 1), to: addDays(new Date(), 5) }, new Date(new Date().setDate(15)), { before: addDays(new Date(), -10) }, ]; return ( <Calendar mode="single" selected={date} onSelect={setDate} disabled={disabledDays} className="border-d-border rounded-md border" /> ); } ``` ### Localized (Italian) and Week Start ```tsx import { Calendar } from "laif-ds"; import { it } from "date-fns/locale"; import { format } from "date-fns"; export function LocalizedCalendar() { return ( <Calendar className="border-d-border rounded-md border" locale={it} weekStartsOn={1} formatters={{ formatMonthCaption: (date: Date) => format(date, "MMMM yyyy", { locale: it }), formatWeekdayName: (date: Date) => format(date, "EEEEEE", { locale: it }), }} /> ); } ``` ### ISO Week, Fixed Weeks, Multiple Months ```tsx import { Calendar } from "laif-ds"; export function ISOWeekCalendar() { return ( <Calendar className="border-d-border rounded-md border" ISOWeek weekStartsOn={1} /> ); } export function FixedWeeksCalendar() { return ( <Calendar className="border-d-border rounded-md border" fixedWeeks showOutsideDays /> ); } export function MultiMonthCalendar() { return ( <Calendar className="border-d-border rounded-md border" numberOfMonths={2} showOutsideDays /> ); } ``` --- ## Notes - **Custom caption component**: The calendar uses a custom `MonthCaption` component with searchable AppSelect dropdowns for month and year selection. - **Month/year navigation**: Months and years outside the minDate/maxDate range are automatically disabled in the dropdowns. - **Default year range**: Without minDate/maxDate, the calendar allows navigation 50 years before and after the current year. - **Customization**: Use `classNames` and `components` props to override internal parts and styling. - **Button variants**: `buttonVariant` controls the appearance of navigation buttons and day buttons. - **Range selection**: Range mode highlights start, middle, and end days with appropriate rounded edges and background colors. - **Today indicator**: Current date shows a small dot at the bottom of the day cell. - **Design tokens**: Uses design tokens for consistent theming across light/dark modes. - **Performance**: Efficient rendering with React.useMemo for computed values and proper ref management.