# Calendar

Komponente des SBB Design Systems Lyne.

- Paket: `@sbb-esta/lyne-elements` (Angular: `@sbb-esta/lyne-angular`)
- Aufbau: Hauptelement mit Bestandteilen; Bestandteile nur innerhalb des jeweiligen Elements verwenden
- Kategorien: Form
- Status: Gestaltungsrichtlinien vollständig

## Links

- Seite auf digital.sbb.ch: https://digital.sbb.ch/de/design-system/lyne/components/calendar/
- Storybook: https://lyne-elements.app.sbb.ch/?path=/docs/elements-calendar--docs
- Angular (`@sbb-esta/lyne-angular`): https://lyne-angular.app.sbb.ch/angular/components/calendar/overview (API: https://lyne-angular.app.sbb.ch/angular/components/calendar/api)
- Figma: https://www.figma.com/design/mWknI2rC5DJmOgRO61WKai/Lyne-Components?node-id=7491-17
- GitHub: https://github.com/sbb-design-systems/lyne-components/tree/v5.12.0/src/elements/calendar

## Elemente

- `sbb-calendar`: It displays a calendar which allows choosing a date.
  - `sbb-calendar-day`, nur innerhalb von `sbb-calendar`: It displays a single day cell in the `sbb-calendar` component.
  - `sbb-calendar-month`, nur innerhalb von `sbb-calendar`: It displays a single month cell in the `sbb-calendar` months view.
  - `sbb-calendar-weekday`, nur innerhalb von `sbb-calendar`: It displays a single week day cell in the `sbb-calendar` component.
  - `sbb-calendar-weeknumber`, nur innerhalb von `sbb-calendar`: It displays a single week number cell in the `sbb-calendar` component.
  - `sbb-calendar-year`, nur innerhalb von `sbb-calendar`: It displays a single year cell in the `sbb-calendar` years view.

## Verwandte Komponenten

- Ähnlich: [Mini-Calendar](https://digital.sbb.ch/de/design-system/lyne/components/mini-calendar/index.md)

## Übersicht

### Was macht die Komponente?
Ein Kalender ermöglicht den Nutzenden, ein Datum auszuwählen. Er zeigt Tage, Monate und Jahre.

### Wann soll die Komponente eingesetzt werden?
* Um ein Datum oder mehrere Daten auszuwählen, zum Beispiel für Buchungen oder Planungen.
* Wenn Tage zusätzliche Informationen zeigen sollen, zum Beispiel einen Status.
* Für die Eingabe eines einzelnen Datums in einem Formular dienen [Date-Input](https://digital.sbb.ch/de/design-system/lyne/components/date-input/) und [Datepicker](https://digital.sbb.ch/de/design-system/lyne/components/datepicker/).

### Regeln.
* Standardmässig wird ein einzelnes Datum ausgewählt. Mehrfachauswahl ist optional (`multiple`): einzelne Tage, ganze Wochentage, Kalenderwochen oder ein Bereich (Shift-Klick).
* Es können ein oder mehrere Monate gleichzeitig angezeigt werden. Der angezeigte Monat kann auch fixiert werden.
* Nicht wählbare Tage werden über Minimum, Maximum oder einen Filter ausgeschlossen und deaktiviert bzw. durchgestrichen dargestellt.
* Ein Tag kann mehrere Zustände haben, zum Beispiel heute, ausgewählt oder nicht wählbar.

## Implementation (Web Components, Englisch)

The `<sbb-calendar>` component displays a calendar that allows the user to select a date.

```html
<sbb-calendar></sbb-calendar>
```

### Slots and day customization

The component uses the `<sbb-calendar-day>` component to render day cells.

Consumers can override this behavior by slotting their own customized `<sbb-calendar-day>`,
mainly if some extra content is needed.
The slot name is mandatory, and it requires a date in ISO8601 format (e.g. 2025-01-01).

```html
<sbb-calendar-day slot="2025-01-01"></sbb-calendar-day>
```

The `<sbb-calendar>` creates its own slots based on the month to be displayed;
during initialization, the month is the current one (if there's no date assigned to `value`).
So for the first render, the slotted `<sbb-calendar-day>` elements must match that month.
The `amount` property can be used to display more than one month; in this case, additional
`<sbb-calendar-day>` must be rendered for each month (they must not be grouped, just
sequentially rendered as direct children of the `<sbb-calendar>` element).

Each time the month changes, a `monthchange` event is emitted, typed as
`SbbMonthChangeEvent`. The event has a `range: Day[]` property, which can be accessed to have
information about the days to render. Consumers can listen to this event to dynamically create
and slot the `<sbb-calendar-day>`s of the chosen month.

```css
/* Custom CSS for the extra content */
.my-custom-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  color: light-dark(var(--sbb-color-metal), var(--sbb-color-smoke));
}
```

```html
<!-- Slot days based on the current or defined date. -->
<sbb-calendar value="2025-01-15" @monthchange="(event) => monthChangeHandler(event)">
  <sbb-calendar-day slot="2025-01-01">
    <span class="sbb-text-xxs my-custom-content"> 19.99 </span>
  </sbb-calendar-day>
  <sbb-calendar-day slot="2025-01-02">
    <span class="sbb-text-xxs my-custom-content"> 9.99 </span>
  </sbb-calendar-day>
  ...
  <sbb-calendar-day slot="2025-01-31">
    <span class="sbb-text-xxs my-custom-content"> 99.99 </span>
  </sbb-calendar-day>
</sbb-calendar>
```

```ts
function monthChangeHandler(event: SbbMonthChangeEvent): void {
  const calendar = event.target;
  // Remove slotted days to keep the DOM clean.
  Array.from(calendar.children).forEach((e) => e.remove());
  // Add the new days
  for (const day of event.range) {
    const child = document.createElement('sbb-calendar-day');
    // The day.value property is the date in ISO8601 format,
    // the correct one for the `<sbb-calendar-day>`'s slot property.
    child.setAttribute('slot', day.value);
    calendar.appendChild(child);
  }
}
```

#### States

The `<sbb-calendar-day>` component has a `current` state, which is set if the slot name matches
the current day.

It also has other states based on the properties of the parent `<sbb-calendar>`.
The disabled and the crossed-out states are based on the value of the `min`, `max` and `dateFilter`
properties, while `selected` matches the parent `value` property, including the multiple
variant.

### Configuration

It's possible to set a date using the `value` property. Also, it's possible to place limits
on the selection using the two properties named `min` and `max`. For these three properties, the
accepted formats are:

- Date objects
- ISO String
- Unix Timestamp (number of seconds since Jan 1, 1970)

It's recommended to set the time to 00:00:00.

```html
<sbb-calendar min="1599955200" max="1699920000" value="1649980800"></sbb-calendar>
```

By default, the component takes, in order of priority, the `activeMonth` property,
the `value` property or the current date to calculate which month it has to show.
It's possible to move to the previous/next month using the two buttons at the top of the component.

It's also possible to select a specific date by clicking on the month label between the buttons:
this action opens a list of twenty-four selectable years, and, after the year selection, the list
of months of that year. Clicking on an element will set the month and restore the first view,
allowing to select the desired day.

The `<sbb-calendar>` can be directly displayed in one of these modalities using the `view` property
(default: `day`).

```html
<sbb-calendar value="1585699200" view="month"></sbb-calendar>

<sbb-calendar value="1585699200" view="year"></sbb-calendar>
```

The `<sbb-calendar>` uses two different components to render years and months in their view,
named `<sbb-calendar-month>` and `<sbb-calendar-year>`.
These are 'internal-use-only' components, and they are **not** meant to be used by consumers.

Unwanted dates can be filtered out using the `dateFilter` property.
Note that the `dateFilter` function should not be used as a replacement for the `min` and `max`
properties. The `dateFilter` is applied in all the views, so if some months or years are not
allowed they will be displayed as disabled in the corresponding view.

```ts
/** Returns only working days (Mon-Fri). */
const dateFilterFn: (d: Date) => boolean = d.getDay() !== 6 && d.getDay() !== 0;
```

```html
<sbb-calendar .dateFilter="${dateFilterFn}"></sbb-calendar>
```

#### Multiple mode

By default, the component allows selecting a single date:
this behavior can be changed by setting the `multiple` attribute to true.
In this case the `value` property, if set, must be an array; moreover,
the days of the week become clickable, allowing to select an entire column
(e.g. all the Mondays, all the Tuesdays and so on).
Additionally it is possible to select a range of dates, by clicking on a
date and then Shift clicking on another date.

```html
<sbb-calendar multiple></sbb-calendar>
```

If the `week-numbers` property is set, the ISO week dates are also clickable, allowing to select
all the days in the week.

```html
<sbb-calendar multiple week-numbers></sbb-calendar>
```

#### Active month

Use the `activeMonth` property to define the currently displayed month.
This can be done initially or dynamically after creation.
It is recommended not to change this during user interaction, as this
can be disorienting.

```html
<sbb-calendar active-month="2025-01"></sbb-calendar>
```

#### Fixed month

In the case where a fixed month (or months with `amount`) should be displayed,
the `fixedMonth` property should be used.
With this configuration, the currently displayed month cannot be changed by the user.

The value must be provided as a valid ISO format with `YYYY-MM`.

```html
<sbb-calendar fixed-month="2025-01"></sbb-calendar>
```

### Style

The component displays by default a single month in the `day` view, a list of twenty-four years
in the `year` view, or a list of months in the `month` view.
When setting the `amount` to a value greater than 1, it will display the given amount of months.

```html
<sbb-calendar wide="true" value="1699920000"></sbb-calendar>
```

It's also possible to change the orientation of dates by setting the `orientation` property to
`vertical`. In this variant, the weekdays are displayed on the left side of the component and the
days progress along the vertical direction.
This visual change is applied only to the day view.

```html
<sbb-calendar orientation="vertical"></sbb-calendar>
```

In both orientations, the week days are always displayed. Using the `week-numbers` property, it's
possible to display the ISO week dates perpendicularly to week days,so on the left side in
`horizontal` and on top in `vertical`.

```html
<sbb-calendar week-numbers></sbb-calendar>

<sbb-calendar orientation="vertical" week-numbers></sbb-calendar>
```

### Events

The `change` event is dispatched every time a user selects or deselects a date.

Check the [Slot and day customization](https://lyne-elements.app.sbb.ch/?path=/docs/elements-calendar--docs#slots-and-day-customization) paragraph
for more information about the `monthchange` event.

### Keyboard interaction

It's possible to move within the component using the keyboard.

The days disabled due to the presence of the `min`, `max` and `dateFilter` properties are taken into account: for example,
pressing the `<kbd>Home</kbd>` when the first day of the month is disabled will result in moving to the first non-disabled day.

#### Horizontal orientation

| Keyboard               | Action                                                                                   |
| ---------------------- | ---------------------------------------------------------------------------------------- |
| <kbd>Left Arrow</kbd>  | Go to previous day.                                                                      |
| <kbd>Right Arrow</kbd> | Go to next day.                                                                          |
| <kbd>Up Arrow</kbd>    | Go to the same day in the previous week (eg. from Monday to previous Monday).            |
| <kbd>Down Arrow</kbd>  | Go to the same day in the next week (eg. from Monday to next Monday).                    |
| <kbd>Home</kbd>        | Go to the first day of the month.                                                        |
| <kbd>End</kbd>         | Go to the last day of the month.                                                         |
| <kbd>Page Up</kbd>     | Go to the same day in the first week (eg. from Monday to the first Monday of the month). |
| <kbd>Page Down</kbd>   | Go to the same day in the last week (eg. from Monday to the last Monday of the month).   |

#### Vertical orientation

| Keyboard               | Action                                                                         |
| ---------------------- | ------------------------------------------------------------------------------ |
| <kbd>Left Arrow</kbd>  | Go to the same day in the next week (eg. from Monday to next Monday).          |
| <kbd>Right Arrow</kbd> | Go to the same day in the next week (eg. from Monday to next Monday).          |
| <kbd>Up Arrow</kbd>    | Go to previous day.                                                            |
| <kbd>Down Arrow</kbd>  | Go to next day.                                                                |
| <kbd>Home</kbd>        | Go to the first day of the month.                                              |
| <kbd>End</kbd>         | Go to the last day of the month.                                               |
| <kbd>Page Up</kbd>     | Go to the first day of the week (eg. from any day to Monday of the same week). |
| <kbd>Page Down</kbd>   | Go to the last day of the week (eg. from any day to Sunday of the same week).  |

If `multiple` is set, it is possible to navigate among weekdays using arrows and special keys;
similarly, if `weekNumbers` is set, it is possible to navigate the week numbers group.
Using the `Tab` button, the first row is reached first (weekday in `horizontal` orientation, week numbers in `vertical`),
then the opposite group (week numbers in `horizontal` orientation, weekday in `vertical`), then the calendar days.

### Accessibility

For accessibility purposes, the component is rendered as a native table element and each day is a button.

## API (Web Components, Englisch)

Vollständige API-Tabellen (Attribute, Events, Slots, CSS-Eigenschaften): https://digital.sbb.ch/de/design-system/lyne/components/calendar/api.md
