Enhance calendar duty display and tooltip functionality

- Updated the calendar rendering to use a duty marker with improved accessibility attributes for duty names and titles.
- Introduced a new function `bindDutyMarkerTooltips` to display tooltips with duty names on hover over duty markers.
- Enhanced CSS styles for duty markers to improve visibility and user interaction.
- Ensured tooltips are dynamically positioned and hidden appropriately to enhance user experience.
This commit is contained in:
2026-02-17 22:47:06 +03:00
parent ef5dbca5df
commit 78a1696a69
2 changed files with 51 additions and 8 deletions

View File

@@ -204,9 +204,11 @@
const cell = document.createElement("div");
cell.className = "day" + (isOther ? " other-month" : "") + (isToday ? " today" : "") + (dayDuties.length ? " has-duty" : "") + (hasEvent ? " holiday" : "");
const dutyNamesAttr = dayDuties.length ? escapeHtml(dayDuties.map(function (x) { return x.full_name; }).join("\n")) : "";
const dutyTitleAttr = dayDuties.length ? escapeHtml(dayDuties.map(function (x) { return x.full_name; }).join(", ")) : "";
cell.innerHTML =
"<span class=\"num\">" + d.getDate() + "</span>" +
(dayDuties.length ? "<span class=\"day-duties\">" + dayDuties.map(function (x) { return escapeHtml(x.full_name); }).join(", ") + "</span>" : "") +
(dayDuties.length ? "<span class=\"duty-marker\" data-duty-names=\"" + dutyNamesAttr + "\" title=\"" + dutyTitleAttr + "\" aria-label=\"Дежурные\">Д</span>" : "") +
(hasEvent ? "<button type=\"button\" class=\"info-btn\" aria-label=\"Информация о дне\" data-summary=\"" + escapeHtml(eventSummaries.join("\n")) + "\">i</button>" : "");
calendarEl.appendChild(cell);
d.setDate(d.getDate() + 1);
@@ -214,6 +216,7 @@
monthTitleEl.textContent = MONTHS[month] + " " + year;
bindInfoButtonTooltips();
bindDutyMarkerTooltips();
}
function positionHint(hintEl, btnRect) {
@@ -298,6 +301,38 @@
}
}
function bindDutyMarkerTooltips() {
var hintEl = document.getElementById("dutyMarkerHint");
if (!hintEl) {
hintEl = document.createElement("div");
hintEl.id = "dutyMarkerHint";
hintEl.className = "calendar-event-hint";
hintEl.setAttribute("role", "tooltip");
hintEl.hidden = true;
document.body.appendChild(hintEl);
}
var hideTimeout = null;
calendarEl.querySelectorAll(".duty-marker").forEach(function (marker) {
marker.addEventListener("mouseenter", function () {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
var names = marker.getAttribute("data-duty-names") || "";
hintEl.textContent = names;
var rect = marker.getBoundingClientRect();
positionHint(hintEl, rect);
hintEl.hidden = false;
});
marker.addEventListener("mouseleave", function () {
hideTimeout = setTimeout(function () {
hintEl.hidden = true;
hideTimeout = null;
}, 150);
});
});
}
function renderDutyList(duties) {
if (duties.length === 0) {
dutyListEl.innerHTML = "<p class=\"muted\">В этом месяце дежурств нет.</p>";

View File

@@ -90,6 +90,9 @@ body {
border-radius: 8px;
font-size: 0.85rem;
background: var(--surface);
min-width: 0;
min-height: 0;
overflow: hidden;
}
.day.other-month {
@@ -161,14 +164,19 @@ body {
transform: none;
}
.day-duties {
font-size: 0.6rem;
color: var(--duty);
.duty-marker {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
margin-top: 2px;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 0.65rem;
font-weight: 700;
color: var(--duty);
background: rgba(158, 206, 106, 0.25);
border-radius: 50%;
flex-shrink: 0;
}
.duty-list {