ethiopian-date-picker-and-converter
Version:
A React component library for converting and picking Ethiopian dates.
424 lines (417 loc) • 16.7 kB
JavaScript
'use strict';
var React = require('react');
var DateConverter = /** @class */function () {
function DateConverter() {
this.ethiopianMonths = ['መስከረም', 'ጥቅምት', 'ኅዳር', 'ታህሳስ', 'ጥር', 'የካቲት', 'መጋቢት', 'ሚያዝያ', 'ግንቦት', 'ሰኔ', 'ሐምሌ', 'ነሐሴ', 'ጷጉሜን'];
this.ethiopianDays = ['ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ', 'እሑድ'];
}
DateConverter.prototype.getMonthName = function (month) {
if (month < 1 || month > 13) {
throw new Error('Invalid month number. Ethiopian months are between 1 and 13.');
}
return this.ethiopianMonths[month - 1];
};
DateConverter.prototype.jdFromGregorian = function (date) {
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var a = Math.floor((14 - month) / 12);
var y = year + 4800 - a;
var m = month + 12 * a - 3;
return day + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
};
DateConverter.prototype.ethiopianFromJD = function (jd) {
var r = (jd - 1723856) % 1461;
var n = r % 365 + 365 * Math.floor(r / 1460);
var year = 4 * Math.floor((jd - 1723856) / 1461) + Math.floor(r / 365) - Math.floor(r / 1460);
var month = Math.floor(n / 30) + 1;
var day = n % 30 + 1;
// Calculate day of week (1 = Monday, 7 = Sunday)
// We add 1 to make Monday = 1, and use % 7 to get the correct index
var dayOfWeek = (jd + 1) % 7;
// Convert to 0-based index for the array
var dayIndex = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
return {
year: year,
month: month,
day: day,
dayOfWeek: this.ethiopianDays[dayIndex]
};
};
DateConverter.prototype.parseDate = function (dateString) {
if (typeof dateString === 'string') {
var _a = dateString.split('-').map(Number),
year = _a[0],
month = _a[1],
day = _a[2];
return new Date(year, month - 1, day);
}
throw new Error('Invalid date format. Please use YYYY-MM-DD format');
};
DateConverter.prototype.convertGregorianToEthiopian = function (gregorianDate) {
var date = this.parseDate(gregorianDate);
var jd = this.jdFromGregorian(date);
var ethiopianDate = this.ethiopianFromJD(jd);
var formattedDate = "".concat(ethiopianDate.dayOfWeek, "\u1363 ").concat(ethiopianDate.day, " ").concat(this.getMonthName(ethiopianDate.month), " ").concat(ethiopianDate.year);
return {
ethiopianDate: ethiopianDate,
formattedDate: formattedDate
};
};
return DateConverter;
}();
var DatePicker = /** @class */function () {
function DatePicker(inputId) {
this.converter = new DateConverter();
this.input = document.getElementById(inputId);
this.onDateChange = null;
this.calendar = null;
// Initialize with current Ethiopian date
var today = new Date();
var ethiopianDate = this.converter.convertGregorianToEthiopian(this.formatDateToString(today)).ethiopianDate;
this.state = {
currentYear: ethiopianDate.year,
currentMonth: ethiopianDate.month,
selectedDate: null
};
if (this.input) {
this.input.addEventListener('focus', this.showDatePicker.bind(this));
document.addEventListener('click', this.handleClickOutside.bind(this));
}
}
DatePicker.prototype.isLeapYear = function (year) {
// Ethiopian leap year calculation
// A year is a leap year if the year divided by 4 gives remainder 3
return year % 4 === 3;
};
DatePicker.prototype.getDaysInMonth = function (year, month) {
if (month === 13) {
// ጷጉሜን has 6 days in leap years, 5 days otherwise
return this.isLeapYear(year) ? 6 : 5;
}
return 30; // All other months have 30 days
};
DatePicker.prototype.validateDate = function (year, month, day) {
if (month < 1 || month > 13) return false;
var daysInMonth = this.getDaysInMonth(year, month);
return day >= 1 && day <= daysInMonth;
};
DatePicker.prototype.formatDateToString = function (date) {
var year = date.getFullYear();
var month = String(date.getMonth() + 1).padStart(2, '0');
var day = String(date.getDate()).padStart(2, '0');
return "".concat(year, "-").concat(month, "-").concat(day);
};
DatePicker.prototype.handleClickOutside = function (event) {
if (this.calendar && event.target instanceof Node) {
if (!this.calendar.contains(event.target) && event.target !== this.input) {
this.calendar.remove();
this.calendar = null;
}
}
};
DatePicker.prototype.createNavigationHeader = function () {
var _this = this;
var header = document.createElement('div');
header.className = 'picker-header';
var prevButton = document.createElement('button');
prevButton.textContent = '←';
prevButton.onclick = function (e) {
e.stopPropagation(); // Prevent event from bubbling up
_this.navigateMonth(-1);
};
var nextButton = document.createElement('button');
nextButton.textContent = '→';
nextButton.onclick = function (e) {
e.stopPropagation(); // Prevent event from bubbling up
_this.navigateMonth(1);
};
// Create month select
var monthSelect = document.createElement('select');
monthSelect.className = 'month-select';
monthSelect.onclick = function (e) {
return e.stopPropagation();
}; // Prevent closing when clicking the select
this.converter.ethiopianMonths.forEach(function (month, index) {
var option = document.createElement('option');
option.value = String(index + 1);
option.textContent = month;
option.selected = index + 1 === _this.state.currentMonth;
monthSelect.appendChild(option);
});
monthSelect.onchange = function (e) {
e.stopPropagation(); // Prevent event from bubbling up
var target = e.target;
var newMonth = parseInt(target.value);
// If there's a selected date, validate it with the new month
if (_this.state.selectedDate) {
var daysInNewMonth = _this.getDaysInMonth(_this.state.currentYear, newMonth);
if (_this.state.selectedDate.day > daysInNewMonth) {
// Adjust the selected date to the last day of the new month
_this.state.selectedDate.day = daysInNewMonth;
}
}
_this.state.currentMonth = newMonth;
_this.updateCalendar();
};
// Create year select
var yearSelect = document.createElement('select');
yearSelect.className = 'year-select';
yearSelect.onclick = function (e) {
return e.stopPropagation();
}; // Prevent closing when clicking the select
var currentYear = this.state.currentYear;
var startYear = currentYear - 20;
var endYear = currentYear + 20;
for (var year = startYear; year <= endYear; year++) {
var option = document.createElement('option');
option.value = String(year);
option.textContent = String(year);
option.selected = year === currentYear;
yearSelect.appendChild(option);
}
yearSelect.onchange = function (e) {
e.stopPropagation(); // Prevent event from bubbling up
var target = e.target;
var newYear = parseInt(target.value);
// If there's a selected date and it's in the 13th month, validate it
if (_this.state.selectedDate && _this.state.selectedDate.month === 13) {
var daysInNewMonth = _this.getDaysInMonth(newYear, 13);
if (_this.state.selectedDate.day > daysInNewMonth) {
// Adjust the selected date to the last day of ጷጉሜን
_this.state.selectedDate.day = daysInNewMonth;
}
}
_this.state.currentYear = newYear;
_this.updateCalendar();
};
// Create a container for the selects
var selectContainer = document.createElement('div');
selectContainer.className = 'select-container';
selectContainer.style.display = 'flex';
selectContainer.style.gap = '8px';
selectContainer.style.alignItems = 'center';
selectContainer.onclick = function (e) {
return e.stopPropagation();
}; // Prevent closing when clicking the container
selectContainer.appendChild(monthSelect);
selectContainer.appendChild(yearSelect);
header.appendChild(prevButton);
header.appendChild(selectContainer);
header.appendChild(nextButton);
return header;
};
DatePicker.prototype.navigateMonth = function (delta) {
var newMonth = this.state.currentMonth + delta;
var newYear = this.state.currentYear;
if (newMonth > 13) {
newMonth = 1;
newYear++;
} else if (newMonth < 1) {
newMonth = 13;
newYear--;
}
// Validate selected date when navigating months
if (this.state.selectedDate) {
var daysInNewMonth = this.getDaysInMonth(newYear, newMonth);
if (this.state.selectedDate.day > daysInNewMonth) {
this.state.selectedDate.day = daysInNewMonth;
}
}
this.state.currentMonth = newMonth;
this.state.currentYear = newYear;
this.updateCalendar();
};
DatePicker.prototype.createDaysGrid = function () {
var _this = this;
var _a, _b, _c;
var table = document.createElement('table');
table.className = 'calendar-grid';
table.style.width = '100%';
// Create table header with day names
var thead = document.createElement('thead');
var headerRow = document.createElement('tr');
var dayNames = ['ሰኞ', 'ማክሰኞ', 'ረቡዕ', 'ሐሙስ', 'ዓርብ', 'ቅዳሜ', 'እሑድ'];
dayNames.forEach(function (day) {
var th = document.createElement('th');
th.textContent = day;
th.style.width = '14.28%';
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Create table body with day cells
var tbody = document.createElement('tbody');
var daysInMonth = this.getDaysInMonth(this.state.currentYear, this.state.currentMonth);
var currentRow = document.createElement('tr');
// Get current Ethiopian date for highlighting
var today = new Date();
var currentEthiopianDate = this.converter.convertGregorianToEthiopian(this.formatDateToString(today)).ethiopianDate;
// Add empty cells for proper day alignment
var firstDayOfMonth = this.getFirstDayOfMonth();
for (var i = 0; i < firstDayOfMonth; i++) {
var emptyCell = document.createElement('td');
currentRow.appendChild(emptyCell);
}
var _loop_1 = function (day) {
if ((day + firstDayOfMonth - 1) % 7 === 0 && day !== 1) {
tbody.appendChild(currentRow);
currentRow = document.createElement('tr');
}
var td = document.createElement('td');
td.textContent = String(day);
td.className = 'day';
// Add current-day class if it's today
if (day === currentEthiopianDate.day && this_1.state.currentMonth === currentEthiopianDate.month && this_1.state.currentYear === currentEthiopianDate.year) {
td.classList.add('current-day');
}
// Add selected-day class if this date is selected
if (day === ((_a = this_1.state.selectedDate) === null || _a === void 0 ? void 0 : _a.day) && this_1.state.currentMonth === ((_b = this_1.state.selectedDate) === null || _b === void 0 ? void 0 : _b.month) && this_1.state.currentYear === ((_c = this_1.state.selectedDate) === null || _c === void 0 ? void 0 : _c.year)) {
td.classList.add('selected-day');
}
td.onclick = function () {
return _this.selectDate(day);
};
currentRow.appendChild(td);
};
var this_1 = this;
for (var day = 1; day <= daysInMonth; day++) {
_loop_1(day);
}
tbody.appendChild(currentRow);
table.appendChild(tbody);
return table;
};
DatePicker.prototype.getFirstDayOfMonth = function () {
// This is a simplified version. For accurate implementation,
// you would need to calculate the actual first day of the Ethiopian month
return 0; // Assuming months start on Monday for now
};
DatePicker.prototype.selectDate = function (day) {
var _a;
if (!this.validateDate(this.state.currentYear, this.state.currentMonth, day)) {
return;
}
// Update selected date in state
this.state.selectedDate = {
year: this.state.currentYear,
month: this.state.currentMonth,
day: day
};
// Update the calendar to show the selection
this.updateCalendar();
if (this.input && this.onDateChange) {
var formattedDate = "".concat(this.state.selectedDate.day, " ").concat(this.converter.getMonthName(this.state.selectedDate.month), " ").concat(this.state.selectedDate.year);
this.input.value = formattedDate;
this.onDateChange(formattedDate);
(_a = this.calendar) === null || _a === void 0 ? void 0 : _a.remove();
this.calendar = null;
}
};
DatePicker.prototype.updateCalendar = function () {
if (this.calendar) {
this.calendar.innerHTML = '';
this.calendar.appendChild(this.createNavigationHeader());
this.calendar.appendChild(this.createDaysGrid());
}
};
DatePicker.prototype.showDatePicker = function () {
if (this.calendar) {
return;
}
this.calendar = document.createElement('div');
this.calendar.className = 'ethiopian-date-picker';
this.calendar.style.minWidth = '350px';
this.updateCalendar();
// Position the calendar below the input
if (this.input) {
var rect = this.input.getBoundingClientRect();
this.calendar.style.position = 'absolute';
this.calendar.style.top = "".concat(rect.bottom + 5, "px");
this.calendar.style.left = "".concat(rect.left, "px");
document.body.appendChild(this.calendar);
}
};
DatePicker.prototype.destroy = function () {
if (this.input) {
this.input.removeEventListener('focus', this.showDatePicker.bind(this));
document.removeEventListener('click', this.handleClickOutside.bind(this));
}
if (this.calendar) {
this.calendar.remove();
this.calendar = null;
}
};
return DatePicker;
}();
var EthiopianDatePicker = function (_a) {
var _b = _a.id,
id = _b === void 0 ? 'default-id-date-picker' : _b,
_c = _a.placeholder,
placeholder = _c === void 0 ? 'Select a date' : _c,
onDateChange = _a.onDateChange;
var datePickerRef = React.useRef(null);
React.useEffect(function () {
datePickerRef.current = new DatePicker(id);
datePickerRef.current.onDateChange = function (formattedDate) {
onDateChange(formattedDate);
};
return function () {
if (datePickerRef.current) {
datePickerRef.current.destroy();
}
};
}, [id, onDateChange]);
return React.createElement("div", null, React.createElement("input", {
id: id,
type: "text",
placeholder: placeholder,
className: "padded-input",
readOnly: true
}));
};
/**
* Converts a Gregorian date to Ethiopian date
* @param {string} gregorianDate - The Gregorian date in 'YYYY-MM-DD' format
* @param {ReturnFormat} [returnFormat='full'] - The desired format of the returned date
* @returns {(string | EthiopianDateObject | null)} The converted Ethiopian date in the specified format
* @example
* // Returns full format (e.g., 'ሰኞ፣ ጥር 7 ቀን 2016 ዓ.ም.')
* EthiopianDateConverter('2024-01-15')
*
* // Returns date only format (e.g., '2016-5-7')
* EthiopianDateConverter('2024-01-15', 'date-only')
*
* // Returns object format
* EthiopianDateConverter('2024-01-15', 'object')
*/
var EthiopianDateConverter = function (gregorianDate, returnFormat) {
if (returnFormat === void 0) {
returnFormat = 'full';
}
try {
var converter = new DateConverter();
var result = converter.convertGregorianToEthiopian(gregorianDate);
switch (returnFormat) {
case 'date-only':
return "".concat(result.ethiopianDate.year, "-").concat(result.ethiopianDate.month, "-").concat(result.ethiopianDate.day);
case 'object':
return {
year: result.ethiopianDate.year,
month: result.ethiopianDate.month,
day: result.ethiopianDate.day,
monthName: converter.getMonthName(result.ethiopianDate.month),
weekDay: result.ethiopianDate.dayOfWeek
};
case 'full':
default:
return result.formattedDate;
}
} catch (error) {
console.error('Error converting date:', error);
return null;
}
};
exports.EthiopianDateConverter = EthiopianDateConverter;
exports.EthiopianDatePicker = EthiopianDatePicker;
//# sourceMappingURL=index.js.map