mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 10:35:30 +00:00
+collapsed view by default
This commit is contained in:
@@ -293,7 +293,26 @@ func (p *AlmanachContentsEditPage) POSTSave(engine *templating.Engine, app core.
|
|||||||
if len(updatedContents) == 0 {
|
if len(updatedContents) == 0 {
|
||||||
updatedContents = contents
|
updatedContents = contents
|
||||||
}
|
}
|
||||||
go updateContentsFTS5(app, entry, updatedContents)
|
shouldUpdateFTS := len(contentInputs) > 0 || len(newContentIDs) > 0
|
||||||
|
if shouldUpdateFTS {
|
||||||
|
touched := updatedContents
|
||||||
|
if len(contentInputs) > 0 {
|
||||||
|
touchedIDs := map[string]struct{}{}
|
||||||
|
for id := range contentInputs {
|
||||||
|
touchedIDs[id] = struct{}{}
|
||||||
|
}
|
||||||
|
filtered := make([]*dbmodels.Content, 0, len(touchedIDs))
|
||||||
|
for _, content := range updatedContents {
|
||||||
|
if _, ok := touchedIDs[content.Id]; ok {
|
||||||
|
filtered = append(filtered, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(filtered) > 0 {
|
||||||
|
touched = filtered
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go updateContentsFTS5(app, entry, touched)
|
||||||
|
}
|
||||||
|
|
||||||
redirect := fmt.Sprintf("/almanach/%s/contents/edit?saved_message=%s", id, url.QueryEscape("Änderungen gespeichert."))
|
redirect := fmt.Sprintf("/almanach/%s/contents/edit?saved_message=%s", id, url.QueryEscape("Änderungen gespeichert."))
|
||||||
if isHTMX {
|
if isHTMX {
|
||||||
|
|||||||
@@ -127,9 +127,13 @@
|
|||||||
<i class="ri-loader-4-line spinning mr-2"></i>
|
<i class="ri-loader-4-line spinning mr-2"></i>
|
||||||
Reihenfolge wird gespeichert
|
Reihenfolge wird gespeichert
|
||||||
</div>
|
</div>
|
||||||
|
<div id="contents-htmx-indicator" class="fixed right-6 bottom-16 z-50 hidden rounded-full bg-stone-200 px-3 py-2 text-sm text-stone-700 shadow-md">
|
||||||
|
<i class="ri-loader-4-line spinning mr-2"></i>
|
||||||
|
<span data-role="contents-htmx-label">Eintrag wird geladen</span>
|
||||||
|
</div>
|
||||||
<input type="hidden" name="csrf_token" value="{{ $model.csrf_token }}" data-role="csrf-token" />
|
<input type="hidden" name="csrf_token" value="{{ $model.csrf_token }}" data-role="csrf-token" />
|
||||||
<div class="flex items-center justify-end px-4">
|
<div class="flex items-center justify-end px-4">
|
||||||
<button type="button" class="resetbutton w-auto px-3 py-2 text-sm flex items-center gap-2" data-role="contents-collapse-all" data-state="expanded">
|
<button type="button" class="resetbutton w-auto px-3 py-2 flex items-center gap-2" data-role="contents-collapse-all" data-state="expanded">
|
||||||
<i class="ri-arrow-up-s-line" data-role="contents-collapse-all-icon"></i>
|
<i class="ri-arrow-up-s-line" data-role="contents-collapse-all-icon"></i>
|
||||||
<span data-role="contents-collapse-all-label">Alle Eintraege einklappen</span>
|
<span data-role="contents-collapse-all-label">Alle Eintraege einklappen</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -217,6 +221,7 @@
|
|||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.type = "button";
|
button.type = "button";
|
||||||
button.className = "absolute left-1/2 top-1/2 z-[10000] -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-150 rounded-full border border-slate-300 bg-stone-100 text-slate-700 px-3 py-2 text-base shadow-sm";
|
button.className = "absolute left-1/2 top-1/2 z-[10000] -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity duration-150 rounded-full border border-slate-300 bg-stone-100 text-slate-700 px-3 py-2 text-base shadow-sm";
|
||||||
|
button.dataset.loadingLabel = "Eintrag wird geladen";
|
||||||
button.setAttribute("aria-label", "Beitrag einfügen");
|
button.setAttribute("aria-label", "Beitrag einfügen");
|
||||||
button.setAttribute("hx-post", insertEndpoint);
|
button.setAttribute("hx-post", insertEndpoint);
|
||||||
button.setAttribute("hx-target", "closest [data-role='content-gap']");
|
button.setAttribute("hx-target", "closest [data-role='content-gap']");
|
||||||
@@ -272,6 +277,7 @@
|
|||||||
const deleteEndpoint = window.location.pathname.replace(/\/contents\/edit\/?$/, "/contents/delete");
|
const deleteEndpoint = window.location.pathname.replace(/\/contents\/edit\/?$/, "/contents/delete");
|
||||||
const csrfToken = document.querySelector("input[name='csrf_token']")?.value || "";
|
const csrfToken = document.querySelector("input[name='csrf_token']")?.value || "";
|
||||||
const syncIndicator = document.querySelector("#contents-sync-indicator");
|
const syncIndicator = document.querySelector("#contents-sync-indicator");
|
||||||
|
const htmxIndicator = document.querySelector("#contents-htmx-indicator");
|
||||||
let orderSyncTimer = null;
|
let orderSyncTimer = null;
|
||||||
let isOrderSyncing = false;
|
let isOrderSyncing = false;
|
||||||
let pendingOrderSync = false;
|
let pendingOrderSync = false;
|
||||||
@@ -282,6 +288,21 @@
|
|||||||
}
|
}
|
||||||
syncIndicator.classList.toggle("hidden", !active);
|
syncIndicator.classList.toggle("hidden", !active);
|
||||||
};
|
};
|
||||||
|
const setHtmxIndicator = (active) => {
|
||||||
|
if (!htmxIndicator) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
htmxIndicator.classList.toggle("hidden", !active);
|
||||||
|
};
|
||||||
|
const setHtmxIndicatorLabel = (label) => {
|
||||||
|
if (!htmxIndicator || !label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const labelEl = htmxIndicator.querySelector("[data-role='contents-htmx-label']");
|
||||||
|
if (labelEl) {
|
||||||
|
labelEl.textContent = label;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const performOrderSync = () => {
|
const performOrderSync = () => {
|
||||||
if (!list || !orderEndpoint || !csrfToken || isOrderSyncing) {
|
if (!list || !orderEndpoint || !csrfToken || isOrderSyncing) {
|
||||||
@@ -405,6 +426,7 @@
|
|||||||
const collapseIcon = item.querySelector("[data-role='content-collapse-icon']");
|
const collapseIcon = item.querySelector("[data-role='content-collapse-icon']");
|
||||||
const collapsedSummary = item.querySelector("[data-role='content-collapsed-summary']");
|
const collapsedSummary = item.querySelector("[data-role='content-collapsed-summary']");
|
||||||
const viewBody = item.querySelector("[data-role='content-view-body']");
|
const viewBody = item.querySelector("[data-role='content-view-body']");
|
||||||
|
const header = item.querySelector("[data-content-header='true']");
|
||||||
|
|
||||||
const setCollapsed = (collapsed) => {
|
const setCollapsed = (collapsed) => {
|
||||||
if (!viewBody || !collapsedSummary) {
|
if (!viewBody || !collapsedSummary) {
|
||||||
@@ -414,6 +436,10 @@
|
|||||||
item.classList.toggle("data-collapsed", collapsed);
|
item.classList.toggle("data-collapsed", collapsed);
|
||||||
viewBody.classList.toggle("hidden", collapsed);
|
viewBody.classList.toggle("hidden", collapsed);
|
||||||
collapsedSummary.classList.toggle("hidden", !collapsed);
|
collapsedSummary.classList.toggle("hidden", !collapsed);
|
||||||
|
if (header) {
|
||||||
|
header.classList.toggle("bg-stone-100", collapsed);
|
||||||
|
header.classList.toggle("bg-stone-200", !collapsed);
|
||||||
|
}
|
||||||
if (collapseButton) {
|
if (collapseButton) {
|
||||||
collapseButton.setAttribute("aria-expanded", (!collapsed).toString());
|
collapseButton.setAttribute("aria-expanded", (!collapsed).toString());
|
||||||
collapseButton.setAttribute("aria-label", collapsed ? "Beitrag ausklappen" : "Beitrag einklappen");
|
collapseButton.setAttribute("aria-label", collapsed ? "Beitrag ausklappen" : "Beitrag einklappen");
|
||||||
@@ -581,6 +607,7 @@
|
|||||||
const collapsedSummary = item.querySelector("[data-role='content-collapsed-summary']");
|
const collapsedSummary = item.querySelector("[data-role='content-collapsed-summary']");
|
||||||
const collapseButton = item.querySelector("[data-role='content-collapse-toggle']");
|
const collapseButton = item.querySelector("[data-role='content-collapse-toggle']");
|
||||||
const collapseIcon = item.querySelector("[data-role='content-collapse-icon']");
|
const collapseIcon = item.querySelector("[data-role='content-collapse-icon']");
|
||||||
|
const header = item.querySelector("[data-content-header='true']");
|
||||||
if (!viewBody || !collapsedSummary) {
|
if (!viewBody || !collapsedSummary) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -588,6 +615,10 @@
|
|||||||
item.classList.toggle("data-collapsed", collapsed);
|
item.classList.toggle("data-collapsed", collapsed);
|
||||||
viewBody.classList.toggle("hidden", collapsed);
|
viewBody.classList.toggle("hidden", collapsed);
|
||||||
collapsedSummary.classList.toggle("hidden", !collapsed);
|
collapsedSummary.classList.toggle("hidden", !collapsed);
|
||||||
|
if (header) {
|
||||||
|
header.classList.toggle("bg-stone-100", collapsed);
|
||||||
|
header.classList.toggle("bg-stone-200", !collapsed);
|
||||||
|
}
|
||||||
if (collapseButton) {
|
if (collapseButton) {
|
||||||
collapseButton.setAttribute("aria-expanded", (!collapsed).toString());
|
collapseButton.setAttribute("aria-expanded", (!collapsed).toString());
|
||||||
collapseButton.setAttribute("aria-label", collapsed ? "Beitrag ausklappen" : "Beitrag einklappen");
|
collapseButton.setAttribute("aria-label", collapsed ? "Beitrag ausklappen" : "Beitrag einklappen");
|
||||||
@@ -618,6 +649,10 @@
|
|||||||
renderInsertGaps();
|
renderInsertGaps();
|
||||||
syncEditSpacing();
|
syncEditSpacing();
|
||||||
showEditButtonsIfIdle();
|
showEditButtonsIfIdle();
|
||||||
|
if (!list.dataset.collapseInit) {
|
||||||
|
list.dataset.collapseInit = "true";
|
||||||
|
setAllCollapsed(true);
|
||||||
|
}
|
||||||
updateCollapseAllLabel();
|
updateCollapseAllLabel();
|
||||||
|
|
||||||
if (list.dataset.pageInit !== "true") {
|
if (list.dataset.pageInit !== "true") {
|
||||||
@@ -750,7 +785,46 @@
|
|||||||
setupEditFormGlobal(item);
|
setupEditFormGlobal(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
target.querySelectorAll("multi-select-simple[data-initial-options], multi-select-simple[data-initial-values]").forEach((el) => {
|
||||||
|
applyMultiSelectInit(el);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
initWhenReady();
|
initWhenReady();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:beforeRequest", (event) => {
|
||||||
|
const elt = event.detail?.elt;
|
||||||
|
if (!elt || !list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (elt.closest("[data-role='contents-list']")) {
|
||||||
|
const label = elt.getAttribute("data-loading-label");
|
||||||
|
if (label) {
|
||||||
|
setHtmxIndicatorLabel(label);
|
||||||
|
} else {
|
||||||
|
setHtmxIndicatorLabel("Eintrag wird geladen");
|
||||||
|
}
|
||||||
|
setHtmxIndicator(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:afterRequest", (event) => {
|
||||||
|
const elt = event.detail?.elt;
|
||||||
|
if (!elt || !list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (elt.closest("[data-role='contents-list']")) {
|
||||||
|
setHtmxIndicator(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:responseError", (event) => {
|
||||||
|
const elt = event.detail?.elt;
|
||||||
|
if (!elt || !list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (elt.closest("[data-role='contents-list']")) {
|
||||||
|
setHtmxIndicator(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
class="w-full dbform"
|
class="w-full dbform"
|
||||||
method="POST"
|
method="POST"
|
||||||
|
data-loading-label="Eintrag wird gespeichert"
|
||||||
hx-boost="false"
|
hx-boost="false"
|
||||||
hx-post="/almanach/{{ $entry.MusenalmID }}/contents/edit"
|
hx-post="/almanach/{{ $entry.MusenalmID }}/contents/edit"
|
||||||
hx-target="closest [data-role='content-item']"
|
hx-target="closest [data-role='content-item']"
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- $editContainerID := printf "content-%s-edit-container" $contentID -}}
|
{{- $editContainerID := printf "content-%s-edit-container" $contentID -}}
|
||||||
|
|
||||||
<div data-role="content-item" class="relative {{ if $isNew }}data-new-content{{ end }} {{ if $openEdit }}data-editing{{ end }}" data-open-edit="{{ if $openEdit }}true{{ end }}" data-content-temp="{{ if $isNew }}true{{ end }}" data-content-id="{{ $contentID }}">
|
<div data-role="content-item" class="relative {{ if $isNew }}data-new-content{{ end }} {{ if $openEdit }}data-editing{{ end }}" data-open-edit="{{ if $openEdit }}true{{ end }}" data-content-temp="{{ if $isNew }}true{{ end }}" data-content-id="{{ $contentID }}" data-collapsed="true">
|
||||||
<div data-role="content-view" class="{{ if $openEdit }}hidden{{ end }} mt-2">
|
<div data-role="content-view" class="{{ if $openEdit }}hidden{{ end }} mt-2">
|
||||||
<div class="border border-slate-200 bg-stone-100 rounded-xs overflow-hidden">
|
<div class="border border-slate-200 bg-stone-100 rounded-xs overflow-hidden">
|
||||||
<div class="flex items-center justify-between gap-4 border-b border-slate-200 bg-stone-200 px-3 py-2 cursor-grab" data-role="content-drag-handle" draggable="true" aria-label="Beitrag verschieben">
|
<div class="flex items-center justify-between gap-4 border-b border-slate-200 bg-stone-100 px-3 py-2 cursor-grab" data-role="content-drag-handle" data-content-header="true" draggable="true" aria-label="Beitrag verschieben">
|
||||||
<div class="flex items-center gap-2 text-sm font-bold text-gray-800">
|
<div class="flex items-center gap-2 text-sm font-bold text-gray-800">
|
||||||
<button type="button" class="text-slate-600 rounded-xs px-2 py-1 text-sm transition-colors hover:bg-stone-100 {{ if $isNew }}hidden{{ end }}" data-role="content-collapse-toggle" aria-label="Beitrag einklappen" aria-expanded="true">
|
<button type="button" class="text-slate-600 rounded-xs px-2 py-1 text-sm transition-colors hover:bg-stone-100 {{ if $isNew }}hidden{{ end }}" data-role="content-collapse-toggle" aria-label="Beitrag einklappen" aria-expanded="true">
|
||||||
<i class="ri-arrow-up-s-line" data-role="content-collapse-icon"></i>
|
<i class="ri-arrow-up-s-line" data-role="content-collapse-icon"></i>
|
||||||
@@ -61,6 +61,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="resetbutton w-32 flex items-center gap-2 justify-center"
|
class="resetbutton w-32 flex items-center gap-2 justify-center"
|
||||||
data-role="content-edit-button"
|
data-role="content-edit-button"
|
||||||
|
data-loading-label="Eintrag wird geladen"
|
||||||
hx-boost="false"
|
hx-boost="false"
|
||||||
hx-get="/almanach/{{ $entry.MusenalmID }}/contents/edit/form?content_id={{ $contentID }}"
|
hx-get="/almanach/{{ $entry.MusenalmID }}/contents/edit/form?content_id={{ $contentID }}"
|
||||||
hx-target="#{{ $editContainerID }}"
|
hx-target="#{{ $editContainerID }}"
|
||||||
|
|||||||
Reference in New Issue
Block a user