Unify dialog infrastructure

This commit is contained in:
2026-03-20 07:56:41 +01:00
parent 96cc82db9e
commit a3f021e5e1
6 changed files with 2047 additions and 1150 deletions

1211
action.php

File diff suppressed because it is too large Load Diff

62
dialog.css Normal file
View File

@@ -0,0 +1,62 @@
/* ============================================================
* Dialog Infrastructure (shared overlay & popup)
* ============================================================ */
.luxtools-dialog-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
z-index: 10000;
justify-content: center;
align-items: center;
}
.luxtools-dialog {
background: @ini_background;
border: 1px solid @ini_border;
border-radius: 0.4em;
padding: 1.5em;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.luxtools-dialog-close {
position: absolute;
top: 0.5em;
right: 0.75em;
background: none;
border: none;
font-size: 1.5em;
cursor: pointer;
color: @ini_text;
line-height: 1;
}
.luxtools-dialog-close:hover {
opacity: 0.7;
}
.luxtools-dialog-title {
margin: 0 0 0.75em 0;
padding-right: 1.5em;
}
.luxtools-dialog-field {
margin: 0.5em 0;
}
.luxtools-dialog-actions {
margin-top: 1em;
padding-top: 0.75em;
border-top: 1px solid @ini_border;
display: flex;
flex-wrap: wrap;
gap: 0.5em;
}

95
js/dialog.js Normal file
View File

@@ -0,0 +1,95 @@
/* global window, document */
/**
* Unified Dialog Infrastructure
*
* Provides a shared modal overlay and dialog container that all other
* client-side modules (event popups, cache purge, etc.) can use.
*
* Usage:
* Luxtools.Dialog.show(htmlString) render content and open
* Luxtools.Dialog.close() close the dialog
* Luxtools.Dialog.getContainer() return the dialog DOM element
*/
(function () {
'use strict';
var Luxtools = window.Luxtools || (window.Luxtools = {});
var Dialog = (function () {
var overlay = null;
var container = null;
/**
* Lazily create the overlay + dialog container and attach them to
* the document body. Wires up click-outside-to-close and Escape.
*/
function ensureElements() {
if (overlay) return;
overlay = document.createElement('div');
overlay.className = 'luxtools-dialog-overlay';
overlay.style.display = 'none';
container = document.createElement('div');
container.className = 'luxtools-dialog';
container.setAttribute('role', 'dialog');
container.setAttribute('aria-modal', 'true');
overlay.appendChild(container);
document.body.appendChild(overlay);
// Close when clicking the backdrop (but not the dialog itself)
overlay.addEventListener('click', function (e) {
if (e.target === overlay) close();
});
// Close on Escape
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && overlay && overlay.style.display !== 'none') {
close();
}
});
}
/**
* Show the dialog with the given HTML content.
*
* The HTML should include a close button with class
* `luxtools-dialog-close` it will be wired up automatically.
*
* @param {string} html - innerHTML for the dialog container
*/
function show(html) {
ensureElements();
container.innerHTML = html;
overlay.style.display = 'flex';
// Auto-wire the close button inside the rendered content
var closeBtn = container.querySelector('.luxtools-dialog-close');
if (closeBtn) closeBtn.addEventListener('click', close);
}
/**
* Close (hide) the dialog.
*/
function close() {
if (overlay) overlay.style.display = 'none';
}
/**
* Return the dialog container element (creates it if necessary).
* Useful for querying form inputs after `show()`.
*
* @returns {HTMLElement}
*/
function getContainer() {
ensureElements();
return container;
}
return { show: show, close: close, getContainer: getContainer };
})();
Luxtools.Dialog = Dialog;
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* global window, document */ /* global window, document */
(function () { (function () {
'use strict'; "use strict";
var Luxtools = window.Luxtools || (window.Luxtools = {}); var Luxtools = window.Luxtools || (window.Luxtools = {});
var Lightbox = Luxtools.Lightbox; var Lightbox = Luxtools.Lightbox;
@@ -16,7 +16,7 @@
function findOpenElement(target) { function findOpenElement(target) {
var el = target; var el = target;
while (el && el !== document) { while (el && el !== document) {
if (el.classList && el.classList.contains('luxtools-open')) return el; if (el.classList && el.classList.contains("luxtools-open")) return el;
el = el.parentNode; el = el.parentNode;
} }
return null; return null;
@@ -25,7 +25,8 @@
function findGalleryItem(target) { function findGalleryItem(target) {
var el = target; var el = target;
while (el && el !== document) { while (el && el !== document) {
if (el.classList && el.classList.contains('luxtools-gallery-item')) return el; if (el.classList && el.classList.contains("luxtools-gallery-item"))
return el;
el = el.parentNode; el = el.parentNode;
} }
return null; return null;
@@ -35,7 +36,9 @@
// Image gallery lightbox: intercept clicks so we don't navigate away. // Image gallery lightbox: intercept clicks so we don't navigate away.
var galleryItem = findGalleryItem(event.target); var galleryItem = findGalleryItem(event.target);
if (galleryItem && Lightbox && Lightbox.open) { if (galleryItem && Lightbox && Lightbox.open) {
var gallery = galleryItem.closest ? galleryItem.closest('div.luxtools-gallery[data-luxtools-gallery="1"]') : null; var gallery = galleryItem.closest
? galleryItem.closest('div.luxtools-gallery[data-luxtools-gallery="1"]')
: null;
if (gallery) { if (gallery) {
event.preventDefault(); event.preventDefault();
Lightbox.open(gallery, galleryItem); Lightbox.open(gallery, galleryItem);
@@ -47,49 +50,56 @@
if (!el) return; if (!el) return;
// {{open>...}} renders as a link; avoid jumping to '#'. // {{open>...}} renders as a link; avoid jumping to '#'.
if (el.tagName && el.tagName.toLowerCase() === 'a') { if (el.tagName && el.tagName.toLowerCase() === "a") {
event.preventDefault(); event.preventDefault();
} }
var raw = el.getAttribute('data-path') || ''; var raw = el.getAttribute("data-path") || "";
if (!raw) return; if (!raw) return;
if (!OpenService || !OpenService.openViaService) return; if (!OpenService || !OpenService.openViaService) return;
// Prefer local client service. // Prefer local client service.
OpenService.openViaService(el, raw) OpenService.openViaService(el, raw).catch(function (err) {
.catch(function (err) { // If the browser blocks the request before it reaches localhost (mixed-content,
// If the browser blocks the request before it reaches localhost (mixed-content, // extensions, stricter CORS handling), fall back to a no-CORS GET ping.
// extensions, stricter CORS handling), fall back to a no-CORS GET ping. if (OpenService && OpenService.pingOpenViaImage) {
if (OpenService && OpenService.pingOpenViaImage) { OpenService.pingOpenViaImage(el, raw);
OpenService.pingOpenViaImage(el, raw); }
}
// Fallback to old behavior (often blocked in modern browsers). // Fallback to old behavior (often blocked in modern browsers).
var url = OpenService && OpenService.normalizeToFileUrl ? OpenService.normalizeToFileUrl(raw) : ''; var url =
if (!url) return; OpenService && OpenService.normalizeToFileUrl
console.warn('Local client service failed, falling back to file:// navigation:', err); ? OpenService.normalizeToFileUrl(raw)
: "";
if (!url) return;
console.warn(
"Local client service failed, falling back to file:// navigation:",
err,
);
try {
window.open(url, "_blank", "noopener");
} catch (e) {
try { try {
window.open(url, '_blank', 'noopener'); window.location.href = url;
} catch (e) { } catch (e2) {
try { console.error("Failed to open file URL:", e2);
window.location.href = url;
} catch (e2) {
console.error('Failed to open file URL:', e2);
}
} }
}); }
});
} }
function initChronologicalEventTimes() { function initChronologicalEventTimes() {
var nodes = document.querySelectorAll('.luxtools-event-time[data-luxtools-start]'); var nodes = document.querySelectorAll(
".luxtools-event-time[data-luxtools-start]",
);
if (!nodes || nodes.length === 0) return; if (!nodes || nodes.length === 0) return;
var formatter; var formatter;
try { try {
formatter = new Intl.DateTimeFormat('de-DE', { formatter = new Intl.DateTimeFormat("de-DE", {
hour: '2-digit', hour: "2-digit",
minute: '2-digit', minute: "2-digit",
hour12: false hour12: false,
}); });
} catch (e) { } catch (e) {
formatter = null; formatter = null;
@@ -97,7 +107,7 @@
for (var i = 0; i < nodes.length; i++) { for (var i = 0; i < nodes.length; i++) {
var node = nodes[i]; var node = nodes[i];
var raw = node.getAttribute('data-luxtools-start') || ''; var raw = node.getAttribute("data-luxtools-start") || "";
if (!raw) continue; if (!raw) continue;
var date = new Date(raw); var date = new Date(raw);
@@ -107,9 +117,9 @@
if (formatter) { if (formatter) {
label = formatter.format(date); label = formatter.format(date);
} else { } else {
var hh = String(date.getHours()).padStart(2, '0'); var hh = String(date.getHours()).padStart(2, "0");
var mm = String(date.getMinutes()).padStart(2, '0'); var mm = String(date.getMinutes()).padStart(2, "0");
label = hh + ':' + mm; label = hh + ":" + mm;
} }
node.textContent = label; node.textContent = label;
@@ -120,123 +130,185 @@
// Purge Cache Dialog // Purge Cache Dialog
// ============================================================ // ============================================================
function initPurgeCacheDialog() { function initPurgeCacheDialog() {
var $link = jQuery('a.luxtools-invalidate-cache'); document.addEventListener(
if ($link.length === 0) return; "click",
function (e) {
var link = e.target.closest
? e.target.closest("a.luxtools-invalidate-cache")
: null;
if (!link) return;
$link.on('click.luxtools', function (e) { e.preventDefault();
e.preventDefault();
var href = $link.attr('href') || ''; var href = link.getAttribute("href") || "";
var lang = (window.LANG && window.LANG.plugins && window.LANG.plugins.luxtools) var lang =
? window.LANG.plugins.luxtools window.LANG && window.LANG.plugins && window.LANG.plugins.luxtools
: {}; ? window.LANG.plugins.luxtools
: {};
var $dialog = jQuery( var html = '<div class="luxtools-dialog-content">';
'<div>' + html +=
'<p>' + (lang.cache_purge_dialog_intro || 'The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches:') + '</p>' + '<button type="button" class="luxtools-dialog-close" aria-label="Close">&times;</button>';
'<p><label><input type="checkbox" id="luxtools-purge-pagelinks"> <strong>' + (lang.cache_purge_pagelinks_label || 'Pagelinks') + '</strong>' + html +=
' &ndash; ' + (lang.cache_purge_pagelinks_desc || 'Purges the pagelink mapping cache') + '</label></p>' + '<h3 class="luxtools-dialog-title">' +
'<p><label><input type="checkbox" id="luxtools-purge-thumbs"> <strong>' + (lang.cache_purge_thumbs_label || 'Thumbnails') + '</strong>' + (lang.cache_purge_dialog_title || "Purge Cache") +
' &ndash; ' + (lang.cache_purge_thumbs_desc || 'Purges all cached image thumbnails') + '</label></p>' + "</h3>";
'</div>' html +=
); "<p>" +
(lang.cache_purge_dialog_intro ||
"The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches:") +
"</p>";
html +=
'<p><label><input type="checkbox" id="luxtools-purge-pagelinks"> <strong>' +
(lang.cache_purge_pagelinks_label || "Pagelinks") +
"</strong>";
html +=
" &ndash; " +
(lang.cache_purge_pagelinks_desc ||
"Purges the pagelink mapping cache") +
"</label></p>";
html +=
'<p><label><input type="checkbox" id="luxtools-purge-thumbs"> <strong>' +
(lang.cache_purge_thumbs_label || "Thumbnails") +
"</strong>";
html +=
" &ndash; " +
(lang.cache_purge_thumbs_desc ||
"Purges all cached image thumbnails") +
"</label></p>";
html += '<div class="luxtools-dialog-actions">';
html +=
'<button type="button" class="button luxtools-purge-confirm">' +
(lang.cache_purge_confirm || "Purge Cache") +
"</button> ";
html +=
'<button type="button" class="button luxtools-purge-cancel">' +
(lang.cache_purge_cancel || "Cancel") +
"</button>";
html += "</div>";
html += "</div>";
$dialog.dialog({ Luxtools.Dialog.show(html);
title: lang.cache_purge_dialog_title || 'Purge Cache',
modal: true, var container = Luxtools.Dialog.getContainer();
width: 420,
buttons: [ var cancelBtn = container.querySelector(".luxtools-purge-cancel");
{ if (cancelBtn) {
text: lang.cache_purge_cancel || 'Cancel', cancelBtn.addEventListener("click", function () {
click: function () { $dialog.dialog('close'); } Luxtools.Dialog.close();
}, });
{
text: lang.cache_purge_confirm || 'Purge Cache',
click: function () {
var url = href;
if ($dialog.find('#luxtools-purge-pagelinks').prop('checked')) {
url += '&luxtools_purge_pagelinks=1';
}
if ($dialog.find('#luxtools-purge-thumbs').prop('checked')) {
url += '&luxtools_purge_thumbs=1';
}
$dialog.dialog('close');
window.location.href = url;
}
}
],
close: function () {
jQuery(this).dialog('destroy').remove();
} }
});
}); var confirmBtn = container.querySelector(".luxtools-purge-confirm");
if (confirmBtn) {
confirmBtn.addEventListener("click", function () {
var url = href;
var plCheck = container.querySelector("#luxtools-purge-pagelinks");
var thCheck = container.querySelector("#luxtools-purge-thumbs");
if (plCheck && plCheck.checked) {
url += "&luxtools_purge_pagelinks=1";
}
if (thCheck && thCheck.checked) {
url += "&luxtools_purge_thumbs=1";
}
Luxtools.Dialog.close();
window.location.href = url;
});
}
},
false,
);
} }
// ============================================================ // ============================================================
// Calendar Sync Button (syntax widget) // Calendar Sync Button (syntax widget)
// ============================================================ // ============================================================
function initCalendarSyncButtons() { function initCalendarSyncButtons() {
document.addEventListener('click', function (e) { document.addEventListener(
var btn = e.target; "click",
if (!btn || !btn.classList || !btn.classList.contains('luxtools-calendar-sync-btn')) return; function (e) {
var btn = e.target;
if (
!btn ||
!btn.classList ||
!btn.classList.contains("luxtools-calendar-sync-btn")
)
return;
e.preventDefault(); e.preventDefault();
var ajaxUrl = btn.getAttribute('data-luxtools-ajax-url') || ''; var ajaxUrl = btn.getAttribute("data-luxtools-ajax-url") || "";
var sectok = btn.getAttribute('data-luxtools-sectok') || ''; var sectok = btn.getAttribute("data-luxtools-sectok") || "";
if (!ajaxUrl) return; if (!ajaxUrl) return;
var status = btn.parentNode ? btn.parentNode.querySelector('.luxtools-calendar-sync-status') : null; var status = btn.parentNode
? btn.parentNode.querySelector(".luxtools-calendar-sync-status")
: null;
btn.disabled = true; btn.disabled = true;
if (status) {
status.textContent = 'Syncing...';
status.style.color = '';
}
var xhr = new XMLHttpRequest();
xhr.open('POST', ajaxUrl, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function () {
btn.disabled = false;
try {
var r = JSON.parse(xhr.responseText);
if (status) {
status.textContent = r.message || (r.ok ? 'Done' : 'Failed');
status.style.color = r.ok ? 'green' : 'red';
}
} catch (ex) {
if (status) {
status.textContent = 'Error';
status.style.color = 'red';
}
}
};
xhr.onerror = function () {
btn.disabled = false;
if (status) { if (status) {
status.textContent = 'Network error'; status.textContent = "Syncing...";
status.style.color = 'red'; status.style.color = "";
} }
};
xhr.send('call=luxtools_calendar_sync&sectok=' + encodeURIComponent(sectok)); var xhr = new XMLHttpRequest();
}, false); xhr.open("POST", ajaxUrl, true);
xhr.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded",
);
xhr.onload = function () {
btn.disabled = false;
try {
var r = JSON.parse(xhr.responseText);
if (status) {
status.textContent = r.message || (r.ok ? "Done" : "Failed");
status.style.color = r.ok ? "green" : "red";
}
} catch (ex) {
if (status) {
status.textContent = "Error";
status.style.color = "red";
}
}
};
xhr.onerror = function () {
btn.disabled = false;
if (status) {
status.textContent = "Network error";
status.style.color = "red";
}
};
xhr.send(
"call=luxtools_calendar_sync&sectok=" + encodeURIComponent(sectok),
);
},
false,
);
} }
// ============================================================ // ============================================================
// Initialize // Initialize
// ============================================================ // ============================================================
document.addEventListener('click', onClick, false); document.addEventListener("click", onClick, false);
document.addEventListener('DOMContentLoaded', function () { document.addEventListener(
if (GalleryThumbnails && GalleryThumbnails.init) GalleryThumbnails.init(); "DOMContentLoaded",
initChronologicalEventTimes(); function () {
if (CalendarWidget && CalendarWidget.init) CalendarWidget.init(); if (GalleryThumbnails && GalleryThumbnails.init) GalleryThumbnails.init();
initPurgeCacheDialog(); initChronologicalEventTimes();
initCalendarSyncButtons(); if (CalendarWidget && CalendarWidget.init) CalendarWidget.init();
}, false); initPurgeCacheDialog();
document.addEventListener('DOMContentLoaded', function () { initCalendarSyncButtons();
if (Scratchpads && Scratchpads.init) Scratchpads.init(); },
}, false); false,
);
document.addEventListener(
"DOMContentLoaded",
function () {
if (Scratchpads && Scratchpads.init) Scratchpads.init();
},
false,
);
Luxtools.initChronologicalEventTimes = initChronologicalEventTimes; Luxtools.initChronologicalEventTimes = initChronologicalEventTimes;
})(); })();

263
style.css
View File

@@ -1,3 +1,5 @@
/* Dialog infrastructure styles are in dialog.css, loaded via CSS_STYLES_INCLUDED hook in action.php */
/* luxtools plugin styles /* luxtools plugin styles
* Keep this minimal and scoped to the plugin container. * Keep this minimal and scoped to the plugin container.
*/ */
@@ -7,7 +9,6 @@ div.luxtools-plugin table thead tr:hover > * {
background-color: @ini_background_alt !important; background-color: @ini_background_alt !important;
} }
/* "Open Location" row above the header should be visually smaller. */ /* "Open Location" row above the header should be visually smaller. */
div.luxtools-plugin table thead tr.luxtools-openlocation-row td { div.luxtools-plugin table thead tr.luxtools-openlocation-row td {
font-size: 80%; font-size: 80%;
@@ -229,8 +230,12 @@ div.plugin_luxtools_admin form.plugin_luxtools_admin_form label.block > br {
} }
div.plugin_luxtools_admin form.plugin_luxtools_admin_form textarea.edit, div.plugin_luxtools_admin form.plugin_luxtools_admin_form textarea.edit,
div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="text"].edit, div.plugin_luxtools_admin
div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="number"].edit, form.plugin_luxtools_admin_form
input[type="text"].edit,
div.plugin_luxtools_admin
form.plugin_luxtools_admin_form
input[type="number"].edit,
div.plugin_luxtools_admin form.plugin_luxtools_admin_form select { div.plugin_luxtools_admin form.plugin_luxtools_admin_form select {
flex: 1 1 auto; flex: 1 1 auto;
margin-left: auto; margin-left: auto;
@@ -249,7 +254,10 @@ div.plugin_luxtools_admin form.plugin_luxtools_admin_form select {
} }
/* Checkbox controls: keep them in the control column, left-aligned. */ /* Checkbox controls: keep them in the control column, left-aligned. */
div.plugin_luxtools_admin form.plugin_luxtools_admin_form label.block input[type="checkbox"] { div.plugin_luxtools_admin
form.plugin_luxtools_admin_form
label.block
input[type="checkbox"] {
margin-left: 0; margin-left: 0;
align-self: center; align-self: center;
} }
@@ -265,8 +273,12 @@ div.plugin_luxtools_admin form.plugin_luxtools_admin_form label.block input[type
} }
div.plugin_luxtools_admin form.plugin_luxtools_admin_form textarea.edit, div.plugin_luxtools_admin form.plugin_luxtools_admin_form textarea.edit,
div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="text"].edit, div.plugin_luxtools_admin
div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="number"].edit, form.plugin_luxtools_admin_form
input[type="text"].edit,
div.plugin_luxtools_admin
form.plugin_luxtools_admin_form
input[type="number"].edit,
div.plugin_luxtools_admin form.plugin_luxtools_admin_form select { div.plugin_luxtools_admin form.plugin_luxtools_admin_form select {
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
@@ -386,12 +398,12 @@ html.luxtools-noscroll body {
} }
.luxtools-lightbox button.luxtools-lightbox-zone-prev::after { .luxtools-lightbox button.luxtools-lightbox-zone-prev::after {
content: ''; content: "";
left: 0.35em; left: 0.35em;
} }
.luxtools-lightbox button.luxtools-lightbox-zone-next::after { .luxtools-lightbox button.luxtools-lightbox-zone-next::after {
content: ''; content: "";
right: 0.35em; right: 0.35em;
} }
@@ -416,7 +428,7 @@ html.luxtools-noscroll body {
.luxtools-lightbox button.luxtools-lightbox-close:hover, .luxtools-lightbox button.luxtools-lightbox-close:hover,
.luxtools-lightbox button.luxtools-lightbox-close:focus-visible { .luxtools-lightbox button.luxtools-lightbox-close:focus-visible {
background: rgba(0, 0, 0, 0.60); background: rgba(0, 0, 0, 0.6);
border-radius: 999px; border-radius: 999px;
} }
@@ -432,7 +444,10 @@ html.luxtools-noscroll body {
.luxtools-grouping { .luxtools-grouping {
display: grid; display: grid;
grid-template-columns: repeat(var(--luxtools-grouping-cols, 2), minmax(0, 1fr)); grid-template-columns: repeat(
var(--luxtools-grouping-cols, 2),
minmax(0, 1fr)
);
gap: var(--luxtools-grouping-gap, 0); gap: var(--luxtools-grouping-gap, 0);
justify-content: var(--luxtools-grouping-justify, start); justify-content: var(--luxtools-grouping-justify, start);
align-items: var(--luxtools-grouping-align, start); align-items: var(--luxtools-grouping-align, start);
@@ -604,7 +619,10 @@ div.luxtools-calendar td.luxtools-calendar-day-today {
} }
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a, div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a,
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a { div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -617,33 +635,77 @@ div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > sp
padding: 0.1em 0; padding: 0.1em 0;
} }
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a.wikilink2:link, div.luxtools-calendar.luxtools-calendar-size-small
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a.wikilink2:visited, td.luxtools-calendar-day
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a.wikilink2:link, > a.wikilink2:link,
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a.wikilink2:visited { div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> a.wikilink2:visited,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a.wikilink2:link,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a.wikilink2:visited {
color: @ini_missing; color: @ini_missing;
border-bottom: 0; border-bottom: 0;
} }
div.luxtools-calendar.luxtools-calendar-size-small
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:hover, td.luxtools-calendar-day
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:focus, > a:hover,
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:active, div.luxtools-calendar.luxtools-calendar-size-small
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:visited, td.luxtools-calendar-day
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:hover, > a:focus,
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:focus, div.luxtools-calendar.luxtools-calendar-size-small
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:active, td.luxtools-calendar-day
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:visited { > a:active,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> a:visited,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:hover,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:focus,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:active,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:visited {
text-decoration: none; text-decoration: none;
border-bottom: 0; border-bottom: 0;
box-shadow: none; box-shadow: none;
} }
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a, div.luxtools-calendar.luxtools-calendar-size-small
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:visited, td.luxtools-calendar-day
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:hover, > span.curid
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:focus, > a,
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:active { div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:visited,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:hover,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:focus,
div.luxtools-calendar.luxtools-calendar-size-small
td.luxtools-calendar-day
> span.curid
> a:active {
font-weight: bold; font-weight: bold;
text-decoration: underline; text-decoration: underline;
border-bottom: 0; border-bottom: 0;
@@ -654,7 +716,8 @@ div.luxtools-calendar td.luxtools-calendar-day:hover {
background-color: @ini_background_alt; background-color: @ini_background_alt;
} }
div.luxtools-calendar td.luxtools-calendar-day.luxtools-calendar-day-today:hover { div.luxtools-calendar
td.luxtools-calendar-day.luxtools-calendar-day-today:hover {
background-color: @ini_highlight; background-color: @ini_highlight;
} }
@@ -668,7 +731,9 @@ div.luxtools-calendar td.luxtools-calendar-day {
position: relative; position: relative;
} }
div.luxtools-calendar.luxtools-calendar-size-large table.luxtools-calendar-table td { div.luxtools-calendar.luxtools-calendar-size-large
table.luxtools-calendar-table
td {
text-align: left; text-align: left;
vertical-align: top; vertical-align: top;
} }
@@ -677,25 +742,36 @@ div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day {
height: 8.25em; height: 8.25em;
} }
div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day-empty { div.luxtools-calendar.luxtools-calendar-size-large
td.luxtools-calendar-day-empty {
height: 8.25em; height: 8.25em;
} }
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-frame { div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-frame {
min-height: 8.25em; min-height: 8.25em;
padding: 0.35em 0.4em 0.4em 0.4em; padding: 0.35em 0.4em 0.4em 0.4em;
box-sizing: border-box; box-sizing: border-box;
} }
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number { div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-number {
text-align: right; text-align: right;
margin-bottom: 0.25em; margin-bottom: 0.25em;
line-height: 1.1; line-height: 1.1;
} }
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > a, div.luxtools-calendar.luxtools-calendar-size-large
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > span.curid > a, .luxtools-calendar-day-number
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number span.curid > a { > a,
div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-number
> span.curid
> a,
div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-number
span.curid
> a {
display: inline; display: inline;
min-height: 0; min-height: 0;
padding: 0; padding: 0;
@@ -706,18 +782,30 @@ div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number
font-weight: bold; font-weight: bold;
} }
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > a.wikilink2:link, div.luxtools-calendar.luxtools-calendar-size-large
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > a.wikilink2:visited, .luxtools-calendar-day-number
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number span.curid > a.wikilink2:link, > a.wikilink2:link,
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number span.curid > a.wikilink2:visited { div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-number
> a.wikilink2:visited,
div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-number
span.curid
> a.wikilink2:link,
div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-number
span.curid
> a.wikilink2:visited {
color: @ini_missing; color: @ini_missing;
} }
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-events { div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-events {
overflow: hidden; overflow: hidden;
} }
div.luxtools-calendar.luxtools-calendar-size-large ul.luxtools-calendar-event-list { div.luxtools-calendar.luxtools-calendar-size-large
ul.luxtools-calendar-event-list {
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -735,17 +823,20 @@ div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event {
cursor: pointer; cursor: pointer;
} }
div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event:hover { div.luxtools-calendar.luxtools-calendar-size-large
li.luxtools-calendar-event:hover {
background-color: @ini_highlight; background-color: @ini_highlight;
} }
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-event-time { div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-event-time {
flex: 0 0 auto; flex: 0 0 auto;
font-weight: bold; font-weight: bold;
white-space: nowrap; white-space: nowrap;
} }
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-event-title { div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-event-title {
flex: 1 1 auto; flex: 1 1 auto;
min-width: 0; min-width: 0;
white-space: nowrap; white-space: nowrap;
@@ -753,7 +844,8 @@ div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-event-titl
text-overflow: ellipsis; text-overflow: ellipsis;
} }
div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event-more { div.luxtools-calendar.luxtools-calendar-size-large
li.luxtools-calendar-event-more {
border-left-color: @ini_border; border-left-color: @ini_border;
justify-content: flex-end; justify-content: flex-end;
font-style: italic; font-style: italic;
@@ -761,8 +853,10 @@ div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event-mo
@media (max-width: 800px) { @media (max-width: 800px) {
div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day, div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day,
div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day-empty, div.luxtools-calendar.luxtools-calendar-size-large
div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-frame { td.luxtools-calendar-day-empty,
div.luxtools-calendar.luxtools-calendar-size-large
.luxtools-calendar-day-frame {
height: 7em; height: 7em;
min-height: 7em; min-height: 7em;
} }
@@ -807,7 +901,6 @@ div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event-mo
clip-path: polygon(0 0, 0 100%, 100% 100%); clip-path: polygon(0 0, 0 100%, 100% 100%);
} }
/* ============================================================ /* ============================================================
* Chronological Events on Day Pages * Chronological Events on Day Pages
* ============================================================ */ * ============================================================ */
@@ -833,7 +926,6 @@ div.luxtools-chronological-events li[data-luxtools-event] .luxtools-event-time {
margin-right: 0.25em; margin-right: 0.25em;
} }
/* ============================================================ /* ============================================================
* Maintenance Tasks * Maintenance Tasks
* ============================================================ */ * ============================================================ */
@@ -868,7 +960,6 @@ button.luxtools-task-complete-btn:disabled {
cursor: wait; cursor: wait;
} }
/* ============================================================ /* ============================================================
* Maintenance Task List (syntax plugin) * Maintenance Task List (syntax plugin)
* ============================================================ */ * ============================================================ */
@@ -907,61 +998,10 @@ li.luxtools-task-overdue .luxtools-task-date {
text-decoration: line-through; text-decoration: line-through;
} }
/* ============================================================ /* ============================================================
* Event Popup * Event Popup (content-specific styles structural dialog
* styles live in dialog.css)
* ============================================================ */ * ============================================================ */
.luxtools-event-popup-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
z-index: 10000;
justify-content: center;
align-items: center;
}
.luxtools-event-popup {
background: @ini_background;
border: 1px solid @ini_border;
border-radius: 0.4em;
padding: 1.5em;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
position: relative;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.luxtools-event-popup-close {
position: absolute;
top: 0.5em;
right: 0.75em;
background: none;
border: none;
font-size: 1.5em;
cursor: pointer;
color: @ini_text;
line-height: 1;
}
.luxtools-event-popup-close:hover {
opacity: 0.7;
}
.luxtools-event-popup-title {
margin: 0 0 0.75em 0;
padding-right: 1.5em;
}
.luxtools-event-popup-field {
margin: 0.5em 0;
}
.luxtools-event-popup-description { .luxtools-event-popup-description {
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-word; word-break: break-word;
@@ -973,16 +1013,6 @@ li.luxtools-task-overdue .luxtools-task-date {
font-size: 0.9em; font-size: 0.9em;
} }
/* Event popup action buttons */
.luxtools-event-popup-actions {
margin-top: 1em;
padding-top: 0.75em;
border-top: 1px solid @ini_border;
display: flex;
flex-wrap: wrap;
gap: 0.5em;
}
.luxtools-recurrence-actions { .luxtools-recurrence-actions {
flex-direction: column; flex-direction: column;
} }
@@ -1051,7 +1081,6 @@ td.luxtools-calendar-day[data-luxtools-day] {
cursor: pointer; cursor: pointer;
} }
/* ============================================================ /* ============================================================
* Notifications (fallback) * Notifications (fallback)
* ============================================================ */ * ============================================================ */