# Calendar Controller: Usage Guide

This guide provides comprehensive instructions for implementing and using the Calendar Controller in your applications.

> **Latest Update (v0.2.1)**: Fixed function scope issues in the disabled-weekdays example. All interactive examples now work correctly in browser environments without "function is not defined" errors.

## Getting Started

### Installation

Install the package from npm:

```bash
npm install @uplink-protocol/calendar-controller
```

### Basic Setup

```javascript
import { CalendarController } from '@uplink-protocol/calendar-controller';

// Create a new calendar controller with default settings
const calendar = CalendarController();

// Or with configuration options
const calendar = CalendarController({
  firstDayOfWeek: 1, // Monday
  dateFormat: 'MM/DD/YYYY',
  initialSelectedDate: new Date()
});
```

## Core Calendar Operations

### Date Selection

```javascript
// Select a specific date (year, month, day)
// Note: month is 0-indexed (0 = January, 11 = December)
calendar.methods.selectDate(2025, 4, 15); // Selects May 15, 2025

// Clear the current selection
calendar.methods.clearSelection();
```

### Date Range Selection

```javascript
// Enable date range selection mode
calendar.methods.setRangeSelectionMode(true);

// When in range selection mode, calling selectDate twice will select a range
// First call selects the start date, second call selects the end date
calendar.methods.selectDate(2025, 4, 10); // Start date: May 10, 2025
calendar.methods.selectDate(2025, 4, 15); // End date: May 15, 2025

// Disable range selection mode (back to single date selection)
calendar.methods.setRangeSelectionMode(false);
```

### Calendar Navigation

```javascript
// Navigate to the next month
calendar.methods.nextMonth();

// Navigate to the previous month
calendar.methods.prevMonth();

// Navigate to the next year
calendar.methods.nextYear();

// Navigate to the previous year
calendar.methods.prevYear();

// Navigate to the next year range (e.g., move from 2020-2029 to 2030-2039)
calendar.methods.nextYearRange();

// Navigate to the previous year range (e.g., move from 2020-2029 to 2010-2019)
calendar.methods.prevYearRange();

// Navigate to a specific month (0-indexed)
calendar.methods.goToMonth(6); // July

// Navigate to a specific year
calendar.methods.goToYear(2026);

// Navigate to a specific year range by specifying the base year
calendar.methods.goToYearRange(2030); // Goes to range starting with 2030

// Navigate to a specific date
calendar.methods.goToDate(new Date(2025, 11, 25)); // December 25, 2025

// Navigate to today
calendar.methods.goToToday();
```

### View Mode Selection

```javascript
// Select a specific month from the month view
// This updates the current date to that month and can be used to switch from month view to day view
calendar.methods.selectMonth(3, 2025); // Select April 2025

// Select a specific year from the year view
// This updates the current date to that year and can be used to switch from year view to month view
calendar.methods.selectYear(2026); // Select 2026
```

### Date Validation

```javascript
// Check if a date is disabled
const isDisabled = calendar.methods.isDateDisabled(new Date(2025, 4, 15));
if (!isDisabled) {
  // Date is valid, proceed with selection
}
```

## UI Integration

### Accessing Calendar State

The calendar state is accessible through reactive bindings:

```javascript
// Get the currently selected date
const selectedDate = calendar.bindings.selectedDate.get();

// Get the selected date range (when in range selection mode)
const dateRange = calendar.bindings.selectedDateRange.get();

// Get the current month and year being displayed
const currentMonth = calendar.bindings.currentMonth.get();
const currentYear = calendar.bindings.currentYear.get();
const monthName = calendar.bindings.monthName.get();
```

### Rendering the Calendar

```javascript
// Get calendar days for the current month view
const days = calendar.bindings.calendarDays.get();

// Get months for the month view
const months = calendar.bindings.calendarMonths.get();

// Get years for the year view
const years = calendar.bindings.calendarYears.get();

// Get information about the current year range
const yearRange = calendar.methods.getCurrentYearRange();
console.log(`Current year range: ${yearRange.startYear}-${yearRange.endYear}`);

// Set the year range size (number of years to display in year view)
calendar.methods.setYearRangeSize(16); // Display 16 years instead of default 12

// Render the calendar days
days.forEach(day => {
  const dayElement = document.createElement('div');
  dayElement.textContent = day.day;
  dayElement.classList.add('calendar-day');
  
  if (day.isCurrentMonth) dayElement.classList.add('current-month');
  if (day.isToday) dayElement.classList.add('today');
  if (day.isSelected) dayElement.classList.add('selected');
  if (day.isDisabled) dayElement.classList.add('disabled');
  if (day.isInRange) dayElement.classList.add('in-range');
  if (day.isRangeStart) dayElement.classList.add('range-start');
  if (day.isRangeEnd) dayElement.classList.add('range-end');
  
  // Add click handler
  dayElement.addEventListener('click', () => {
    if (!day.isDisabled && day.date) {
      calendar.methods.selectDate(
        day.date.getFullYear(),
        day.date.getMonth(),
        day.date.getDate()
      );
    }
  });
  
  // Add to calendar container
  calendarContainer.appendChild(dayElement);
});
```

### Subscribing to Calendar Changes

```javascript
// Subscribe to calendar day changes
calendar.bindings.calendarDays.subscribe(days => {
  // Re-render the calendar when the days change
  renderCalendar(days);
});

// Subscribe to selected date changes
calendar.bindings.selectedDate.subscribe(date => {
  if (date) {
    updateSelectedDateDisplay(date);
  }
});

// Subscribe to month name changes
calendar.bindings.monthName.subscribe(monthName => {
  document.getElementById('current-month').textContent = monthName;
});

// Subscribe to year changes
calendar.bindings.currentYear.subscribe(year => {
  document.getElementById('current-year').textContent = year;
});
```

### Handling Calendar Events

```javascript
// Listen for date selection events
calendar.events.dateSelected.subscribe(date => {
  console.log('Date selected:', date);
  // Update your UI here
});

// Listen for date range selection events
calendar.events.dateRangeSelected.subscribe(range => {
  console.log('Date range selected:', range);
  // Update your UI here
});

// Listen for month change events
calendar.events.monthChanged.subscribe(({ month, year }) => {
  console.log(`Month changed to ${month} ${year}`);
  // Update your UI here
});
```

## Complete HTML Example

Here's a minimal standalone HTML example to get started:

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Date Picker</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
</head>
<body class="p-6 bg-gray-50">
  <div class="max-w-md mx-auto bg-white rounded-lg shadow">
    <!-- Calendar header -->
    <div class="p-4 flex justify-between items-center border-b">
      <button id="prevMonth" class="p-2 hover:bg-gray-100 rounded">
        <i class="fa-solid fa-chevron-left"></i>
      </button>
      <div>
        <span id="monthName" class="text-lg font-medium"></span>
        <span id="yearNumber" class="text-lg font-medium ml-1"></span>
      </div>
      <button id="nextMonth" class="p-2 hover:bg-gray-100 rounded">
        <i class="fa-solid fa-chevron-right"></i>
      </button>
    </div>
    
    <!-- Weekday header -->
    <div id="weekdays" class="grid grid-cols-7 text-center text-gray-500 p-2"></div>
    
    <!-- Calendar days -->
    <div id="calendarDays" class="grid grid-cols-7 text-center p-2"></div>
    
    <!-- Selected date display -->
    <div class="p-4 border-t">
      <div class="text-sm text-gray-500">Selected Date:</div>
      <div id="selectedDate" class="font-medium">None</div>
    </div>
  </div>

  <script type="module">
    // Import the Calendar Controller (using CDN for this example)
    import { CalendarController } from 'https://cdn.jsdelivr.net/npm/@uplink-protocol/calendar-controller/dist/index.js';
    
    // Initialize the calendar
    const calendar = CalendarController({
      firstDayOfWeek: 1, // Monday
      dateFormat: 'MM/DD/YYYY'
    });
    
    // DOM elements
    const monthNameElement = document.getElementById('monthName');
    const yearNumberElement = document.getElementById('yearNumber');
    const weekdaysElement = document.getElementById('weekdays');
    const calendarDaysElement = document.getElementById('calendarDays');
    const selectedDateElement = document.getElementById('selectedDate');
    const prevMonthButton = document.getElementById('prevMonth');
    const nextMonthButton = document.getElementById('nextMonth');
    
    // Initialize weekdays
    const weekdays = calendar.bindings.weekdays.get();
    weekdays.forEach(weekday => {
      const day = document.createElement('div');
      day.textContent = weekday.substring(0, 2); // Short weekday name
      weekdaysElement.appendChild(day);
    });
    
    // Render calendar days
    function renderCalendarDays(days) {
      calendarDaysElement.innerHTML = '';
      
      days.forEach(day => {
        const dayElement = document.createElement('button');
        dayElement.textContent = day.day;
        dayElement.className = 'w-full h-10 flex items-center justify-center';
        
        if (!day.isCurrentMonth) {
          dayElement.classList.add('text-gray-300');
        }
        if (day.isToday) {
          dayElement.classList.add('bg-gray-100');
        }
        if (day.isSelected) {
          dayElement.classList.add('bg-blue-500', 'text-white', 'font-bold', 'rounded-full');
        }
        if (day.isDisabled) {
          dayElement.classList.add('text-gray-300', 'cursor-not-allowed');
          dayElement.disabled = true;
        }
        
        dayElement.addEventListener('click', () => {
          if (day.date) {
            calendar.methods.selectDate(
              day.date.getFullYear(),
              day.date.getMonth(),
              day.date.getDate()
            );
          }
        });
        
        calendarDaysElement.appendChild(dayElement);
      });
    }
    
    // Subscribe to calendar changes
    calendar.bindings.monthName.subscribe(value => {
      monthNameElement.textContent = value;
    });
    
    calendar.bindings.currentYear.subscribe(value => {
      yearNumberElement.textContent = value;
    });
    
    calendar.bindings.calendarDays.subscribe(days => {
      renderCalendarDays(days);
    });
    
    calendar.bindings.selectedDate.subscribe(date => {
      selectedDateElement.textContent = date 
        ? date.toLocaleDateString() 
        : 'None';
    });
    
    // Set up button handlers
    prevMonthButton.addEventListener('click', () => {
      calendar.methods.prevMonth();
    });
    
    nextMonthButton.addEventListener('click', () => {
      calendar.methods.nextMonth();
    });
  </script>
</body>
</html>
```

## Framework Integration Examples

### React Integration

```jsx
import React, { useEffect, useState, useRef } from 'react';
import { CalendarController } from '@uplink-protocol/calendar-controller';

function DatePicker() {
  const calendarRef = useRef(null);
  const [days, setDays] = useState([]);
  const [monthName, setMonthName] = useState('');
  const [year, setYear] = useState('');
  const [selectedDate, setSelectedDate] = useState(null);
  
  useEffect(() => {
    // Initialize calendar controller
    calendarRef.current = CalendarController({
      firstDayOfWeek: 1,
      dateFormat: 'MM/DD/YYYY'
    });
    
    // Set up subscriptions
    const subscriptions = [
      calendarRef.current.bindings.calendarDays.subscribe(setDays),
      calendarRef.current.bindings.monthName.subscribe(setMonthName),
      calendarRef.current.bindings.currentYear.subscribe(setYear),
      calendarRef.current.bindings.selectedDate.subscribe(setSelectedDate)
    ];
    
    // Clean up subscriptions
    return () => {
      subscriptions.forEach(sub => sub.unsubscribe());
    };
  }, []);
  
  const handleDateClick = (day) => {
    if (day.date && !day.isDisabled) {
      calendarRef.current.methods.selectDate(
        day.date.getFullYear(),
        day.date.getMonth(),
        day.date.getDate()
      );
    }
  };
  
  return (
    <div className="calendar-wrapper">
      <div className="calendar-header">
        <button onClick={() => calendarRef.current.methods.prevMonth()}>
          Previous
        </button>
        <div>
          {monthName} {year}
        </div>
        <button onClick={() => calendarRef.current.methods.nextMonth()}>
          Next
        </button>
      </div>
      
      <div className="calendar-grid">
        {days.map((day, index) => (
          <div
            key={index}
            className={`calendar-day ${day.isToday ? 'today' : ''} ${
              day.isSelected ? 'selected' : ''
            } ${day.isCurrentMonth ? '' : 'other-month'} ${
              day.isDisabled ? 'disabled' : ''
            }`}
            onClick={() => handleDateClick(day)}
          >
            {day.day}
          </div>
        ))}
      </div>
      
      {selectedDate && (
        <div className="selected-date">
          Selected: {selectedDate.toLocaleDateString()}
        </div>
      )}
    </div>
  );
}

export default DatePicker;
```

### Vue.js Integration

```vue
<template>
  <div class="calendar-wrapper">
    <div class="calendar-header">
      <button @click="prevMonth">Previous</button>
      <div>{{ monthName }} {{ year }}</div>
      <button @click="nextMonth">Next</button>
    </div>
    
    <div class="calendar-grid">
      <div 
        v-for="(day, index) in days" 
        :key="index"
        :class="{
          'calendar-day': true,
          'today': day.isToday,
          'selected': day.isSelected,
          'other-month': !day.isCurrentMonth,
          'disabled': day.isDisabled
        }"
        @click="handleDateClick(day)"
      >
        {{ day.day }}
      </div>
    </div>
    
    <div v-if="selectedDate" class="selected-date">
      Selected: {{ formatDate(selectedDate) }}
    </div>
  </div>
</template>

<script>
import { CalendarController } from '@uplink-protocol/calendar-controller';

export default {
  data() {
    return {
      calendar: null,
      days: [],
      monthName: '',
      year: '',
      selectedDate: null,
      subscriptions: []
    };
  },
  
  mounted() {
    this.initCalendar();
  },
  
  beforeUnmount() {
    this.subscriptions.forEach(sub => sub.unsubscribe());
  },
  
  methods: {
    initCalendar() {
      this.calendar = CalendarController({
        firstDayOfWeek: 1,
        dateFormat: 'MM/DD/YYYY'
      });
      
      // Set up subscriptions
      this.subscriptions = [
        this.calendar.bindings.calendarDays.subscribe(days => this.days = days),
        this.calendar.bindings.monthName.subscribe(name => this.monthName = name),
        this.calendar.bindings.currentYear.subscribe(year => this.year = year),
        this.calendar.bindings.selectedDate.subscribe(date => this.selectedDate = date)
      ];
    },
    
    handleDateClick(day) {
      if (day.date && !day.isDisabled) {
        this.calendar.methods.selectDate(
          day.date.getFullYear(),
          day.date.getMonth(),
          day.date.getDate()
        );
      }
    },
    
    prevMonth() {
      this.calendar.methods.prevMonth();
    },
    
    nextMonth() {
      this.calendar.methods.nextMonth();
    },
    
    formatDate(date) {
      return date.toLocaleDateString();
    }
  }
};
</script>
```

## Advanced Configuration

### Date Constraints

```javascript
// Create a calendar with constrained dates
const calendar = CalendarController({
  // Min date: January 1, 2025
  minDate: new Date(2025, 0, 1),
  
  // Max date: December 31, 2025
  maxDate: new Date(2025, 11, 31),
  
  // Disabled dates (e.g., holidays)
  disabledDates: [
    new Date(2025, 11, 25), // Christmas
    new Date(2025, 0, 1),   // New Year's Day
  ]
});
```

### Custom Date Formatting

```javascript
// Create a calendar with custom date formatting
const calendar = CalendarController({
  // Format string follows common patterns
  // YYYY = 4-digit year, MM = 2-digit month, DD = 2-digit day
  dateFormat: 'YYYY-MM-DD' // e.g., 2025-05-15
});

// Or use a different format
const calendar = CalendarController({
  dateFormat: 'DD/MM/YYYY' // e.g., 15/05/2025
});
```

### Calendar View Options

```javascript
// Create a calendar that starts the week on Monday
const calendar = CalendarController({
  firstDayOfWeek: 1 // 0 = Sunday, 1 = Monday, 6 = Saturday
});

// Create a calendar that hides days from adjacent months
const calendar = CalendarController({
  hideOtherMonthDays: true
});
```

## Troubleshooting

### Calendar Not Updating

If the calendar view isn't updating when you make changes:

1. Make sure you've set up subscriptions to the appropriate bindings
2. Check that your event handlers are calling the correct calendar methods
3. Verify that your rendering logic correctly processes the calendar day properties

### Date Selection Issues

If dates aren't being selected correctly:

1. Check that you're passing the correct year, month, and day parameters
2. Verify that the date isn't disabled due to constraints
3. Ensure you're using the correct month index (remember: January = 0, December = 11)

### Date Format Problems

If dates aren't formatting as expected:

1. Check your dateFormat configuration
2. Remember that MM = month, DD = day, YYYY = 4-digit year
3. Consider using the native Date.toLocaleDateString() method for locale-aware formatting

## Examples

The package includes several working examples in the `examples/` folder that demonstrate different features and use cases:

### Available Examples

1. **Basic Calendar** (`examples/calendar/`) - Simple calendar implementation
2. **Date Picker** (`examples/date-picker/`) - Full-featured date picker with styling
3. **Disabled Weekdays** (`examples/disabled-weekdays/`) - Demonstrates weekday restrictions (fixed in v0.2.1)
4. **Internationalization** (`examples/i18n/`) - Multi-language support
5. **Multi-view Calendar** (`examples/multi-view-calendar/`) - Shows day/month/year views
6. **Comprehensive Date Picker** (`examples/comprehensive-date-picker/`) - Advanced features showcase

### Running Examples

You can run the examples locally by:

1. Cloning the repository
2. Opening the HTML files directly in your browser
3. Or serving them through a local development server

```bash
# Example: serve the examples using a simple HTTP server
npx http-server examples/ -p 8080
```

### Example Fixes in v0.2.1

The disabled-weekdays example had function scope issues where button handlers couldn't access JavaScript functions. This has been resolved by properly exposing functions to the global scope:

```javascript
// Functions are now accessible globally
window.presetWeekends = function() { /* ... */ };
window.presetWeekdays = function() { /* ... */ };
window.applyDisabledDays = function() { /* ... */ };
window.clearDisabledDays = function() { /* ... */ };
```

This ensures all interactive examples work correctly without requiring a build step or module bundler.
