mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 02:25:30 +00:00
+ some minor frontend annoyanceS
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -142,15 +142,15 @@
|
|||||||
class="min-w-[10rem] w-full max-w-none flex-1 border border-slate-300 rounded-xs bg-white px-3 py-1.5 text-base leading-6 text-gray-800 focus:outline-none focus:ring-2 focus:ring-slate-400/30"
|
class="min-w-[10rem] w-full max-w-none flex-1 border border-slate-300 rounded-xs bg-white px-3 py-1.5 text-base leading-6 text-gray-800 focus:outline-none focus:ring-2 focus:ring-slate-400/30"
|
||||||
placeholder="z. B. 12 Bl., 3 Taf."
|
placeholder="z. B. 12 Bl., 3 Taf."
|
||||||
value="{{- $model.result.Entry.Extent -}}" />
|
value="{{- $model.result.Entry.Extent -}}" />
|
||||||
<button type="submit" class="rounded-xs border border-slate-300 bg-stone-100 px-3 py-1.5 text-base font-semibold text-gray-700 hover:bg-stone-200">
|
<button type="submit" class="rounded-xs border border-slate-300 bg-stone-50 px-3 py-1.5 text-base font-semibold text-gray-700 shadow-sm hover:bg-stone-100">
|
||||||
Speichern
|
Struktur speichern
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 text-xl font-bold text-gray-800 mt-3">Inhalt</div>
|
<div class="px-4 text-xl font-bold text-gray-800 mt-3">Inhalt</div>
|
||||||
<div class="px-4 py-2 flex flex-wrap items-center gap-3">
|
<div class="px-4 py-2 flex flex-wrap items-center gap-3">
|
||||||
<label for="content-filter" class="text-sm font-bold text-gray-700 whitespace-nowrap">
|
<label for="content-filter" class="text-base font-bold text-gray-700 whitespace-nowrap">
|
||||||
<i class="ri-search-line"></i> Filtern
|
<i class="ri-search-line"></i> Filter
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="content-filter"
|
id="content-filter"
|
||||||
@@ -185,33 +185,37 @@
|
|||||||
<span>Neuer Beitrag</span>
|
<span>Neuer Beitrag</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-0 mt-2"
|
<div class="mx-4 mt-2 rounded-md border border-slate-200 bg-white flex flex-col overflow-hidden" data-role="contents-panel">
|
||||||
data-role="contents-list"
|
<div class="flex-1 min-h-0 overflow-y-auto">
|
||||||
data-order-endpoint="/almanach/{{ $model.result.Entry.MusenalmID }}/contents/edit">
|
<div class="flex flex-col gap-0"
|
||||||
{{- range $_, $content := $model.result.Contents -}}
|
data-role="contents-list"
|
||||||
{{- template "_content_item" (Dict
|
data-order-endpoint="/almanach/{{ $model.result.Entry.MusenalmID }}/contents/edit">
|
||||||
"content" $content
|
{{- range $_, $content := $model.result.Contents -}}
|
||||||
"entry" $model.result.Entry
|
{{- template "_content_item" (Dict
|
||||||
"csrf_token" $model.csrf_token
|
"content" $content
|
||||||
"content_types" $model.content_types
|
"entry" $model.result.Entry
|
||||||
"musenalm_types" $model.musenalm_types
|
"csrf_token" $model.csrf_token
|
||||||
"pagination_values" $model.pagination_values
|
"content_types" $model.content_types
|
||||||
"agents" $model.result.Agents
|
"musenalm_types" $model.musenalm_types
|
||||||
"content_agents" (index $model.result.ContentsAgents $content.Id)
|
"pagination_values" $model.pagination_values
|
||||||
"agent_relations" $model.agent_relations
|
"agents" $model.result.Agents
|
||||||
"open_edit" false
|
"content_agents" (index $model.result.ContentsAgents $content.Id)
|
||||||
"is_new" false
|
"agent_relations" $model.agent_relations
|
||||||
) -}}
|
"open_edit" false
|
||||||
{{- end -}}
|
"is_new" false
|
||||||
</div>
|
) -}}
|
||||||
<div class="px-4 py-2 mt-2 flex items-center">
|
{{- end -}}
|
||||||
<button
|
</div>
|
||||||
type="button"
|
</div>
|
||||||
class="content-action-button"
|
<div class="px-3 py-2 border-t border-slate-200 bg-stone-50 flex items-center">
|
||||||
onclick="window.location.assign('/almanach/{{ $model.result.Entry.MusenalmID }}/contents/new')">
|
<button
|
||||||
<i class="ri-add-line"></i>
|
type="button"
|
||||||
<span>Neuer Beitrag</span>
|
class="content-action-button"
|
||||||
</button>
|
onclick="window.location.assign('/almanach/{{ $model.result.Entry.MusenalmID }}/contents/new')">
|
||||||
|
<i class="ri-add-line"></i>
|
||||||
|
<span>Neuer Beitrag</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -290,6 +294,8 @@
|
|||||||
const list = document.querySelector("[data-role='contents-list']");
|
const list = document.querySelector("[data-role='contents-list']");
|
||||||
|
|
||||||
if (!filterInput || !list) return;
|
if (!filterInput || !list) return;
|
||||||
|
if (filterInput.dataset.init === "true") return;
|
||||||
|
filterInput.dataset.init = "true";
|
||||||
|
|
||||||
// Populate type select from search index
|
// Populate type select from search index
|
||||||
if (filterType && window.contentsSearchIndex) {
|
if (filterType && window.contentsSearchIndex) {
|
||||||
@@ -574,6 +580,11 @@
|
|||||||
let orderSyncTimer = null;
|
let orderSyncTimer = null;
|
||||||
let isOrderSyncing = false;
|
let isOrderSyncing = false;
|
||||||
let pendingOrderSync = false;
|
let pendingOrderSync = false;
|
||||||
|
if (list.__contentsDragController) {
|
||||||
|
list.__contentsDragController.abort();
|
||||||
|
}
|
||||||
|
list.__contentsDragController = new AbortController();
|
||||||
|
const { signal: dragSignal } = list.__contentsDragController;
|
||||||
|
|
||||||
const setSyncIndicator = (active) => {
|
const setSyncIndicator = (active) => {
|
||||||
if (!syncIndicator) {
|
if (!syncIndicator) {
|
||||||
@@ -638,7 +649,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Setup shared delete dialog events
|
// Setup shared delete dialog events
|
||||||
if (deleteDialog) {
|
if (deleteDialog && deleteDialog.dataset.init !== "true") {
|
||||||
|
deleteDialog.dataset.init = "true";
|
||||||
deleteCancelBtn?.addEventListener("click", () => deleteDialog.close());
|
deleteCancelBtn?.addEventListener("click", () => deleteDialog.close());
|
||||||
deleteDialog.addEventListener("cancel", (e) => {
|
deleteDialog.addEventListener("cancel", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -730,156 +742,165 @@
|
|||||||
setupItem(item);
|
setupItem(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (list.dataset.pageInit !== "true") {
|
list.dataset.pageInit = "true";
|
||||||
list.dataset.pageInit = "true";
|
let draggedItem = null;
|
||||||
let draggedItem = null;
|
let pointerDrag = null;
|
||||||
let pointerDrag = null;
|
let lastDragOverTime = 0;
|
||||||
let lastDragOverTime = 0;
|
const DRAG_THROTTLE_MS = 100;
|
||||||
const DRAG_THROTTLE_MS = 100;
|
const startPointerDrag = (event) => {
|
||||||
const startPointerDrag = (event) => {
|
const handle = event.target.closest("[data-role='content-drag-handle']");
|
||||||
const handle = event.target.closest("[data-role='content-drag-handle']");
|
if (!handle || event.button !== 0) {
|
||||||
if (!handle || event.button !== 0) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
const item = handle.closest("[data-role='content-item']");
|
||||||
const item = handle.closest("[data-role='content-item']");
|
if (!item) {
|
||||||
if (!item) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
event.preventDefault();
|
||||||
event.preventDefault();
|
pointerDrag = {
|
||||||
pointerDrag = {
|
item,
|
||||||
item,
|
pointerId: event.pointerId,
|
||||||
pointerId: event.pointerId,
|
|
||||||
};
|
|
||||||
item.dataset.dragging = "true";
|
|
||||||
item.classList.add("opacity-60");
|
|
||||||
setDraggingState(true);
|
|
||||||
if (handle.setPointerCapture) {
|
|
||||||
handle.setPointerCapture(event.pointerId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
item.dataset.dragging = "true";
|
||||||
|
item.classList.add("opacity-60");
|
||||||
|
setDraggingState(true);
|
||||||
|
if (handle.setPointerCapture) {
|
||||||
|
handle.setPointerCapture(event.pointerId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const movePointerDrag = (event) => {
|
const movePointerDrag = (event) => {
|
||||||
if (!pointerDrag || event.pointerId !== pointerDrag.pointerId) {
|
if (!pointerDrag || event.pointerId !== pointerDrag.pointerId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const item = pointerDrag.item;
|
const now = Date.now();
|
||||||
const targetItem = document.elementFromPoint(event.clientX, event.clientY)?.closest("[data-role='content-item']");
|
if (now - lastDragOverTime < DRAG_THROTTLE_MS) {
|
||||||
if (!targetItem || targetItem === item) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
lastDragOverTime = now;
|
||||||
const rect = targetItem.getBoundingClientRect();
|
const item = pointerDrag.item;
|
||||||
const before = event.clientY - rect.top < rect.height / 2;
|
const targetItem = document.elementFromPoint(event.clientX, event.clientY)?.closest("[data-role='content-item']");
|
||||||
if (before) {
|
if (!targetItem || targetItem === item) {
|
||||||
targetItem.before(item);
|
return;
|
||||||
} else {
|
}
|
||||||
targetItem.after(item);
|
const rect = targetItem.getBoundingClientRect();
|
||||||
}
|
const before = event.clientY - rect.top < rect.height / 2;
|
||||||
};
|
if (before) {
|
||||||
|
targetItem.before(item);
|
||||||
|
} else {
|
||||||
|
targetItem.after(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const endPointerDrag = (event) => {
|
const endPointerDrag = (event) => {
|
||||||
if (!pointerDrag || event.pointerId !== pointerDrag.pointerId) {
|
if (!pointerDrag || event.pointerId !== pointerDrag.pointerId) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
pointerDrag.item.classList.remove("opacity-60");
|
||||||
|
pointerDrag.item.dataset.dragging = "";
|
||||||
|
pointerDrag = null;
|
||||||
|
setDraggingState(false);
|
||||||
|
syncOrder();
|
||||||
|
};
|
||||||
|
list.addEventListener("click", (event) => {
|
||||||
|
const moveUp = event.target.closest("[data-role='content-move-up']");
|
||||||
|
const moveDown = event.target.closest("[data-role='content-move-down']");
|
||||||
|
if (!moveUp && !moveDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
const item = event.target.closest("[data-role='content-item']");
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (moveUp) {
|
||||||
|
let prev = item.previousElementSibling;
|
||||||
|
while (prev && !prev.matches("[data-role='content-item']")) {
|
||||||
|
prev = prev.previousElementSibling;
|
||||||
}
|
}
|
||||||
pointerDrag.item.classList.remove("opacity-60");
|
if (prev) {
|
||||||
pointerDrag.item.dataset.dragging = "";
|
prev.before(item);
|
||||||
pointerDrag = null;
|
|
||||||
setDraggingState(false);
|
|
||||||
syncOrder();
|
|
||||||
};
|
|
||||||
list.addEventListener("click", (event) => {
|
|
||||||
const moveUp = event.target.closest("[data-role='content-move-up']");
|
|
||||||
const moveDown = event.target.closest("[data-role='content-move-down']");
|
|
||||||
if (!moveUp && !moveDown) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let next = item.nextElementSibling;
|
||||||
|
while (next && !next.matches("[data-role='content-item']")) {
|
||||||
|
next = next.nextElementSibling;
|
||||||
|
}
|
||||||
|
if (next) {
|
||||||
|
next.after(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syncOrder();
|
||||||
|
}, { signal: dragSignal });
|
||||||
|
|
||||||
|
list.addEventListener("dragstart", (event) => {
|
||||||
|
if (pointerDrag) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const item = event.target.closest("[data-role='content-item']");
|
return;
|
||||||
if (!item) {
|
}
|
||||||
return;
|
if (event.target.closest("[data-role='content-move-up']") || event.target.closest("[data-role='content-move-down']")) {
|
||||||
}
|
return;
|
||||||
if (moveUp) {
|
}
|
||||||
let prev = item.previousElementSibling;
|
if (event.target.closest(".status-badge") || event.target.closest("multi-select-simple") || event.target.closest("select")) {
|
||||||
while (prev && !prev.matches("[data-role='content-item']")) {
|
return;
|
||||||
prev = prev.previousElementSibling;
|
}
|
||||||
}
|
const handle = event.target.closest("[data-role='content-drag-handle']");
|
||||||
if (prev) {
|
if (!handle) {
|
||||||
prev.before(item);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let next = item.nextElementSibling;
|
|
||||||
while (next && !next.matches("[data-role='content-item']")) {
|
|
||||||
next = next.nextElementSibling;
|
|
||||||
}
|
|
||||||
if (next) {
|
|
||||||
next.after(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syncOrder();
|
|
||||||
});
|
|
||||||
|
|
||||||
list.addEventListener("dragstart", (event) => {
|
|
||||||
if (pointerDrag) {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.target.closest("[data-role='content-move-up']") || event.target.closest("[data-role='content-move-down']")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.target.closest(".status-badge") || event.target.closest("multi-select-simple") || event.target.closest("select")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const handle = event.target.closest("[data-role='content-drag-handle']");
|
|
||||||
if (!handle) {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const item = handle.closest("[data-role='content-item']");
|
|
||||||
if (!item) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
draggedItem = item;
|
|
||||||
item.dataset.dragging = "true";
|
|
||||||
draggedItem.classList.add("opacity-60");
|
|
||||||
setDraggingState(true);
|
|
||||||
event.dataTransfer.effectAllowed = "move";
|
|
||||||
event.dataTransfer.setData("text/plain", "move");
|
|
||||||
});
|
|
||||||
|
|
||||||
list.addEventListener("dragover", (event) => {
|
|
||||||
if (!draggedItem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const targetItem = event.target.closest("[data-role='content-item']");
|
return;
|
||||||
if (!targetItem || targetItem === draggedItem) {
|
}
|
||||||
return;
|
const item = handle.closest("[data-role='content-item']");
|
||||||
}
|
if (!item) {
|
||||||
const rect = targetItem.getBoundingClientRect();
|
return;
|
||||||
const before = event.clientY - rect.top < rect.height / 2;
|
}
|
||||||
if (before) {
|
draggedItem = item;
|
||||||
targetItem.before(draggedItem);
|
item.dataset.dragging = "true";
|
||||||
} else {
|
draggedItem.classList.add("opacity-60");
|
||||||
targetItem.after(draggedItem);
|
setDraggingState(true);
|
||||||
}
|
event.dataTransfer.effectAllowed = "move";
|
||||||
});
|
event.dataTransfer.setData("text/plain", "move");
|
||||||
|
}, { signal: dragSignal });
|
||||||
|
|
||||||
list.addEventListener("dragend", () => {
|
list.addEventListener("dragover", (event) => {
|
||||||
if (draggedItem) {
|
if (!draggedItem) {
|
||||||
draggedItem.classList.remove("opacity-60");
|
return;
|
||||||
draggedItem.dataset.dragging = "";
|
}
|
||||||
}
|
event.preventDefault();
|
||||||
draggedItem = null;
|
const targetItem = event.target.closest("[data-role='content-item']");
|
||||||
setDraggingState(false);
|
if (!targetItem || targetItem === draggedItem) {
|
||||||
syncOrder();
|
return;
|
||||||
});
|
}
|
||||||
|
const rect = targetItem.getBoundingClientRect();
|
||||||
|
const before = event.clientY - rect.top < rect.height / 2;
|
||||||
|
if (before) {
|
||||||
|
targetItem.before(draggedItem);
|
||||||
|
} else {
|
||||||
|
targetItem.after(draggedItem);
|
||||||
|
}
|
||||||
|
}, { signal: dragSignal });
|
||||||
|
|
||||||
list.addEventListener("pointerdown", startPointerDrag);
|
list.addEventListener("dragend", () => {
|
||||||
list.addEventListener("pointermove", movePointerDrag);
|
if (draggedItem) {
|
||||||
list.addEventListener("pointerup", endPointerDrag);
|
draggedItem.classList.remove("opacity-60");
|
||||||
list.addEventListener("pointercancel", endPointerDrag);
|
draggedItem.dataset.dragging = "";
|
||||||
}
|
}
|
||||||
|
draggedItem = null;
|
||||||
|
setDraggingState(false);
|
||||||
|
syncOrder();
|
||||||
|
}, { signal: dragSignal });
|
||||||
|
|
||||||
|
list.addEventListener("pointerdown", startPointerDrag, { signal: dragSignal });
|
||||||
|
list.addEventListener("pointermove", movePointerDrag, { signal: dragSignal });
|
||||||
|
list.addEventListener("pointerup", endPointerDrag, { signal: dragSignal });
|
||||||
|
list.addEventListener("pointercancel", endPointerDrag, { signal: dragSignal });
|
||||||
};
|
};
|
||||||
|
|
||||||
initFilter();
|
initFilter();
|
||||||
initPage();
|
initPage();
|
||||||
|
window.addEventListener("pageshow", (event) => {
|
||||||
|
if (event.persisted) {
|
||||||
|
initFilter();
|
||||||
|
initPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -135,7 +135,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content-action-button {
|
.content-action-button {
|
||||||
@apply inline-flex items-center justify-center gap-2 rounded-xs border border-slate-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-800 shadow-sm transition-all duration-75 hover:bg-stone-50 focus:outline-none focus:ring-2 focus:ring-slate-400/30;
|
@apply inline-flex items-center justify-center gap-2 rounded-xs border border-slate-300 bg-white px-3 py-1.5 text-base font-semibold text-gray-800 shadow-sm transition-all duration-75 hover:bg-stone-50 focus:outline-none focus:ring-2 focus:ring-slate-400/30;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dbform div-menu {
|
.dbform div-menu {
|
||||||
|
|||||||
Reference in New Issue
Block a user