UNPKG

kenat

Version:

A JavaScript library for the Ethiopian calendar with date and time support.

234 lines (207 loc) 11.8 kB
<!DOCTYPE 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> <style> body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; text-align: center; background-color: #f9f9f9; padding: 1rem;} table { border-collapse: collapse; margin: 1rem auto; box-shadow: 0 4px 12px rgba(0,0,0,0.1); background-color: #fff; } th, td { border: 1px solid #e0e0e0; padding: 0.5rem; width: 85px; height: 85px; vertical-align: top; } th { background-color: #f5f5f5; font-weight: 600; } .today { background-color: #fffde7; border: 2px solid #ffc107; font-weight: bold; } .holiday { background-color: #e3f2fd; cursor: pointer; transition: background-color 0.2s; } .holiday:hover { background-color: #bbdefb; } .holiday-labels { font-size: 0.75rem; color: #1e88e5; margin-top: 5px; line-height: 1.2; } /* Navigation and Controls Styling */ .header-controls, .controls, .filter-controls { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin: 1rem 0; align-items: center; } .nav-controls { display: flex; align-items: center; gap: 0.5rem; } .filter-controls { margin-bottom: 1.5rem; background-color: #fff; padding: 1rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); } .filter-controls label { font-size: 0.9rem; cursor: pointer; display: flex; align-items: center; gap: 4px; } button, select { padding: 0.6rem 1.2rem; font-size: 0.9rem; cursor: pointer; border-radius: 6px; border: 1px solid #ccc; background-color: #fff; transition: background-color 0.2s, box-shadow 0.2s; } select { padding: 0.5rem; } button:hover { background-color: #f0f0f0; } button:active { box-shadow: inset 0 2px 4px rgba(0,0,0,0.1); } /* Modal Styles */ .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.5); justify-content: center; align-items: center; } .modal-content { background-color: #fefefe; margin: auto; padding: 25px; border: 1px solid #888; width: 90%; max-width: 500px; border-radius: 10px; position: relative; box-shadow: 0 5px 15px rgba(0,0,0,0.3); text-align: left; } .modal-close { color: #aaa; position: absolute; top: 10px; right: 20px; font-size: 28px; font-weight: bold; cursor: pointer; } .modal-close:hover { color: #000; } .modal-content h3 { margin-top: 0; } .modal-content hr { border: 0; border-top: 1px solid #eee; margin: 15px 0; } </style> </head> <body> <h1>Kenat Calendar</h1> <div id="calendar">Loading...</div> <!-- Modal HTML --> <div id="holidayModal" class="modal"> <div class="modal-content"> <span class="modal-close">&times;</span> <div id="modalBody"></div> </div> </div> <script type="module"> import Kenat, { MonthGrid, HolidayTags, toArabic } from '../../src/index.js'; import { monthNames } from '../../src/constants.js'; let monthGridInstance; // Settings let useGeez = false; let weekdayLang = 'amharic'; let weekStart = 1; let holidayFilter = null; const initialDate = new Kenat().getEthiopian(); let currentYear = initialDate.year; let currentMonth = initialDate.month; // --- MODAL LOGIC --- const modal = document.getElementById('holidayModal'); const modalBody = document.getElementById('modalBody'); const closeModal = document.querySelector('.modal-close'); function showHolidayModal(holidays) { let content = ''; holidays.forEach((h, index) => { content += `<h3>${h.name}</h3><p>${h.description}</p>`; if (index < holidays.length - 1) content += '<hr>'; }); modalBody.innerHTML = content; modal.style.display = 'flex'; } closeModal.onclick = () => { modal.style.display = 'none'; } window.onclick = (event) => { if (event.target == modal) modal.style.display = 'none'; } // --- CALENDAR RENDERING --- function renderCalendar(gridData) { let { headers, days, year, month, monthName } = gridData; const holidaysForDay = (item) => item.holidays.map(h => h.name).join(', '); const yearForComparison = typeof year === 'string' ? toArabic(year) : year; const yearOptions = Array.from({length: 201}, (_, i) => 1900 + i) .map(y => `<option value="${y}" ${y === yearForComparison ? '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="header-controls"> <button id="prevMonth">⬅️</button> <div class="nav-controls"> <select id="monthSelector">${monthOptions}</select> <select id="yearSelector">${yearOptions}</select> </div> <button id="nextMonth">➡️</button> </div> <div class="controls"> <button id="toggleGeez">Geez (${useGeez ? 'ON' : 'OFF'})</button> <button id="toggleLang">Lang (${weekdayLang})</button> <button id="toggleWeekStart">Start (${weekStart === 1 ? 'Mon' : 'Sun'})</button> </div> <div class="filter-controls" id="filterControls"></div> <table><thead><tr>`; html += headers.map(day => `<th>${day}</th>`).join(''); html += '</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></td>'; } else { const todayClass = item.isToday ? 'today' : ''; const isHoliday = item.holidays && item.holidays.length > 0; const holidaysText = isHoliday ? holidaysForDay(item) : ''; const holidayAttr = isHoliday ? `data-day-index="${i + j}"` : ''; html += `<td class="${todayClass} ${isHoliday ? 'holiday' : ''}" ${holidayAttr}> <strong>${item.ethiopian.day}</strong><br/> <small>${item.gregorian.month}/${item.gregorian.day}</small> ${holidaysText ? `<div class="holiday-labels">${holidaysText}</div>` : ''} </td>`; } } html += '</tr>'; } html += '</tbody></table>'; document.getElementById('calendar').innerHTML = html; renderFilterControls(); attachEventListeners(); } function renderFilterControls() { const controlsContainer = document.getElementById('filterControls'); let controlsHtml = `<label><input type="checkbox" name="holidayTag" value="all" ${!holidayFilter ? 'checked' : ''}> All</label>`; for (const key in HolidayTags) { const value = HolidayTags[key]; const isChecked = holidayFilter && holidayFilter.includes(value); controlsHtml += `<label><input type="checkbox" name="holidayTag" value="${value}" ${isChecked ? 'checked' : ''}> ${value}</label>`; } controlsContainer.innerHTML = controlsHtml; } function attachEventListeners() { document.getElementById('prevMonth').onclick = () => { const newGrid = monthGridInstance.down().generate(); currentYear = (typeof newGrid.year === 'string') ? toArabic(newGrid.year) : newGrid.year; currentMonth = newGrid.month; renderCalendar(newGrid); }; document.getElementById('nextMonth').onclick = () => { const newGrid = monthGridInstance.up().generate(); currentYear = (typeof newGrid.year === 'string') ? toArabic(newGrid.year) : newGrid.year; currentMonth = newGrid.month; renderCalendar(newGrid); }; document.getElementById('monthSelector').onchange = (e) => { currentMonth = parseInt(e.target.value); rerender(); }; document.getElementById('yearSelector').onchange = (e) => { currentYear = parseInt(e.target.value); rerender(); }; document.getElementById('toggleGeez').onclick = () => { useGeez = !useGeez; rerender(); }; document.getElementById('toggleLang').onclick = () => { weekdayLang = weekdayLang === 'amharic' ? 'english' : 'amharic'; rerender(); }; document.getElementById('toggleWeekStart').onclick = () => { weekStart = weekStart === 1 ? 0 : 1; rerender(); }; document.querySelectorAll('.holiday').forEach(cell => { cell.onclick = () => { const dayIndex = parseInt(cell.getAttribute('data-day-index')); const dayData = monthGridInstance.generate().days[dayIndex]; if (dayData && dayData.holidays.length > 0) { showHolidayModal(dayData.holidays); } }; }); const allCheckbox = document.querySelector('input[name="holidayTag"][value="all"]'); const otherCheckboxes = document.querySelectorAll('input[name="holidayTag"]:not([value="all"])'); const updateFilter = () => { const checkedValues = Array.from(otherCheckboxes).filter(i => i.checked).map(i => i.value); if (checkedValues.length === 0) { allCheckbox.checked = true; holidayFilter = null; } else { allCheckbox.checked = false; holidayFilter = checkedValues; } rerender(); }; allCheckbox.onchange = () => { if (allCheckbox.checked) { otherCheckboxes.forEach(cb => cb.checked = false); holidayFilter = null; rerender(); } else { allCheckbox.checked = true; } }; otherCheckboxes.forEach(checkbox => { checkbox.onchange = updateFilter; }); } function rerender() { monthGridInstance = new MonthGrid({ year: currentYear, month: currentMonth, useGeez, weekdayLang, weekStart, holidayFilter, }); renderCalendar(monthGridInstance.generate()); } // Initial render rerender(); </script> </body> </html>