@uplink-protocol/calendar-controller
Version:
Flexible calendar API supporting both calendar and date-picker integrations for any JavaScript framework or library
418 lines (328 loc) • 12.1 kB
Markdown
# Disabled Weekdays Feature
The disabled weekdays feature allows you to disable specific days of the week across all calendar views (days, months, years). This is useful for business applications, appointment scheduling, and any scenario where certain days should not be selectable.
## Overview
- **Days of Week**: Represented as numbers 0-6 (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
- **Scope**: Applied globally across all calendar views
- **Validation**: Automatically filters invalid day numbers and duplicates
- **Combination**: Works seamlessly with other date constraints (min/max dates, disabled specific dates)
## Basic Usage
### Initial Configuration
```typescript
import { CalendarController } from '@odyssey/date-picker';
// Disable weekends (Saturday and Sunday)
const calendar = new CalendarController('#date-input', {
disabledDaysOfWeek: [0, 6] // 0 = Sunday, 6 = Saturday
});
// Disable specific business days
const businessCalendar = new CalendarController('#business-date', {
disabledDaysOfWeek: [1, 3] // Monday and Wednesday
});
// Disable all weekdays (only weekends selectable)
const weekendOnlyCalendar = new CalendarController('#weekend-date', {
disabledDaysOfWeek: [1, 2, 3, 4, 5] // Monday through Friday
});
```
### Dynamic Management
```typescript
// Set disabled days (replaces existing)
calendar.setDisabledDaysOfWeek([0, 6]); // Weekends only
// Add individual days
calendar.addDisabledDayOfWeek(1); // Add Monday
calendar.addDisabledDayOfWeek(5); // Add Friday
// Remove individual days
calendar.removeDisabledDayOfWeek(0); // Remove Sunday
// Get current disabled days
const disabledDays = calendar.getDisabledDaysOfWeek();
console.log('Disabled days:', disabledDays); // [1, 5, 6]
```
## API Reference
### CalendarOptions
```typescript
interface CalendarOptions {
disabledDaysOfWeek?: number[]; // Array of day numbers (0-6)
// ... other options
}
```
### CalendarController Methods
```typescript
class CalendarController {
// Set disabled days of week (replaces existing array)
setDisabledDaysOfWeek(days: number[]): number[];
// Add a single day to disabled days
addDisabledDayOfWeek(day: number): number[];
// Remove a single day from disabled days
removeDisabledDayOfWeek(day: number): number[];
// Get current disabled days of week
getDisabledDaysOfWeek(): number[];
}
```
### Day Number Reference
| Day | Number |
|-----|--------|
| Sunday | 0 |
| Monday | 1 |
| Tuesday | 2 |
| Wednesday | 3 |
| Thursday | 4 |
| Friday | 5 |
| Saturday | 6 |
## Common Use Cases
### 1. Business Hours (Weekdays Only)
```typescript
const businessCalendar = new CalendarController('#appointment-date', {
disabledDaysOfWeek: [0, 6], // Disable weekends
minDate: new Date(), // Only future dates
onDateSelect: (date) => {
console.log('Business day selected:', date);
}
});
```
### 2. Service Availability
```typescript
// Service available Tuesday, Thursday, Saturday only
const serviceCalendar = new CalendarController('#service-date', {
disabledDaysOfWeek: [0, 1, 3, 5], // Disable Sun, Mon, Wed, Fri
onDateSelect: (date) => {
scheduleService(date);
}
});
```
### 3. Maintenance Windows
```typescript
// No deployments on Fridays and weekends
const deploymentCalendar = new CalendarController('#deployment-date', {
disabledDaysOfWeek: [0, 5, 6], // Disable Fri, Sat, Sun
disabledDates: getHolidayDates(), // Also disable holidays
});
```
### 4. Educational Calendar
```typescript
// School days only (no weekends)
const schoolCalendar = new CalendarController('#class-date', {
disabledDaysOfWeek: [0, 6], // No weekends
minDate: new Date(2024, 8, 1), // School year start
maxDate: new Date(2025, 5, 30), // School year end
disabledDates: getSchoolHolidays()
});
```
### 5. Dynamic Business Rules
```typescript
class BusinessRuleManager {
constructor(calendarSelector) {
this.calendar = new CalendarController(calendarSelector, {
disabledDaysOfWeek: []
});
}
// Apply different rules based on business type
applyBusinessHours(businessType) {
switch (businessType) {
case 'retail':
this.calendar.setDisabledDaysOfWeek([]); // Open all days
break;
case 'office':
this.calendar.setDisabledDaysOfWeek([0, 6]); // Weekdays only
break;
case 'restaurant':
this.calendar.setDisabledDaysOfWeek([1]); // Closed Mondays
break;
case 'medical':
this.calendar.setDisabledDaysOfWeek([0]); // Closed Sundays
break;
}
}
// Seasonal adjustments
applySummerSchedule() {
this.calendar.addDisabledDayOfWeek(5); // Close Fridays in summer
}
applyWinterSchedule() {
this.calendar.removeDisabledDayOfWeek(5); // Reopen Fridays
}
}
```
## Advanced Examples
### Combining with Other Constraints
```typescript
const appointmentCalendar = new CalendarController('#appointment', {
// Date range constraints
minDate: new Date(),
maxDate: new Date(2025, 11, 31),
// Weekday constraints
disabledDaysOfWeek: [0, 6], // No weekends
// Specific date constraints
disabledDates: [
new Date(2024, 11, 25), // Christmas
new Date(2024, 11, 26), // Boxing Day
new Date(2025, 0, 1), // New Year
...getCompanyHolidays()
],
// Callback when valid date selected
onDateSelect: (date) => {
if (isBusinessDay(date)) {
bookAppointment(date);
}
}
});
```
### Interactive Configuration UI
```typescript
class CalendarConfigUI {
constructor() {
this.calendar = new CalendarController('#configurable-calendar');
this.setupUI();
}
setupUI() {
// Create checkboxes for each day
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const container = document.getElementById('day-checkboxes');
days.forEach((dayName, index) => {
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `day-${index}`;
checkbox.value = index;
checkbox.addEventListener('change', () => this.updateDisabledDays());
const label = document.createElement('label');
label.htmlFor = `day-${index}`;
label.textContent = dayName;
container.appendChild(checkbox);
container.appendChild(label);
});
}
updateDisabledDays() {
const checkboxes = document.querySelectorAll('#day-checkboxes input:checked');
const disabledDays = Array.from(checkboxes).map(cb => parseInt(cb.value));
this.calendar.setDisabledDaysOfWeek(disabledDays);
this.updateStatusDisplay(disabledDays);
}
updateStatusDisplay(disabledDays) {
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const disabledNames = disabledDays.map(day => dayNames[day]);
document.getElementById('status').textContent =
disabledNames.length ?
`Disabled: ${disabledNames.join(', ')}` :
'All days enabled';
}
// Preset configurations
presetWeekends() {
this.setCheckboxes([0, 6]);
this.updateDisabledDays();
}
presetWeekdays() {
this.setCheckboxes([1, 2, 3, 4, 5]);
this.updateDisabledDays();
}
clearAll() {
this.setCheckboxes([]);
this.updateDisabledDays();
}
setCheckboxes(days) {
document.querySelectorAll('#day-checkboxes input').forEach(cb => {
cb.checked = days.includes(parseInt(cb.value));
});
}
}
```
## Validation and Error Handling
The disabled weekdays feature includes automatic validation:
```typescript
// Invalid day numbers are filtered out
calendar.setDisabledDaysOfWeek([0, 7, -1, 6, 15]);
// Result: [0, 6] (only valid days 0-6 are kept)
// Duplicates are automatically removed
calendar.setDisabledDaysOfWeek([1, 1, 2, 2, 3]);
// Result: [1, 2, 3]
// Adding existing day has no effect
calendar.setDisabledDaysOfWeek([1, 2]);
calendar.addDisabledDayOfWeek(1); // No change
// Result: [1, 2]
// Removing non-existent day has no effect
calendar.removeDisabledDayOfWeek(5); // No change if 5 wasn't disabled
```
## Testing
The feature includes comprehensive test coverage:
- **Validation Tests**: Ensure only valid day numbers (0-6) are accepted
- **Management Tests**: Verify add/remove operations work correctly
- **Integration Tests**: Test interaction with other date constraints
- **Calendar Generation Tests**: Verify disabled days appear correctly in calendar views
- **Edge Case Tests**: Handle empty arrays, invalid inputs, and boundary conditions
Run the tests:
```bash
npm test
```
## Browser Support
The disabled weekdays feature works in all modern browsers that support:
- ES6 Arrays and Array methods
- Date object and `getDay()` method
- Standard DOM events
## Performance Considerations
- **Efficient Filtering**: Day validation uses Set operations for O(1) lookups
- **Minimal Recalculation**: Calendar only regenerates when disabled days change
- **Memory Efficient**: Stores only the day numbers, not full date objects
- **Batch Operations**: Use `setDisabledDaysOfWeek()` for multiple changes instead of multiple `addDisabledDayOfWeek()` calls
## Migration Guide
If upgrading from a version without disabled weekdays support:
### From Custom Implementation
```typescript
// Before (custom implementation)
const isWeekend = (date) => {
const day = date.getDay();
return day === 0 || day === 6;
};
const calendar = new CalendarController('#date-input', {
customValidation: (date) => !isWeekend(date)
});
// After (built-in feature)
const calendar = new CalendarController('#date-input', {
disabledDaysOfWeek: [0, 6]
});
```
### Adding to Existing Calendar
```typescript
// Existing calendar instance
const existingCalendar = new CalendarController('#date-input', {
minDate: new Date(),
maxDate: new Date(2025, 11, 31)
});
// Add disabled weekdays
existingCalendar.setDisabledDaysOfWeek([0, 6]);
```
## Best Practices
1. **Use Meaningful Constants**:
```typescript
const WEEKENDS = [0, 6];
const WEEKDAYS = [1, 2, 3, 4, 5];
const BUSINESS_DAYS = [1, 2, 3, 4, 5];
calendar.setDisabledDaysOfWeek(WEEKENDS);
```
2. **Provide User Feedback**:
```typescript
calendar.setDisabledDaysOfWeek([0, 6]);
showNotification('Weekends are now disabled');
```
3. **Document Business Rules**:
```typescript
// Business rule: No appointments on weekends or Fridays
// Fridays are reserved for internal meetings
calendar.setDisabledDaysOfWeek([0, 5, 6]);
```
4. **Combine with Validation**:
```typescript
calendar.setDisabledDaysOfWeek([0, 6]);
calendar.onDateSelect = (date) => {
if (isHoliday(date)) {
showError('Selected date is a holiday');
return;
}
processSelection(date);
};
```
5. **Save User Preferences**:
```typescript
const savedDisabledDays = localStorage.getItem('disabledDays');
if (savedDisabledDays) {
calendar.setDisabledDaysOfWeek(JSON.parse(savedDisabledDays));
}
// Save when changed
calendar.onDisabledDaysChange = (days) => {
localStorage.setItem('disabledDays', JSON.stringify(days));
};
```