Skip to content

Commit 1057d7c

Browse files
committed
Use viewport bounds to position calendar popups
Replace CSS row/column heuristics (tr:last-child, weekday-N selectors) with JavaScript that checks getBoundingClientRect() against the actual viewport, toggling .popup-above and .popup-right classes as needed.
1 parent 046c81b commit 1057d7c

2 files changed

Lines changed: 32 additions & 15 deletions

File tree

share/static/css/elevator/calendar.css

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -192,16 +192,21 @@ table.rt-calendar .ticket-entry span.calendar-event-detail {
192192
overflow-wrap: break-word;
193193
}
194194

195-
/* Position popup above when in bottom rows */
196-
table.rt-calendar tbody tr:last-child .ticket-entry span.calendar-event-detail,
197-
table.rt-calendar tbody tr:nth-last-child(2) .ticket-entry span.calendar-event-detail {
195+
/* Position popup above when JS determines there is insufficient space below */
196+
table.rt-calendar .ticket-entry span.calendar-event-detail.popup-above {
198197
top: auto;
199198
bottom: calc(100% - 2px);
200199
margin-top: 0;
201200
margin-bottom: 2px;
202201
}
203202

204-
/* Create a hover bridge to prevent popup from disappearing */
203+
/* Position popup right-aligned when JS determines it would overflow the right edge */
204+
table.rt-calendar .ticket-entry span.calendar-event-detail.popup-right {
205+
left: unset;
206+
right: 0;
207+
}
208+
209+
/* Create a hover bridge to prevent popup from disappearing when moving mouse into it */
205210
table.rt-calendar .ticket-entry span.calendar-event-detail::before {
206211
content: '';
207212
position: absolute;
@@ -213,8 +218,7 @@ table.rt-calendar .ticket-entry span.calendar-event-detail::before {
213218
}
214219

215220
/* Hover bridge for upward-positioned popups */
216-
table.rt-calendar tbody tr:last-child .ticket-entry span.calendar-event-detail::before,
217-
table.rt-calendar tbody tr:nth-last-child(2) .ticket-entry span.calendar-event-detail::before {
221+
table.rt-calendar .ticket-entry span.calendar-event-detail.popup-above::before {
218222
top: auto;
219223
bottom: -6px;
220224
}
@@ -248,15 +252,6 @@ table.rt-calendar .ticket-entry span.calendar-event-detail:hover {
248252
display: block !important;
249253
}
250254

251-
table.rt-calendar td.weekday-7 .ticket-entry:hover span.calendar-event-detail,
252-
table.rt-calendar td.weekday-7 .ticket-entry span.calendar-event-detail:hover,
253-
table.rt-calendar td.weekday-6 .ticket-entry:hover span.calendar-event-detail,
254-
table.rt-calendar td.weekday-6 .ticket-entry span.calendar-event-detail:hover,
255-
table.rt-calendar td.weekday-5 .ticket-entry:hover span.calendar-event-detail,
256-
table.rt-calendar td.weekday-5 .ticket-entry span.calendar-event-detail:hover {
257-
left: unset;
258-
right: 24px;
259-
}
260255

261256
.event-icon {
262257
float: left;

share/static/js/init.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
function positionCalendarPopup(entry) {
2+
const popup = entry.querySelector('.calendar-event-detail');
3+
if (!popup) return;
4+
5+
const rect = entry.getBoundingClientRect();
6+
const popupWidth = 350; // matches CSS width
7+
8+
popup.classList.toggle('popup-above', window.innerHeight - rect.bottom < 250);
9+
popup.classList.toggle('popup-right', window.innerWidth - rect.left < popupWidth);
10+
}
11+
12+
document.addEventListener('htmx:afterSwap', function(evt) {
13+
const popup = evt.detail.elt;
14+
if (popup.classList && popup.classList.contains('calendar-event-detail')) {
15+
const entry = popup.closest('.ticket-entry');
16+
if (entry) positionCalendarPopup(entry);
17+
}
18+
});
19+
120
document.addEventListener('htmx:configRequest', function(evt) {
221
for ( const param in evt.detail.parameters ) {
322
if ( evt.detail.parameters[param + 'Type'] === 'text/html' && RT.CKEditor.instances[param] ) {
@@ -876,8 +895,11 @@ document.addEventListener('htmx:load', function(evt) {
876895

877896
// Delay calendar popup requests so that mousing across the calendar
878897
// does not fire a request for every ticket the cursor passes over.
898+
// Also calculate popup position from viewport bounds rather than
899+
// relying on CSS row/column heuristics.
879900
elt.querySelectorAll('.ticket-entry[hx-trigger]').forEach(function(el) {
880901
el.addEventListener('mouseenter', function() {
902+
positionCalendarPopup(el);
881903
el._calendarHoverTimer = setTimeout(() => htmx.trigger(el, 'calendar-hover'), 300);
882904
});
883905
el.addEventListener('mouseleave', function() {

0 commit comments

Comments
 (0)