npm package: @gymast/calendar
Description: A flexible React calendar component
Latest version: 0.1.0

# @gymast/calendar

A powerful, flexible, and modern React calendar component designed for complex scheduling needs. Supports multiple views, drag-and-drop, resizing, recurring events, and extensive customization.

[![npm version](https://img.shields.io/npm/v/@gymast/calendar.svg)](https://www.npmjs.com/package/@gymast/calendar)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Demo

Check out the live demo and playground here: [community.gymast.net/calendar-playground](https://community.gymast.net/calendar-playground)

## Features

*   **Multiple Views:** Month, Week, Day, and Agenda views.
*   **Interactive:** Drag-and-drop events, resize events, and click-to-create slots.
*   **Recurring Events:** Built-in support for weekly recurring events (e.g., "every Monday and Wednesday").
*   **Lazy Loading:** `onRangeChange` callback to fetch events dynamically as the user navigates.
*   **Customizable:**
    *   Light and Dark modes.
    *   Configurable start/end hours, week start day, and time slot duration.
    *   Customizable indicators for "Today" and "Now".
*   **Responsive:** optimized for desktop and mobile interactions.
*   **TypeScript:** Fully typed for excellent developer experience.

## Installation

```bash
npm install @gymast/calendar date-fns
# or
yarn add @gymast/calendar date-fns
# or
pnpm add @gymast/calendar date-fns
```

## Usage

Import the `Calendar` component and the CSS styles:

```tsx
import { Calendar, CalendarEvent } from '@gymast/calendar';
import '@gymast/calendar/dist/index.css';
import { useState } from 'react';

const MyCalendar = () => {
  const [events, setEvents] = useState<CalendarEvent[]>([
    {
      id: '1',
      startTime: new Date('2023-10-10T10:00:00'),
      endTime: new Date('2023-10-10T11:00:00'),
      primaryLabel: 'Meeting with Team',
      color: '#3b82f6',
    },
    {
        id: '2',
        startTime: new Date('2023-10-12T09:00:00'),
        endTime: new Date('2023-10-12T10:30:00'),
        primaryLabel: 'Gym Session',
        recurringDays: ['mon', 'wed', 'fri'], // Recurring event
        color: '#10b981'
    }
  ]);

  return (
    <div style={{ height: '800px' }}>
      <Calendar
        events={events}
        defaultView="week"
        onEventClick={(event) => console.log('Clicked:', event)}
        onSlotClick={(date) => console.log('Slot clicked:', date)}
        enableSlotSelection={true}
        theme="light"
      />
    </div>
  );
};
```

## Screenshots

### Month View
![Month View](https://community.gymast.net/calendar-playground/month-view.webp)

### Week View
![Week View](https://community.gymast.net/calendar-playground/week-view.webp)

### Day View
![Day View](https://community.gymast.net/calendar-playground/day-view.webp)

### Agenda View
![Agenda View](https://community.gymast.net/calendar-playground/agenda-view.webp)

### Dark Mode
![Dark Mode](https://community.gymast.net/calendar-playground/dark-mode-example.webp)

## API Reference

### `CalendarProps`

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `events` | `CalendarEvent[]` | `[]` | Array of event objects to display. |
| `date` | `Date` | `new Date()` | The currently selected/visible date. |
| `defaultView` | `'month' \| 'week' \| 'day' \| 'agenda'` | `'month'` | The initial view to render. |
| `onViewChange` | `(view: ViewType) => void` | - | Callback fired when the view changes. |
| `onDateChange` | `(date: Date) => void` | - | Callback fired when the selected date changes. |
| `onRangeChange` | `(range: { start: Date; end: Date; view: ViewType }) => void` | - | Callback fired when the visible date range changes. Useful for lazy loading. |
| `onEventClick` | `(event: CalendarEvent) => void` | - | Callback fired when an event is clicked. |
| `onSlotClick` | `(date: Date) => void` | - | Callback fired when an empty time slot is clicked. |
| `enableSlotSelection` | `boolean` | `true` | Enables/disables hover effects and click interactions on empty slots. |
| `onEventResize` | `(event: CalendarEvent, newStart: Date, newEnd: Date) => void` | - | Callback fired when an event is resized. |
| `onEventDrop` | `(event: CalendarEvent, newStart: Date, newEnd: Date) => void` | - | Callback fired when an event is dropped (dragged). |
| `theme` | `'light' \| 'dark'` | `'light'` | Color theme of the calendar. |
| `weekStartDay` | `0-6` | `1` (Monday) | Day of the week to start on (0=Sunday, 1=Monday, etc.). |
| `timeSlotDuration` | `number` | `30` | Duration of each time slot in minutes. |
| `dayStartHour` | `0-23` | `0` | Hour to start the day view/week view grid. |
| `dayEndHour` | `0-23` | `23` | Hour to end the day view/week view grid. |
| `hourFormat` | `'12' \| '24'` | `'24'` | Format for time labels. |
| `draggable` | `boolean` | `false` | Enable drag-and-drop for events. |
| `resizable` | `boolean` | `false` | Enable resizing for events. |

### `CalendarEvent` Interface

```typescript
interface CalendarEvent {
    id: string | number;
    startTime: Date;
    endTime: Date;
    primaryLabel: string | React.ReactNode;
    secondaryLabel?: string | React.ReactNode;
    color?: string;
    recurringDays?: ('mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun')[];
    [key: string]: any; // Allow custom properties
}
```

## License

MIT