Basic more event handling for month view when there are too many events in a day given its height
This commit is contained in:
parent
25515eccb9
commit
5f4cffd5aa
@ -26,6 +26,7 @@
|
||||
/* day element */
|
||||
li {
|
||||
@apply bg-white relative px-1 pt-8 border-t-md border-gray-900 overflow-y-auto;
|
||||
transition: scale 150ms ease-in-out;
|
||||
|
||||
/* day number */
|
||||
&::before {
|
||||
@ -54,6 +55,60 @@
|
||||
@apply rounded-br-lg;
|
||||
}*/
|
||||
|
||||
/* progressive "show more" button */
|
||||
div.more-events {
|
||||
@apply absolute bottom-0 h-6 bg-inherit z-2 flex items-center;
|
||||
width: calc(100% - 0.5rem);
|
||||
}
|
||||
button.day-more {
|
||||
@apply text-xs px-1 h-4;
|
||||
}
|
||||
&[data-event-visible="0"] {
|
||||
.event:nth-child(n+1) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="1"] {
|
||||
.event:nth-child(n+2) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="2"] {
|
||||
.event:nth-child(n+3) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="3"] {
|
||||
.event:nth-child(n+4) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="4"] {
|
||||
.event:nth-child(n+5) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="5"] {
|
||||
.event:nth-child(n+6) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="6"] {
|
||||
.event:nth-child(n+7) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="7"] {
|
||||
.event:nth-child(n+8) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="8"] {
|
||||
.event:nth-child(n+9) { @apply hidden; }
|
||||
}
|
||||
&[data-event-visible="9"] {
|
||||
.event:nth-child(n+10) { @apply hidden; }
|
||||
}
|
||||
&.is-expanded {
|
||||
position: relative;
|
||||
height: min-content;
|
||||
padding-bottom: 1px;
|
||||
z-index: 3;
|
||||
border: 1.5px solid black;
|
||||
border-radius: 0.5rem;
|
||||
scale: 1.05;
|
||||
width: 120%;
|
||||
margin-left: -10%;
|
||||
|
||||
div.more-events {
|
||||
@apply relative h-8;
|
||||
}
|
||||
}
|
||||
|
||||
/* events */
|
||||
.event {
|
||||
@apply flex items-center text-xs gap-1 px-1 py-px font-medium truncate rounded-sm bg-transparent;
|
||||
|
||||
@ -9,6 +9,10 @@ const SELECTORS = {
|
||||
colorPickerColor: '[data-colorpicker-color]',
|
||||
colorPickerHex: '[data-colorpicker-hex]',
|
||||
colorPickerRandom: '[data-colorpicker-random]',
|
||||
monthDay: '.calendar.month .day',
|
||||
monthDayEvent: 'a.event',
|
||||
monthDayMore: '[data-day-more]',
|
||||
monthDayMoreWrap: '.more-events',
|
||||
};
|
||||
|
||||
/**
|
||||
@ -193,8 +197,127 @@ function initColorPickers(root = document) {
|
||||
root.querySelectorAll(SELECTORS.colorPicker).forEach(wire);
|
||||
}
|
||||
|
||||
/**
|
||||
* month view overflow handling (progressive enhancement)
|
||||
*/
|
||||
function initMonthOverflow(root = document) {
|
||||
const days = root.querySelectorAll(SELECTORS.monthDay);
|
||||
days.forEach((day) => updateMonthOverflow(day));
|
||||
}
|
||||
|
||||
function ensureDayMoreButton(dayEl) {
|
||||
let wrapper = dayEl.querySelector(SELECTORS.monthDayMoreWrap);
|
||||
if (!wrapper) {
|
||||
wrapper = document.createElement('div');
|
||||
wrapper.className = 'more-events';
|
||||
dayEl.appendChild(wrapper);
|
||||
}
|
||||
|
||||
let button = wrapper.querySelector(SELECTORS.monthDayMore);
|
||||
if (!button) {
|
||||
button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'day-more hidden';
|
||||
button.setAttribute('data-day-more', '');
|
||||
wrapper.appendChild(button);
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
function formatMoreLabel(dayEl, count) {
|
||||
const template = dayEl.getAttribute('data-more-label') || ':count more';
|
||||
return template.replace(':count', count);
|
||||
}
|
||||
|
||||
function lessLabel(dayEl) {
|
||||
return dayEl.getAttribute('data-less-label') || 'Show less';
|
||||
}
|
||||
|
||||
function updateMonthOverflow(dayEl) {
|
||||
if (!dayEl) return;
|
||||
|
||||
const events = Array.from(dayEl.querySelectorAll(SELECTORS.monthDayEvent))
|
||||
.filter((el) => !el.classList.contains('hidden'));
|
||||
|
||||
const moreButton = ensureDayMoreButton(dayEl);
|
||||
|
||||
if (!events.length) {
|
||||
moreButton.textContent = '';
|
||||
moreButton.classList.add('hidden');
|
||||
moreButton.removeAttribute('aria-expanded');
|
||||
dayEl.classList.remove('day--event-overflow');
|
||||
dayEl.setAttribute('data-event-visible', '0');
|
||||
return;
|
||||
}
|
||||
|
||||
if (dayEl.classList.contains('is-expanded')) {
|
||||
moreButton.textContent = lessLabel(dayEl);
|
||||
moreButton.classList.remove('hidden');
|
||||
moreButton.setAttribute('aria-expanded', 'true');
|
||||
dayEl.classList.remove('day--event-overflow');
|
||||
dayEl.setAttribute('data-event-visible', String(events.length));
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapper = moreButton.closest(SELECTORS.monthDayMoreWrap);
|
||||
let wrapperHeight = wrapper ? wrapper.getBoundingClientRect().height : 0;
|
||||
if (wrapperHeight === 0 && wrapper) {
|
||||
const wasHidden = moreButton.classList.contains('hidden');
|
||||
const prevVisibility = moreButton.style.visibility;
|
||||
|
||||
if (wasHidden) {
|
||||
moreButton.classList.remove('hidden');
|
||||
moreButton.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
wrapperHeight = wrapper.getBoundingClientRect().height || 0;
|
||||
|
||||
if (wasHidden) {
|
||||
moreButton.classList.add('hidden');
|
||||
moreButton.style.visibility = prevVisibility;
|
||||
}
|
||||
}
|
||||
|
||||
const prevVisibility = dayEl.style.visibility;
|
||||
dayEl.style.visibility = 'hidden';
|
||||
dayEl.removeAttribute('data-event-visible');
|
||||
dayEl.classList.remove('day--event-overflow');
|
||||
|
||||
const availableHeight = dayEl.clientHeight - wrapperHeight;
|
||||
let hiddenCount = 0;
|
||||
events.forEach((eventEl) => {
|
||||
const bottom = eventEl.offsetTop + eventEl.offsetHeight;
|
||||
if (bottom > availableHeight + 0.5) {
|
||||
hiddenCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
dayEl.style.visibility = prevVisibility;
|
||||
|
||||
const visibleCount = Math.max(0, events.length - hiddenCount);
|
||||
dayEl.setAttribute('data-event-visible', String(visibleCount));
|
||||
|
||||
if (hiddenCount > 0) {
|
||||
moreButton.textContent = formatMoreLabel(dayEl, hiddenCount);
|
||||
moreButton.classList.remove('hidden');
|
||||
moreButton.setAttribute('aria-expanded', 'false');
|
||||
dayEl.classList.add('day--event-overflow');
|
||||
} else {
|
||||
moreButton.textContent = '';
|
||||
moreButton.classList.add('hidden');
|
||||
moreButton.removeAttribute('aria-expanded');
|
||||
dayEl.classList.remove('day--event-overflow');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initialization
|
||||
*/
|
||||
|
||||
function initUI() {
|
||||
initColorPickers();
|
||||
initMonthOverflow();
|
||||
}
|
||||
|
||||
// initial bind
|
||||
@ -203,4 +326,23 @@ document.addEventListener('DOMContentLoaded', initUI);
|
||||
// rebind in htmx for swapped content
|
||||
document.addEventListener('htmx:afterSwap', (e) => {
|
||||
initColorPickers(e.target);
|
||||
initMonthOverflow(e.target);
|
||||
});
|
||||
|
||||
document.addEventListener('click', (event) => {
|
||||
const button = event.target.closest(SELECTORS.monthDayMore);
|
||||
if (!button) return;
|
||||
|
||||
const dayEl = button.closest(SELECTORS.monthDay);
|
||||
if (!dayEl) return;
|
||||
|
||||
dayEl.classList.toggle('is-expanded');
|
||||
updateMonthOverflow(dayEl);
|
||||
});
|
||||
|
||||
let monthResizeTimer;
|
||||
window.addEventListener('resize', () => {
|
||||
if (!document.querySelector(SELECTORS.monthDay)) return;
|
||||
window.clearTimeout(monthResizeTimer);
|
||||
monthResizeTimer = window.setTimeout(() => initMonthOverflow(), 100);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user