Optical refinement for calendar
This commit is contained in:
@@ -43,12 +43,62 @@
|
|||||||
calendar.removeAttribute('data-luxtools-loading');
|
calendar.removeAttribute('data-luxtools-loading');
|
||||||
}
|
}
|
||||||
|
|
||||||
var buttons = calendar.querySelectorAll('button.luxtools-calendar-nav-button');
|
var buttons = calendar.querySelectorAll('button.luxtools-calendar-nav-button, button.luxtools-calendar-today-button');
|
||||||
for (var i = 0; i < buttons.length; i++) {
|
for (var i = 0; i < buttons.length; i++) {
|
||||||
buttons[i].disabled = !!busy;
|
buttons[i].disabled = !!busy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCalendarStateKey(calendar) {
|
||||||
|
var baseNs = calendar.getAttribute('data-base-ns') || 'chronological';
|
||||||
|
return 'luxtools.calendar.month.' + baseNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readSavedCalendarMonth(calendar) {
|
||||||
|
if (!window.localStorage) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
var raw = window.localStorage.getItem(getCalendarStateKey(calendar));
|
||||||
|
if (!raw) return null;
|
||||||
|
|
||||||
|
var parsed = JSON.parse(raw);
|
||||||
|
var year = parseInt(parsed && parsed.year, 10);
|
||||||
|
var month = parseInt(parsed && parsed.month, 10);
|
||||||
|
if (!year || month < 1 || month > 12) return null;
|
||||||
|
|
||||||
|
return { year: year, month: month };
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCalendarMonth(calendar) {
|
||||||
|
if (!window.localStorage) return;
|
||||||
|
|
||||||
|
var year = parseInt(calendar.getAttribute('data-current-year') || '', 10);
|
||||||
|
var month = parseInt(calendar.getAttribute('data-current-month') || '', 10);
|
||||||
|
if (!year || month < 1 || month > 12) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem(getCalendarStateKey(calendar), JSON.stringify({
|
||||||
|
year: year,
|
||||||
|
month: month
|
||||||
|
}));
|
||||||
|
} catch (e) {
|
||||||
|
// ignore storage failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSavedCalendarMonth(calendar) {
|
||||||
|
if (!window.localStorage) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
window.localStorage.removeItem(getCalendarStateKey(calendar));
|
||||||
|
} catch (e) {
|
||||||
|
// ignore storage failures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fetchCalendarMonth(calendar, year, month) {
|
function fetchCalendarMonth(calendar, year, month) {
|
||||||
var ajaxUrl = calendar.getAttribute('data-luxtools-ajax-url') || '';
|
var ajaxUrl = calendar.getAttribute('data-luxtools-ajax-url') || '';
|
||||||
if (!ajaxUrl) return Promise.reject(new Error('Missing calendar ajax url'));
|
if (!ajaxUrl) return Promise.reject(new Error('Missing calendar ajax url'));
|
||||||
@@ -105,53 +155,99 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateCalendarMonth(calendar, direction) {
|
function replaceCalendar(calendar, replacement, persistState) {
|
||||||
var year = parseInt(calendar.getAttribute('data-current-year') || '', 10);
|
if (persistState !== false) {
|
||||||
var month = parseInt(calendar.getAttribute('data-current-month') || '', 10);
|
saveCalendarMonth(replacement);
|
||||||
if (!year || !month) return;
|
}
|
||||||
|
syncCalendarToday(replacement);
|
||||||
|
calendar.replaceWith(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
var next = getNextMonth(year, month, direction);
|
function loadCalendarMonth(calendar, targetYear, targetMonth, persistState) {
|
||||||
setCalendarBusy(calendar, true);
|
setCalendarBusy(calendar, true);
|
||||||
|
|
||||||
fetchCalendarMonth(calendar, next.year, next.month)
|
fetchCalendarMonth(calendar, targetYear, targetMonth)
|
||||||
.then(function (html) {
|
.then(function (html) {
|
||||||
var replacement = parseCalendarFromHtml(html);
|
var replacement = parseCalendarFromHtml(html);
|
||||||
if (!replacement) {
|
if (!replacement) {
|
||||||
throw new Error('Calendar markup missing in response');
|
throw new Error('Calendar markup missing in response');
|
||||||
}
|
}
|
||||||
|
|
||||||
syncCalendarToday(replacement);
|
replaceCalendar(calendar, replacement, persistState);
|
||||||
calendar.replaceWith(replacement);
|
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
var fallbackLink = calendar.querySelector('a.luxtools-calendar-month-link');
|
window.location.reload();
|
||||||
if (fallbackLink && fallbackLink.href) {
|
|
||||||
window.location.href = fallbackLink.href;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.finally(function () {
|
.finally(function () {
|
||||||
setCalendarBusy(calendar, false);
|
setCalendarBusy(calendar, false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function navigateCalendarMonth(calendar, direction, persistState) {
|
||||||
|
var year = parseInt(calendar.getAttribute('data-current-year') || '', 10);
|
||||||
|
var month = parseInt(calendar.getAttribute('data-current-month') || '', 10);
|
||||||
|
if (!year || !month) return;
|
||||||
|
|
||||||
|
var next = getNextMonth(year, month, direction);
|
||||||
|
loadCalendarMonth(calendar, next.year, next.month, persistState);
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateCalendarToday(calendar) {
|
||||||
|
clearSavedCalendarMonth(calendar);
|
||||||
|
|
||||||
|
var now = new Date();
|
||||||
|
var year = parseInt(calendar.getAttribute('data-current-year') || '', 10);
|
||||||
|
var month = parseInt(calendar.getAttribute('data-current-month') || '', 10);
|
||||||
|
if (!year || !month) return;
|
||||||
|
|
||||||
|
var targetYear = now.getFullYear();
|
||||||
|
var targetMonth = now.getMonth() + 1;
|
||||||
|
var direction = ((targetYear - year) * 12) + (targetMonth - month);
|
||||||
|
if (!direction) {
|
||||||
|
syncCalendarToday(calendar);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateCalendarMonth(calendar, direction, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreCalendarMonth(calendar) {
|
||||||
|
var saved = readSavedCalendarMonth(calendar);
|
||||||
|
if (!saved) return;
|
||||||
|
|
||||||
|
var year = parseInt(calendar.getAttribute('data-current-year') || '', 10);
|
||||||
|
var month = parseInt(calendar.getAttribute('data-current-month') || '', 10);
|
||||||
|
if (saved.year === year && saved.month === month) return;
|
||||||
|
|
||||||
|
loadCalendarMonth(calendar, saved.year, saved.month, true);
|
||||||
|
}
|
||||||
|
|
||||||
function onCalendarClick(event) {
|
function onCalendarClick(event) {
|
||||||
var target = event.target;
|
var target = event.target;
|
||||||
if (!target || !target.classList || !target.classList.contains('luxtools-calendar-nav-button')) return;
|
if (!target || !target.classList) return;
|
||||||
|
if (!target.classList.contains('luxtools-calendar-nav-button') && !target.classList.contains('luxtools-calendar-today-button')) return;
|
||||||
|
|
||||||
var calendar = findCalendarRoot(target);
|
var calendar = findCalendarRoot(target);
|
||||||
if (!calendar) return;
|
if (!calendar) return;
|
||||||
|
|
||||||
var direction = parseInt(target.getAttribute('data-luxtools-dir') || '0', 10);
|
|
||||||
if (direction !== -1 && direction !== 1) return;
|
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
navigateCalendarMonth(calendar, direction);
|
|
||||||
|
if (target.getAttribute('data-luxtools-today') === '1') {
|
||||||
|
navigateCalendarToday(calendar);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction = parseInt(target.getAttribute('data-luxtools-dir') || '0', 10);
|
||||||
|
if (!direction) return;
|
||||||
|
|
||||||
|
navigateCalendarMonth(calendar, direction, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initCalendarWidgets() {
|
function initCalendarWidgets() {
|
||||||
var calendars = document.querySelectorAll('div.luxtools-calendar[data-luxtools-calendar="1"]');
|
var calendars = document.querySelectorAll('div.luxtools-calendar[data-luxtools-calendar="1"]');
|
||||||
for (var i = 0; i < calendars.length; i++) {
|
for (var i = 0; i < calendars.length; i++) {
|
||||||
syncCalendarToday(calendars[i]);
|
syncCalendarToday(calendars[i]);
|
||||||
|
restoreCalendarMonth(calendars[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('click', onCalendarClick, false);
|
document.addEventListener('click', onCalendarClick, false);
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ class ChronologicalCalendarWidget
|
|||||||
|
|
||||||
$html .= '<div class="luxtools-calendar-nav">';
|
$html .= '<div class="luxtools-calendar-nav">';
|
||||||
$html .= '<div class="luxtools-calendar-nav-prev">';
|
$html .= '<div class="luxtools-calendar-nav-prev">';
|
||||||
|
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="-12" aria-label="Previous year">⏮</button>';
|
||||||
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="-1" aria-label="Previous month">◀</button>';
|
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="-1" aria-label="Previous month">◀</button>';
|
||||||
$html .= '</div>';
|
$html .= '</div>';
|
||||||
|
|
||||||
@@ -84,6 +85,7 @@ class ChronologicalCalendarWidget
|
|||||||
|
|
||||||
$html .= '<div class="luxtools-calendar-nav-next">';
|
$html .= '<div class="luxtools-calendar-nav-next">';
|
||||||
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="1" aria-label="Next month">▶</button>';
|
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="1" aria-label="Next month">▶</button>';
|
||||||
|
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="12" aria-label="Next year">⏭</button>';
|
||||||
$html .= '</div>';
|
$html .= '</div>';
|
||||||
$html .= '</div>';
|
$html .= '</div>';
|
||||||
|
|
||||||
@@ -118,7 +120,10 @@ class ChronologicalCalendarWidget
|
|||||||
if ($cell % 7 === 6) $html .= '</tr>';
|
if ($cell % 7 === 6) $html .= '</tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$html .= '</tbody></table></div>';
|
$html .= '</tbody></table>';
|
||||||
|
$html .= '<div class="luxtools-calendar-footer">';
|
||||||
|
$html .= '<button type="button" class="luxtools-calendar-nav-button luxtools-calendar-today-button" data-luxtools-today="1" aria-label="Current month">Today</button>';
|
||||||
|
$html .= '</div></div>';
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
style.css
13
style.css
@@ -534,11 +534,15 @@ div.luxtools-calendar .luxtools-calendar-nav {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.luxtools-calendar .luxtools-calendar-nav-prev {
|
div.luxtools-calendar .luxtools-calendar-nav-prev {
|
||||||
text-align: left;
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.luxtools-calendar .luxtools-calendar-nav-next {
|
div.luxtools-calendar .luxtools-calendar-nav-next {
|
||||||
text-align: right;
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.luxtools-calendar .luxtools-calendar-nav-prev a,
|
div.luxtools-calendar .luxtools-calendar-nav-prev a,
|
||||||
@@ -562,6 +566,11 @@ div.luxtools-calendar .luxtools-calendar-nav-button:focus {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.luxtools-calendar .luxtools-calendar-footer {
|
||||||
|
margin-top: 0.35em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
div.luxtools-calendar table.luxtools-calendar-table {
|
div.luxtools-calendar table.luxtools-calendar-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
|||||||
Reference in New Issue
Block a user