# Events data
Package: @mantine/schedule
Import: import { ScheduleEventsData } from '@mantine/schedule';
Description: Event data shapes and callback payloads for @mantine/schedule

## Overview

All `@mantine/schedule` components accept events through the `events` prop. Each event
is typed as `ScheduleEventData` and can be one of three shapes:

* **One-off event** – a regular event without recurrence
* **Recurring series event** – a source event that expands into multiple occurrences
* **Recurring override event** – an event that replaces a single occurrence of a series

All three variants share a common set of base fields described in `ScheduleEventBase`.

## ScheduleEventBase

```tsx
import type { DateTimeStringValue, EventPayload } from '@mantine/schedule';
import type { MantineColor } from '@mantine/core';

interface ScheduleEventBase<Payload extends EventPayload = EventPayload> {
  /** Unique event id, used for key and identification */
  id: string | number;

  /** Event title, displayed in month, week and day views */
  title: string;

  /** Event start date/time */
  start: Date | DateTimeStringValue;

  /** Event end date/time */
  end: Date | DateTimeStringValue;

  /** Event background color. Key of `theme.colors` or any valid CSS color. */
  color: MantineColor;

  /** Event variant, default is `'light'` */
  variant?: 'filled' | 'light';

  /** Event display mode. Background events render as full-width blocks behind regular
   *  events, non-interactive unless `withInteractiveBackgroundEvents` is set on the view.
   *  @default 'default' */
  display?: 'default' | 'background';

  /** Additional event data, defined by the user, not used internally by the library */
  payload?: Payload;
}
```

`start` and `end` accept either a `Date` instance or a string in `YYYY-MM-DD HH:mm:ss`
format (the `DateTimeStringValue` type used by all Mantine date components).

## ScheduleSingleEventData

A one-off event without recurrence. This is the most common shape:

```tsx
import type { ScheduleSingleEventData } from '@mantine/schedule';

const event: ScheduleSingleEventData = {
  id: 'meeting-1',
  title: 'Team sync',
  start: '2024-01-15 10:00:00',
  end: '2024-01-15 11:00:00',
  color: 'blue',
};
```

## ScheduleRecurringSeriesEventData

A source event for a recurring series. Adds a `recurrence` field with an [RFC 5545](https://datatracker.ietf.org/doc/html/rfc5545)
recurrence rule:

```tsx
import type { ScheduleRecurringSeriesEventData } from '@mantine/schedule';

const series: ScheduleRecurringSeriesEventData = {
  id: 'weekly-planning',
  title: 'Weekly planning',
  start: '2024-01-15 10:00:00',
  end: '2024-01-15 11:00:00',
  color: 'blue',
  recurrence: {
    rrule: 'FREQ=WEEKLY;BYDAY=MO;COUNT=12',
    exdate: ['2024-02-12 10:00:00'],
    dtstart: '2024-01-15 10:00:00',
  },
};
```

The `recurrence` object has the following shape:

```tsx
interface ScheduleRecurrenceData {
  /** Recurrence rule string, for example: `FREQ=WEEKLY;BYDAY=MO,WE` */
  rrule: string;

  /** Exception datetimes in `YYYY-MM-DD HH:mm:ss` or valid date string format */
  exdate?: DateTimeStringValue[];

  /** Optional explicit series start datetime */
  dtstart?: DateTimeStringValue;
}
```

See the [Recurring events guide](https://mantine.dev/llms/schedule-recurring-events.md) for more details on
recurrence rules and series expansion.

## ScheduleRecurringOverrideEventData

An override event replaces a single generated occurrence from a series:

```tsx
import type { ScheduleRecurringOverrideEventData } from '@mantine/schedule';

const override: ScheduleRecurringOverrideEventData = {
  id: 'weekly-planning-override',
  title: 'Weekly planning (moved)',
  start: '2024-01-17 16:00:00',
  end: '2024-01-17 17:00:00',
  color: 'grape',
  recurringEventId: 'weekly-planning',
  recurrenceId: '2024-01-15 10:00:00',
};
```

* `recurringEventId` – id of the parent series event
* `recurrenceId` – original occurrence datetime in `YYYY-MM-DD HH:mm:ss` format

## EventPayload

Use the `payload` field to attach your own data to events. It is not used by the library,
you can read it in callbacks and in custom event renderers:

```tsx
import type { ScheduleEventData } from '@mantine/schedule';

interface MyEventPayload {
  description: string;
  attendees: string[];
  location?: string;
}

const event: ScheduleEventData<MyEventPayload> = {
  id: 'meeting-1',
  title: 'Team sync',
  start: '2024-01-15 10:00:00',
  end: '2024-01-15 11:00:00',
  color: 'blue',
  payload: {
    description: 'Weekly planning meeting',
    attendees: ['Alice', 'Bob'],
    location: 'Conference room A',
  },
};
```

`EventPayload` is `Record<PropertyKey, any>`. Pass your own payload type as a generic
argument: `ScheduleEventData<MyPayload>`.

## Recurring instance metadata

Each event generated from a recurring series has a `recurringInstance` object. It is added
by the library, read it in callbacks and custom renderers to distinguish generated
occurrences from regular events:

```tsx
interface RecurringInstanceMeta {
  /** If true, event is generated from recurrence rule */
  isRecurringInstance: boolean;

  /** Parent series event id */
  recurringEventId: string | number;

  /** Original occurrence datetime key */
  recurrenceId: DateTimeStringValue;

  /** Original occurrence dates before any drag/drop updates */
  originalStart: DateTimeStringValue;
  originalEnd: DateTimeStringValue;
}
```

Example of reading the metadata in `onEventClick`:

```tsx
<Schedule
  events={events}
  onEventClick={(event) => {
    if (event.recurringInstance?.isRecurringInstance) {
      console.log('Clicked generated occurrence of', event.recurringInstance.recurringEventId);
    } else {
      console.log('Clicked regular event', event.id);
    }
  }}
/>
```

## Callback payloads

### onEventClick

Called when an event is clicked in any view:

```tsx
onEventClick?: (
  event: ScheduleEventData,
  e: React.MouseEvent<HTMLButtonElement>
) => void;
```

### onEventDrop

Called when an event is dropped after a drag (requires `withEventsDragAndDrop`):

```tsx
onEventDrop?: (data: {
  eventId: string | number;
  newStart: DateTimeStringValue;
  newEnd: DateTimeStringValue;
  event: ScheduleEventData;
}) => void;
```

* `eventId` – id of the dropped event, same as `event.id`. For generated recurring
  occurrences it is the occurrence id (`"<seriesId>::<recurrenceId>"`), not the series id.
  Use `event.recurringInstance?.recurringEventId` to get the series id.
* `newStart` / `newEnd` – new datetime values in `YYYY-MM-DD HH:mm:ss` format.

### onEventResize

Called when an event is resized by dragging its top or bottom edge
(requires `withEventResize`):

```tsx
onEventResize?: (data: {
  eventId: string | number;
  newStart: DateTimeStringValue;
  newEnd: DateTimeStringValue;
  event: ScheduleEventData;
}) => void;
```

The payload has the same shape as `onEventDrop`, including the caveat about `eventId`
for generated recurring instances.

### onEventDragStart / onEventDragEnd

Called when an event drag starts or ends:

```tsx
onEventDragStart?: (event: ScheduleEventData) => void;
onEventDragEnd?: () => void;
```

`onEventDragEnd` is called both for successful drops and for cancelled drags – use
`onEventDrop` if you need the new position.

### onTimeSlotClick

Called when a time slot is clicked in `DayView` or `WeekView`:

```tsx
onTimeSlotClick?: (data: {
  slotStart: DateTimeStringValue;
  slotEnd: DateTimeStringValue;
  nativeEvent: React.MouseEvent<HTMLButtonElement>;
}) => void;
```

* `slotStart` / `slotEnd` – the slot range in `YYYY-MM-DD HH:mm:ss` format. Slot length
  is controlled by the view's `intervalMinutes` prop.

### onAllDaySlotClick

Called when the all-day slot is clicked in `DayView` or `WeekView`:

```tsx
onAllDaySlotClick?: (
  date: DateStringValue,
  event: React.MouseEvent<HTMLButtonElement>
) => void;
```

* `date` – the clicked date in `YYYY-MM-DD` format.

### onDayClick

Called when a day is clicked in `MonthView` or `YearView`:

```tsx
onDayClick?: (
  date: DateStringValue,
  event: React.MouseEvent<HTMLButtonElement>
) => void;
```

* `date` – the clicked date in `YYYY-MM-DD` format.

### onSlotDragEnd

Called when a slot range is selected by dragging across time slots or day cells
(requires `withDragSlotSelect`):

```tsx
onSlotDragEnd?: (
  rangeStart: DateTimeStringValue,
  rangeEnd: DateTimeStringValue
) => void;
```

* `rangeStart` / `rangeEnd` – the selected range in `YYYY-MM-DD HH:mm:ss` format.

### onExternalEventDrop

Called when an item is dragged from outside the schedule and dropped onto a slot:

```tsx
onExternalEventDrop?: (
  dataTransfer: DataTransfer,
  dropDateTime: DateTimeStringValue
) => void;
```

* `dataTransfer` – the native `DataTransfer` object set by the external item during its
  `onDragStart` handler. Read your custom data with `dataTransfer.getData(type)`.
* `dropDateTime` – the drop target datetime in `YYYY-MM-DD HH:mm:ss` format.

### Callbacks in resources views

In `ResourcesSchedule` and `Resources*` views, `onDayClick`, `onSlotDragEnd` and `onExternalEventDrop`
are called with a single object that includes `resourceId` of the row. `onTimeSlotClick` payload
includes `resourceId` as well:

```tsx
onDayClick?: (data: {
  date: DateStringValue;
  nativeEvent: React.MouseEvent<HTMLButtonElement>;
  resourceId?: string | number;
}) => void;

onSlotDragEnd?: (data: {
  rangeStart: DateTimeStringValue;
  rangeEnd: DateTimeStringValue;
  resourceId?: string | number;
}) => void;

onExternalEventDrop?: (data: {
  dataTransfer: DataTransfer;
  dropDateTime: DateTimeStringValue;
  resourceId?: string | number;
}) => void;
```

### canDragEvent / canResizeEvent

Permission callbacks – return `false` to prevent dragging or resizing for a specific event:

```tsx
canDragEvent?: (event: ScheduleEventData) => boolean;
canResizeEvent?: (event: ScheduleEventData) => boolean;
```

### preventEventOverlap

Rejects drops and resizes that would make the event overlap another event, see
[drop validation](https://mantine.dev/llms/schedule-schedule.md#drop-validation) for what counts as a conflict:

```tsx
preventEventOverlap?:
  | boolean
  | ((stillEvent: ScheduleEventData, movingEvent: ScheduleEventData) => boolean);
```

Pass a function to decide per pair of events, return `true` to forbid the overlap.
`movingEvent` has the new `start` and `end` and, in `Resources*` views, the target `resourceId`.
The function is called many times while dragging and resizing, it must be pure and cheap.

### canDropEvent / canResizeEventTo / canDropExternalEvent

Placement callbacks – return `false` to reject a drop or resize based on where the event
is being placed. They are called many times while dragging and again before the placement
is committed, so they must be pure and cheap:

```tsx
canDropEvent?: (data: {
  event: ScheduleEventData;
  start: DateTimeStringValue;
  end: DateTimeStringValue;
  resourceId?: string | number;
}) => boolean;

canResizeEventTo?: (data: {
  event: ScheduleEventData;
  start: DateTimeStringValue;
  end: DateTimeStringValue;
  edge: 'start' | 'end';
}) => boolean;

canDropExternalEvent?: (data: {
  dataTransfer: DataTransfer;
  start: DateTimeStringValue;
  resourceId?: string | number;
}) => boolean;
```

* `start` / `end` – the candidate range in `YYYY-MM-DD HH:mm:ss` format. `canDropExternalEvent`
  has no `end` – the schedule does not know the duration of an external item.
* `resourceId` – the target resource, only in `Resources*` views.
* `edge` – the edge of the event that is being dragged.
* `dataTransfer` – only `dataTransfer.types` can be read while the drag is in progress.

### onEventPlacementRejected

Called when a drop, resize or external drop is rejected by `preventEventOverlap` or by one of
the placement callbacks above. The payload is a discriminated union keyed on `action`:

```tsx
onEventPlacementRejected?: (
  data:
    | {
        action: 'drop';
        event: ScheduleEventData;
        start: DateTimeStringValue;
        end: DateTimeStringValue;
        conflicts: ScheduleEventData[];
        reason: 'overlap' | 'rejected';
        resourceId?: string | number;
      }
    | {
        action: 'resize';
        event: ScheduleEventData;
        edge: 'start' | 'end';
        start: DateTimeStringValue;
        end: DateTimeStringValue;
        conflicts: ScheduleEventData[];
        reason: 'overlap' | 'rejected';
        resourceId?: string | number;
      }
    | {
        action: 'external-drop';
        dataTransfer: DataTransfer;
        start: DateTimeStringValue;
        conflicts: [];
        reason: 'rejected';
        resourceId?: string | number;
      }
) => void;
```

* `conflicts` – events the placement collided with, empty when the rejection came from a
  callback.
* `reason` – `'overlap'` when `preventEventOverlap` found a conflict, `'rejected'` when a
  callback returned `false`.
* `resourceId` – only in `Resources*` views: the target resource for `drop` and
  `external-drop`, the event's own resource for `resize`.

## Date and time value formats

Event data and callback payloads use the following string formats consistently:

* `DateStringValue` – `YYYY-MM-DD` (e.g. `2024-01-15`)
* `DateTimeStringValue` – `YYYY-MM-DD HH:mm:ss` (e.g. `2024-01-15 10:00:00`)

These types are shared across `@mantine/dates` and `@mantine/schedule` and are exported
from both packages.
