UNPKG

ethio-calendar-converter-angular

Version:
356 lines (312 loc) 12.7 kB
import { Component, Output, EventEmitter } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { EthiopianCalendarService } from './ethiopian-calendar.service'; import { EthiopianDate, GregorianDate } from './models/calendar.model'; @Component({ selector: 'ethiopian-calendar', standalone: true, imports: [ CommonModule, FormsModule ], template: ` <div class="bg-white rounded-lg shadow-lg p-6 max-w-md mx-auto"> <!-- Ethiopian Calendar Input --> <div class="mb-8"> <h3 class="text-lg font-medium text-gray-900 mb-4">Ethiopian Calendar</h3> <div class="relative"> <input type="text" [value]="formatEthiopianDateForInput(ethiopianDate)" (click)="toggleEthiopianPicker()" readonly class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all cursor-pointer" /> <label class="absolute -top-2 left-2 bg-white px-1 text-xs text-gray-600">Select Date</label> <!-- Calendar Icon --> <button (click)="toggleEthiopianPicker()" class="absolute right-2 top-2 text-gray-400 hover:text-gray-600 focus:outline-none" > <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> </button> <!-- Ethiopian Calendar Picker --> <div *ngIf="showEthiopianPicker" class="absolute left-0 mt-1 p-4 bg-white rounded-lg shadow-lg border border-gray-200 z-50 w-[320px]"> <!-- Month and Year Navigation --> <div class="flex justify-between items-center mb-4"> <button (click)="previousMonth()" class="p-1 hover:bg-gray-100 rounded-full" > <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" /> </svg> </button> <div class="flex items-center space-x-2"> <select [(ngModel)]="ethiopianDate.month" (change)="updateEthiopianDate()" class="text-sm border-none bg-transparent font-semibold focus:outline-none focus:ring-0" > <option *ngFor="let month of ethiopianMonths" [value]="month.value"> {{month.label}} </option> </select> <select [(ngModel)]="ethiopianDate.year" (change)="updateEthiopianDate()" class="text-sm border-none bg-transparent font-semibold focus:outline-none focus:ring-0" > <option *ngFor="let year of ethiopianYears" [value]="year">{{year}}</option> </select> </div> <button (click)="nextMonth()" class="p-1 hover:bg-gray-100 rounded-full" > <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </svg> </button> </div> <!-- Calendar Grid --> <div class="grid grid-cols-7 gap-1"> <!-- Week days --> <div *ngFor="let day of weekDays" class="text-center text-xs font-medium text-gray-500 py-1"> {{day}} </div> <!-- Days --> <ng-container *ngFor="let day of getDaysArray()"> <button *ngIf="day !== 0" (click)="selectDay(day)" [class]="getDayClasses(day)" > {{day}} </button> <div *ngIf="day === 0" class="h-8"></div> </ng-container> </div> <!-- Today Button --> <div class="mt-4 flex justify-between items-center border-t pt-4"> <button (click)="selectToday()" class="text-sm text-blue-600 hover:text-blue-800 font-medium" > Today </button> <button (click)="toggleEthiopianPicker()" class="text-sm text-gray-600 hover:text-gray-800 font-medium" > Close </button> </div> </div> </div> <div *ngIf="ethiopianDate" class="mt-2 text-sm text-gray-600"> Selected: {{formatEthiopianDateForDisplay(ethiopianDate)}} </div> <button (click)="convertToGregorian()" class="mt-4 w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors" > Convert to Gregorian </button> </div> <!-- Gregorian Calendar Input --> <div class="mb-8"> <h3 class="text-lg font-medium text-gray-900 mb-4">Gregorian Calendar</h3> <div class="relative"> <input type="date" [value]="formatDateForInput(gregorianDate)" (change)="onGregorianDateSelected($event)" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all" /> <label class="absolute -top-2 left-2 bg-white px-1 text-xs text-gray-600">Select Date</label> </div> <div *ngIf="gregorianDate" class="mt-2 text-sm text-gray-600"> Selected: {{formatGregorianDate(gregorianDate)}} </div> <button (click)="convertToEthiopian()" class="mt-4 w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors" > Convert to Ethiopian </button> </div> <!-- Conversion Result --> <div *ngIf="lastConversion" class="p-4 bg-gray-50 rounded-lg border border-gray-200"> <p class="text-sm font-medium text-gray-900">Last Conversion:</p> <p class="text-sm text-gray-600 mt-1">{{lastConversion}}</p> </div> </div> ` }) export class EthiopianCalendarComponent { ethiopianDate: EthiopianDate = { year: 2015, month: 4, day: 25 }; gregorianDate: GregorianDate = { year: 2022, month: 3, day: 25 }; lastConversion: string = ''; showEthiopianPicker: boolean = false; ethiopianMonths = [ { value: 1, label: 'Meskerem' }, { value: 2, label: 'Tikimt' }, { value: 3, label: 'Hidar' }, { value: 4, label: 'Tahsas' }, { value: 5, label: 'Tir' }, { value: 6, label: 'Yekatit' }, { value: 7, label: 'Megabit' }, { value: 8, label: 'Miyazia' }, { value: 9, label: 'Ginbot' }, { value: 10, label: 'Sene' }, { value: 11, label: 'Hamle' }, { value: 12, label: 'Nehase' }, { value: 13, label: 'Pagume' } ]; get ethiopianYears(): number[] { const currentYear = new Date().getFullYear(); const years: number[] = []; for (let i = currentYear - 100; i <= currentYear + 100; i++) { years.push(i - 8); // Convert to approximate Ethiopian year } return years; } @Output() ethiopianDateChange = new EventEmitter<EthiopianDate>(); @Output() gregorianDateChange = new EventEmitter<GregorianDate>(); constructor(private calendarService: EthiopianCalendarService) {} toggleEthiopianPicker() { this.showEthiopianPicker = !this.showEthiopianPicker; } getEthiopianDays(): number[] { const maxDays = this.ethiopianDate.month === 13 ? (this.calendarService['isEthiopianLeapYear'](this.ethiopianDate.year) ? 6 : 5) : 30; return Array.from({length: maxDays}, (_, i) => i + 1); } applyEthiopianDate() { this.convertToGregorian(); this.toggleEthiopianPicker(); } formatEthiopianDateForDisplay(date: EthiopianDate): string { const monthName = this.ethiopianMonths.find(m => m.value === date.month)?.label || ''; return `${monthName} ${date.day}, ${date.year}`; } convertToGregorian() { try { this.gregorianDate = this.calendarService.toGregorian(this.ethiopianDate); this.gregorianDateChange.emit(this.gregorianDate); this.lastConversion = `Ethiopian ${this.formatDate(this.ethiopianDate)} → Gregorian ${this.formatDate(this.gregorianDate)}`; } catch (error) { this.lastConversion = 'Invalid Ethiopian date'; } } convertToEthiopian() { try { this.ethiopianDate = this.calendarService.toEthiopian(this.gregorianDate); this.ethiopianDateChange.emit(this.ethiopianDate); this.lastConversion = `Gregorian ${this.formatDate(this.gregorianDate)} → Ethiopian ${this.formatDate(this.ethiopianDate)}`; } catch (error) { this.lastConversion = 'Invalid Gregorian date'; } } onGregorianDateSelected(event: any) { const date = new Date(event.target.value); this.gregorianDate = { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() }; this.convertToEthiopian(); } formatDate(date: EthiopianDate | GregorianDate): string { return `${date.year}-${String(date.month).padStart(2, '0')}-${String(date.day).padStart(2, '0')}`; } formatDateForInput(date: GregorianDate): string { return `${date.year}-${String(date.month).padStart(2, '0')}-${String(date.day).padStart(2, '0')}`; } formatGregorianDate(date: GregorianDate): string { return new Date(date.year, date.month - 1, date.day) .toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); } weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; previousMonth() { if (this.ethiopianDate.month > 1) { this.ethiopianDate.month--; } else { this.ethiopianDate.month = 13; this.ethiopianDate.year--; } this.updateEthiopianDate(); } nextMonth() { if (this.ethiopianDate.month < 13) { this.ethiopianDate.month++; } else { this.ethiopianDate.month = 1; this.ethiopianDate.year++; } this.updateEthiopianDate(); } getDaysArray(): number[] { const daysInMonth = this.ethiopianDate.month === 13 ? (this.calendarService['isEthiopianLeapYear'](this.ethiopianDate.year) ? 6 : 5) : 30; // Create array with padding for alignment const days: number[] = []; const firstDayOffset = this.getFirstDayOffset(); // Add padding for (let i = 0; i < firstDayOffset; i++) { days.push(0); } // Add days for (let i = 1; i <= daysInMonth; i++) { days.push(i); } return days; } getFirstDayOffset(): number { // This is a simplified version - you might want to implement proper Ethiopian calendar day-of-week calculation return (this.ethiopianDate.month * 2) % 7; } getDayClasses(day: number): string { const baseClasses = 'h-8 w-8 rounded-full flex items-center justify-center text-sm'; const isSelected = day === this.ethiopianDate.day; if (isSelected) { return `${baseClasses} bg-blue-600 text-white`; } return `${baseClasses} hover:bg-gray-100 text-gray-700`; } selectDay(day: number) { this.ethiopianDate.day = day; this.updateEthiopianDate(); this.toggleEthiopianPicker(); } selectToday() { const today = this.calendarService.toEthiopian({ year: new Date().getFullYear(), month: new Date().getMonth() + 1, day: new Date().getDate() }); this.ethiopianDate = today; this.updateEthiopianDate(); this.toggleEthiopianPicker(); } updateEthiopianDate() { this.ethiopianDateChange.emit(this.ethiopianDate); this.convertToGregorian(); } formatEthiopianDateForInput(date: EthiopianDate): string { const monthName = this.ethiopianMonths.find(m => m.value === date.month)?.label || ''; return `${monthName} ${date.day}, ${date.year}`; } }