kenat
Version:
A JavaScript library for the Ethiopian calendar with date and time support.
212 lines (188 loc) • 9.55 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kenat Calendar Example</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 font-sans text-center p-4">
<h1 class="text-3xl font-bold mb-6">Kenat Calendar</h1>
<div class="flex flex-wrap gap-4 items-center justify-center mb-4">
<label class="flex items-center space-x-2">
<span>Mode:</span>
<select id="modeSelector" class="px-2 py-1 border rounded">
<option value="none">All</option>
<option value="christian">Christian</option>
<option value="muslim">Muslim</option>
<option value="public" selected>Public</option>
</select>
</label>
<label class="flex items-center space-x-2" id="saintToggleWrapper" style="display: none">
<input type="checkbox" id="showAllSaintsToggle" class="accent-blue-500">
<span>Show All Saints</span>
</label>
</div>
<div id="calendar" class="mb-4">Loading...</div>
<div id="filterControls"
class="flex flex-wrap gap-4 items-center justify-center p-4 bg-white rounded-lg shadow mt-4">
<label class="flex items-center space-x-2">
<input type="checkbox" name="holidayTag" value="all" checked class="accent-blue-500">
<span>All Holidays</span>
</label>
<label class="flex items-center space-x-2">
<input type="checkbox" name="holidayTag" value="public" class="accent-blue-500">
<span>Public</span>
</label>
<label class="flex items-center space-x-2">
<input type="checkbox" name="holidayTag" value="christian" class="accent-blue-500">
<span>Christian</span>
</label>
<label class="flex items-center space-x-2">
<input type="checkbox" name="holidayTag" value="muslim" class="accent-blue-500">
<span>Muslim</span>
</label>
</div>
<div id="holidayModal" class="fixed hidden inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center">
<div class="bg-white p-6 rounded-lg max-w-lg w-full relative shadow-lg">
<button class="absolute top-2 right-4 text-gray-600 text-xl font-bold" id="closeModal">×</button>
<div id="modalBody" class="text-left"></div>
</div>
</div>
<script type="module">
import Kenat, { MonthGrid, HolidayTags, toArabic } from '../../../src/index.js';
import { monthNames } from '../../../src/constants.js';
let monthGridInstance;
let useGeez = false;
let weekdayLang = 'amharic';
let weekStart = 1;
let holidayFilter = null;
let activeMode = 'public';
let showAllSaints = false;
const initialDate = new Kenat().getEthiopian();
let currentYear = initialDate.year;
let currentMonth = initialDate.month;
const modal = document.getElementById('holidayModal');
const modalBody = document.getElementById('modalBody');
const closeModal = document.getElementById('closeModal');
const saintToggleWrapper = document.getElementById('saintToggleWrapper');
const saintToggle = document.getElementById('showAllSaintsToggle');
function showHolidayModal(holidays) {
modalBody.innerHTML = holidays.map((h, i) => {
return `<h3 class="text-lg font-bold mb-1 ${h.isNigs ? 'text-yellow-700' : ''}">${h.name}</h3><p class="text-sm text-gray-600 mb-2">${h.description || ''}</p>${i < holidays.length - 1 ? '<hr class="my-2">' : ''}`;
}).join('');
modal.classList.remove('hidden');
}
closeModal.onclick = () => modal.classList.add('hidden');
window.onclick = e => { if (e.target === modal) modal.classList.add('hidden'); };
function renderCalendar(gridData) {
const { headers, days, year, month } = gridData;
const yearOptions = Array.from({ length: 201 }, (_, i) => 1900 + i).map(y => `<option value="${y}" ${y === +year ? 'selected' : ''}>${y}</option>`).join('');
const monthOptions = monthNames.amharic.map((name, i) => `<option value="${i + 1}" ${i + 1 === month ? 'selected' : ''}>${name}</option>`).join('');
let html = `
<div class="flex justify-center items-center gap-4 mb-4">
<button id="prevMonth" class="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300">⬅️</button>
<select id="monthSelector" class="px-2 py-1 border rounded">${monthOptions}</select>
<select id="yearSelector" class="px-2 py-1 border rounded">${yearOptions}</select>
<button id="nextMonth" class="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300">➡️</button>
</div>
<div class="overflow-x-auto">
<table class="min-w-full bg-white rounded shadow">
<thead class="bg-gray-100 text-gray-700">
<tr>${headers.map(d => `<th class="py-2">${d}</th>`).join('')}</tr>
</thead>
<tbody>
`;
for (let i = 0; i < days.length; i += 7) {
html += '<tr>';
for (let j = 0; j < 7; j++) {
const item = days[i + j];
if (!item) {
html += '<td class="border h-20 w-20"></td>';
} else {
const holidayNames = item.holidays.map(h => `<div class="text-xs ${h.isNigs ? 'text-yellow-800 font-bold' : ''}">${h.name}</div>`).join('');
const classes = [
'border p-1 align-top h-20 w-20 text-xs',
item.isToday ? 'bg-yellow-100 border-yellow-500' : '',
item.holidays.length ? 'cursor-pointer bg-blue-50 hover:bg-blue-100' : ''
].join(' ');
html += `<td class="${classes}" data-day-index="${i + j}"><div class="font-semibold">${item.ethiopian.day}</div><div class="text-gray-500">${item.gregorian.month}/${item.gregorian.day}</div>${holidayNames}</td>`;
}
}
html += '</tr>';
}
html += '</tbody></table></div>';
document.getElementById('calendar').innerHTML = html;
attachListeners();
}
function attachListeners() {
document.getElementById('prevMonth').onclick = () => {
const newGrid = monthGridInstance.down().generate();
currentYear = +newGrid.year;
currentMonth = newGrid.month;
renderCalendar(newGrid);
};
document.getElementById('nextMonth').onclick = () => {
const newGrid = monthGridInstance.up().generate();
currentYear = +newGrid.year;
currentMonth = newGrid.month;
renderCalendar(newGrid);
};
document.getElementById('monthSelector').onchange = (e) => {
currentMonth = +e.target.value;
rerender();
};
document.getElementById('yearSelector').onchange = (e) => {
currentYear = +e.target.value;
rerender();
};
document.getElementById('modeSelector').onchange = (e) => {
activeMode = e.target.value;
showAllSaints = false;
saintToggle.checked = false;
saintToggleWrapper.style.display = activeMode === 'christian' ? 'flex' : 'none';
rerender();
};
saintToggle.onchange = (e) => {
showAllSaints = e.target.checked;
rerender();
};
document.querySelectorAll('[data-day-index]').forEach(td => {
td.onclick = () => {
const index = +td.dataset.dayIndex;
const dayData = monthGridInstance.generate().days[index];
if (dayData.holidays.length) showHolidayModal(dayData.holidays);
};
});
const allCheckbox = document.querySelector('input[name="holidayTag"][value="all"]');
const otherCheckboxes = document.querySelectorAll('input[name="holidayTag"]:not([value="all"])');
function updateFilter() {
const checked = Array.from(otherCheckboxes).filter(cb => cb.checked).map(cb => cb.value);
holidayFilter = checked.length ? checked : null;
allCheckbox.checked = !holidayFilter;
rerender();
}
allCheckbox.onchange = () => {
if (allCheckbox.checked) {
otherCheckboxes.forEach(cb => cb.checked = false);
holidayFilter = null;
rerender();
}
};
otherCheckboxes.forEach(cb => cb.onchange = updateFilter);
}
function rerender() {
monthGridInstance = new MonthGrid({
year: currentYear,
month: currentMonth,
useGeez, weekdayLang, weekStart,
holidayFilter,
mode: activeMode,
showAllSaints
});
renderCalendar(monthGridInstance.generate());
}
rerender();
</script>
</body>
</html>