mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 02:25:30 +00:00
BUGFIX: stresstest u select-vals
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -9,6 +9,7 @@
|
||||
</head>
|
||||
|
||||
<body class="w-full min-h-full" id="body" hx-ext="response-targets" hx-boost="true">
|
||||
{{ template "_global_notice" . }}
|
||||
<div class="pb-12">
|
||||
{{ block "body" . }}
|
||||
<!-- Default app body... -->
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<body id="body" class="w-full min-h-full" hx-ext="response-targets" hx-boost="true">
|
||||
<div class="flex flex-col min-h-screen w-full">
|
||||
{{ template "_global_notice" . }}
|
||||
<header class="container-normal bg-slate-100 " id="header">
|
||||
{{ template "_menu" . }}
|
||||
</header>
|
||||
|
||||
12
views/layouts/components/_global_notice.gohtml
Normal file
12
views/layouts/components/_global_notice.gohtml
Normal file
@@ -0,0 +1,12 @@
|
||||
<div
|
||||
id="global-notice"
|
||||
class="global-notice hidden"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
data-state="">
|
||||
<div class="global-notice-inner">
|
||||
<i class="ri-loader-4-line spinning" aria-hidden="true"></i>
|
||||
<span data-role="global-notice-text">Laden…</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
<body id="body" class="w-full text-lg" hx-ext="response-targets" hx-boost="true">
|
||||
<div class="flex flex-col min-h-screen w-full">
|
||||
{{ template "_global_notice" . }}
|
||||
<header class="container-normal pb-0" id="header">
|
||||
{{ block "_menu" . }}
|
||||
<!-- Default app menu... -->
|
||||
|
||||
@@ -495,10 +495,12 @@ type AlmanachResult struct {
|
||||
</div>
|
||||
</div>
|
||||
<div id="series-section" class="rel-section-container">
|
||||
{{- $hasNonPreferredSeries := false -}}
|
||||
{{- if $model.result.Series -}}
|
||||
{{- range $i, $s := $model.result.Series -}}
|
||||
{{- $rel := index $model.result.EntriesSeries $s.Id -}}
|
||||
{{- if and $rel (ne $rel.Type "Bevorzugter Reihentitel") -}}
|
||||
{{- $hasNonPreferredSeries = true -}}
|
||||
<div data-rel-row class="entries-series-row rel-row">
|
||||
<div class="rel-grid">
|
||||
<div data-rel-strike class="relation-strike rel-name-col">
|
||||
@@ -542,7 +544,8 @@ type AlmanachResult struct {
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- end -}}
|
||||
{{- if not $hasNonPreferredSeries -}}
|
||||
<div class="rel-empty-text">Keine Reihen verknüpft.</div>
|
||||
{{- end -}}
|
||||
</div>
|
||||
@@ -716,7 +719,7 @@ type AlmanachResult struct {
|
||||
<label for="entries_agents_new_type" class="sr-only">Beziehung</label>
|
||||
<select data-role="relation-type-select" name="entries_agents_new_type" id="entries_agents_new_type" autocomplete="off" class="inputselect font-bold w-full">
|
||||
{{- range $t := $model.agent_relations -}}
|
||||
<option value="{{- $t -}}">{{- $t -}}</option>
|
||||
<option value="{{- $t -}}" {{ if eq $t "Herausgeber:in" }}selected{{ end }}>{{- $t -}}</option>
|
||||
{{- end -}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -260,7 +260,7 @@
|
||||
<div data-rel-strike class="relation-strike">
|
||||
<select name="{{ $agentsPrefix }}_new_type" class="inputselect font-bold w-full">
|
||||
{{- range $t := $agentRelations -}}
|
||||
<option value="{{- $t -}}" {{ if eq $r.Type $t }}selected{{ end }}>{{- $t -}}</option>
|
||||
<option value="{{- $t -}}" {{ if or (eq $r.Type $t) (and (eq $r.Type "") (eq $t "Autor:in")) }}selected{{ end }}>{{- $t -}}</option>
|
||||
{{- end -}}
|
||||
</select>
|
||||
</div>
|
||||
@@ -300,7 +300,7 @@
|
||||
<label for="{{ $agentsPrefix }}_new_type" class="sr-only">Beziehung</label>
|
||||
<select data-role="relation-type-select" name="{{ $agentsPrefix }}_new_type" id="{{ $agentsPrefix }}_new_type" autocomplete="off" class="inputselect font-bold w-full">
|
||||
{{- range $t := $agentRelations -}}
|
||||
<option value="{{- $t -}}">{{- $t -}}</option>
|
||||
<option value="{{- $t -}}" {{ if eq $t "Autor:in" }}selected{{ end }}>{{- $t -}}</option>
|
||||
{{- end -}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -324,6 +324,168 @@ function DisconnectNoEnters(textarea) {
|
||||
textarea.removeEventListener("keydown", NoEnters);
|
||||
}
|
||||
|
||||
function InitGlobalHtmxNotice() {
|
||||
if (!window.htmx) {
|
||||
return;
|
||||
}
|
||||
const ensureNotice = () => {
|
||||
let noticeEl = document.getElementById("global-notice");
|
||||
if (!noticeEl) {
|
||||
noticeEl = document.createElement("div");
|
||||
noticeEl.id = "global-notice";
|
||||
noticeEl.className = "global-notice hidden";
|
||||
noticeEl.setAttribute("role", "status");
|
||||
noticeEl.setAttribute("aria-live", "polite");
|
||||
noticeEl.setAttribute("aria-atomic", "true");
|
||||
noticeEl.dataset.state = "";
|
||||
noticeEl.innerHTML = `
|
||||
<div class="global-notice-inner">
|
||||
<i class="ri-loader-4-line spinning" aria-hidden="true"></i>
|
||||
<span data-role="global-notice-text">Laden…</span>
|
||||
</div>
|
||||
`;
|
||||
document.body?.appendChild(noticeEl);
|
||||
}
|
||||
return noticeEl;
|
||||
};
|
||||
let notice = ensureNotice();
|
||||
let textEl = notice ? notice.querySelector("[data-role='global-notice-text']") : null;
|
||||
let pending = 0;
|
||||
let errorTimeout = null;
|
||||
|
||||
const setNoticeState = (state, message) => {
|
||||
notice = ensureNotice();
|
||||
if (notice && !textEl) {
|
||||
textEl = notice.querySelector("[data-role='global-notice-text']");
|
||||
}
|
||||
if (textEl && message) {
|
||||
textEl.textContent = message;
|
||||
}
|
||||
if (notice && state) {
|
||||
notice.dataset.state = state;
|
||||
} else if (notice) {
|
||||
notice.removeAttribute("data-state");
|
||||
}
|
||||
};
|
||||
|
||||
const showNotice = (state, message) => {
|
||||
notice = ensureNotice();
|
||||
if (!notice) {
|
||||
return;
|
||||
}
|
||||
setNoticeState(state, message);
|
||||
notice.classList.remove("hidden");
|
||||
};
|
||||
|
||||
const hideNotice = () => {
|
||||
notice = ensureNotice();
|
||||
if (!notice) {
|
||||
return;
|
||||
}
|
||||
notice.classList.add("hidden");
|
||||
notice.removeAttribute("data-state");
|
||||
};
|
||||
|
||||
const setBodyBusy = (busy) => {
|
||||
const root = document.documentElement;
|
||||
if (busy) {
|
||||
if (root) {
|
||||
root.dataset.htmxBusy = "true";
|
||||
}
|
||||
if (document.body) {
|
||||
document.body.dataset.htmxBusy = "true";
|
||||
}
|
||||
} else {
|
||||
if (root) {
|
||||
delete root.dataset.htmxBusy;
|
||||
}
|
||||
if (document.body) {
|
||||
delete document.body.dataset.htmxBusy;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const markElementBusy = (element, busy) => {
|
||||
if (!element || !(element instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
if (busy) {
|
||||
element.dataset.htmxBusy = "true";
|
||||
element.setAttribute("aria-busy", "true");
|
||||
if (element instanceof HTMLButtonElement && !element.disabled) {
|
||||
element.dataset.htmxDisabled = "true";
|
||||
element.disabled = true;
|
||||
}
|
||||
} else if (element.dataset.htmxBusy === "true") {
|
||||
delete element.dataset.htmxBusy;
|
||||
element.removeAttribute("aria-busy");
|
||||
if (element instanceof HTMLButtonElement && element.dataset.htmxDisabled === "true") {
|
||||
element.disabled = false;
|
||||
delete element.dataset.htmxDisabled;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const clearErrorTimeout = () => {
|
||||
if (errorTimeout) {
|
||||
clearTimeout(errorTimeout);
|
||||
errorTimeout = null;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("htmx:beforeRequest", (event) => {
|
||||
pending += 1;
|
||||
clearErrorTimeout();
|
||||
setBodyBusy(true);
|
||||
showNotice("loading", "Laden...");
|
||||
markElementBusy(event.detail?.elt, true);
|
||||
});
|
||||
|
||||
document.addEventListener("htmx:afterRequest", (event) => {
|
||||
markElementBusy(event.detail?.elt, false);
|
||||
pending = Math.max(0, pending - 1);
|
||||
if (pending === 0) {
|
||||
setBodyBusy(false);
|
||||
if (notice.dataset.state !== "error") {
|
||||
hideNotice();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("htmx:responseError", () => {
|
||||
setBodyBusy(false);
|
||||
showNotice("error", "Laden fehlgeschlagen.");
|
||||
clearErrorTimeout();
|
||||
errorTimeout = setTimeout(() => {
|
||||
if (pending === 0) {
|
||||
hideNotice();
|
||||
} else {
|
||||
showNotice("loading", "Laden...");
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
document.addEventListener("htmx:sendError", () => {
|
||||
setBodyBusy(false);
|
||||
showNotice("error", "Verbindung fehlgeschlagen.");
|
||||
clearErrorTimeout();
|
||||
errorTimeout = setTimeout(() => {
|
||||
if (pending === 0) {
|
||||
hideNotice();
|
||||
} else {
|
||||
showNotice("loading", "Laden...");
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
document.addEventListener("htmx:afterSwap", () => {
|
||||
notice = ensureNotice();
|
||||
if (notice && !textEl) {
|
||||
textEl = notice.querySelector("[data-role='global-notice-text']");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function MutateObserve(mutations, observer) {
|
||||
const needsJSResize = !supportsFieldSizing();
|
||||
|
||||
@@ -476,5 +638,6 @@ window.PathPlusQuery = PathPlusQuery;
|
||||
window.HookupRBChange = HookupRBChange;
|
||||
window.FormLoad = FormLoad;
|
||||
window.TextareaAutoResize = TextareaAutoResize;
|
||||
InitGlobalHtmxNotice();
|
||||
|
||||
export { FilterList, ScrollButton, AbbreviationTooltips, MultiSelectSimple, MultiSelectRole, ToolTip, PopupImage, TabList, FilterPill, ImageReel, IntLink, ItemsEditor, SingleSelectRemote, AlmanachEditPage, RelationsEditor, EditPage, FabMenu, LookupField };
|
||||
|
||||
@@ -624,6 +624,30 @@
|
||||
@apply !inline-block;
|
||||
}
|
||||
|
||||
.global-notice {
|
||||
@apply fixed right-6 bottom-6 z-50 hidden;
|
||||
}
|
||||
|
||||
.global-notice-inner {
|
||||
@apply flex items-center gap-2 rounded-md border border-slate-200 bg-white/95 px-3 py-2 text-sm font-semibold text-gray-700 shadow-lg backdrop-blur;
|
||||
}
|
||||
|
||||
.global-notice[data-state="error"] .global-notice-inner {
|
||||
@apply border-red-200 bg-red-50 text-red-800;
|
||||
}
|
||||
|
||||
html[data-htmx-busy="true"] .global-notice,
|
||||
body[data-htmx-busy="true"] .global-notice {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
html[data-htmx-busy="true"],
|
||||
body[data-htmx-busy="true"],
|
||||
[data-htmx-busy="true"] {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.tab-list-head[aria-pressed="true"] {
|
||||
@apply !text-slate-900 bg-stone-50;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user