mirror of
https://github.com/Theodor-Springmann-Stiftung/musenalm.git
synced 2026-02-04 10:35:30 +00:00
some list things; image uplaod
This commit is contained in:
@@ -57,6 +57,8 @@ export class ContentImages extends HTMLElement {
|
||||
}
|
||||
this.dataset.init = "true";
|
||||
|
||||
this._wireUpload();
|
||||
|
||||
const raw = this.getAttribute("data-images") || "[]";
|
||||
const rawFiles = this.getAttribute("data-files") || "[]";
|
||||
let images = [];
|
||||
@@ -82,8 +84,62 @@ export class ContentImages extends HTMLElement {
|
||||
this._render(normalized);
|
||||
}
|
||||
|
||||
_wireUpload() {
|
||||
const panel = this.closest("[data-role='content-images-panel']");
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
const uploadInput = panel.querySelector("[data-role='content-images-upload-input']");
|
||||
if (!uploadInput || uploadInput.dataset.bound === "true") {
|
||||
return;
|
||||
}
|
||||
uploadInput.dataset.bound = "true";
|
||||
uploadInput.addEventListener("change", () => {
|
||||
this._uploadFiles(uploadInput, panel);
|
||||
});
|
||||
}
|
||||
|
||||
_uploadFiles(input, panel) {
|
||||
const endpoint = input.getAttribute("data-upload-endpoint") || "";
|
||||
const contentId = input.getAttribute("data-content-id") || "";
|
||||
const csrfToken = input.getAttribute("data-csrf-token") || "";
|
||||
const files = Array.from(input.files || []);
|
||||
if (!endpoint || !contentId || !csrfToken || files.length === 0) {
|
||||
return;
|
||||
}
|
||||
const payload = new FormData();
|
||||
payload.append("csrf_token", csrfToken);
|
||||
payload.append("content_id", contentId);
|
||||
files.forEach((file) => payload.append("scans", file));
|
||||
fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"HX-Request": "true",
|
||||
},
|
||||
body: payload,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
return null;
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then((html) => {
|
||||
if (!html || !panel) {
|
||||
return;
|
||||
}
|
||||
this._applyServerResponse(html, panel);
|
||||
})
|
||||
.catch(() => null)
|
||||
.finally(() => {
|
||||
input.value = "";
|
||||
});
|
||||
}
|
||||
|
||||
_render(images) {
|
||||
this.classList.add("inline-flex");
|
||||
this.classList.add("block");
|
||||
this.style.display = "block";
|
||||
this.style.width = "100%";
|
||||
const list = this._ensureList();
|
||||
list.innerHTML = "";
|
||||
|
||||
@@ -161,6 +217,11 @@ export class ContentImages extends HTMLElement {
|
||||
list.appendChild(wrapper);
|
||||
});
|
||||
|
||||
const uploadTile = this._findUploadTile();
|
||||
if (uploadTile) {
|
||||
list.appendChild(uploadTile);
|
||||
}
|
||||
|
||||
const dialog = this._ensureDialog();
|
||||
const fullImage = dialog.querySelector(`[data-role='${CONTENT_IMAGES_FULL_ROLE}']`);
|
||||
|
||||
@@ -185,12 +246,26 @@ export class ContentImages extends HTMLElement {
|
||||
if (!list) {
|
||||
list = document.createElement("div");
|
||||
list.dataset.role = CONTENT_IMAGES_LIST_ROLE;
|
||||
list.className = "inline-flex flex-wrap gap-2";
|
||||
this.appendChild(list);
|
||||
}
|
||||
list.className = "grid gap-2";
|
||||
list.style.gridTemplateColumns = "repeat(auto-fill, minmax(7rem, 1fr))";
|
||||
list.style.width = "100%";
|
||||
return list;
|
||||
}
|
||||
|
||||
_findUploadTile() {
|
||||
const panel = this.closest("[data-role='content-images-panel']");
|
||||
if (!panel) {
|
||||
return null;
|
||||
}
|
||||
const upload = panel.querySelector("[data-role='content-images-upload']");
|
||||
if (!upload) {
|
||||
return null;
|
||||
}
|
||||
return upload;
|
||||
}
|
||||
|
||||
_ensureDialog() {
|
||||
let dialog = this.querySelector(`[data-role='${CONTENT_IMAGES_DIALOG_ROLE}']`);
|
||||
if (dialog) {
|
||||
@@ -377,11 +452,35 @@ export class ContentImages extends HTMLElement {
|
||||
if (!html || !panel) {
|
||||
return;
|
||||
}
|
||||
panel.outerHTML = html;
|
||||
this._applyServerResponse(html, panel);
|
||||
})
|
||||
.catch(() => null)
|
||||
.finally(() => {
|
||||
dialog.close();
|
||||
});
|
||||
}
|
||||
|
||||
_applyServerResponse(html, panel) {
|
||||
const template = document.createElement("template");
|
||||
template.innerHTML = html.trim();
|
||||
const oobNodes = Array.from(template.content.querySelectorAll("[hx-swap-oob]"));
|
||||
oobNodes.forEach((node) => {
|
||||
const swapRaw = node.getAttribute("hx-swap-oob") || "";
|
||||
const [swapTypeRaw, selector] = swapRaw.split(":");
|
||||
const swapType = swapTypeRaw || "outerHTML";
|
||||
const target = selector ? document.querySelector(selector) : (node.id ? document.getElementById(node.id) : null);
|
||||
if (target) {
|
||||
if (swapType === "innerHTML") {
|
||||
target.innerHTML = node.innerHTML;
|
||||
} else {
|
||||
target.outerHTML = node.outerHTML;
|
||||
}
|
||||
}
|
||||
node.remove();
|
||||
});
|
||||
const replacement = template.content.firstElementChild;
|
||||
if (replacement) {
|
||||
panel.replaceWith(replacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,22 @@
|
||||
@apply bg-stone-50;
|
||||
}
|
||||
|
||||
body[data-dragging="true"] tool-tip {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body[data-dragging="true"] .tooltip-box {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
html.dragging tool-tip {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
html.dragging .tooltip-box {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply normal-nums;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,58 @@ export class ToolTip extends HTMLElement {
|
||||
return ["position", "timeout"];
|
||||
}
|
||||
|
||||
static _dragGuardInitialized = false;
|
||||
|
||||
static _setDragging(active) {
|
||||
window.__toolTipDragging = active;
|
||||
if (document.documentElement) {
|
||||
document.documentElement.classList.toggle("dragging", active);
|
||||
}
|
||||
if (document.body) {
|
||||
if (active) {
|
||||
document.body.dataset.dragging = "true";
|
||||
} else {
|
||||
delete document.body.dataset.dragging;
|
||||
}
|
||||
}
|
||||
if (active) {
|
||||
document.querySelectorAll(".tooltip-box").forEach((box) => {
|
||||
box.classList.remove("opacity-100");
|
||||
box.classList.add("opacity-0");
|
||||
box.classList.add("hidden");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static _ensureDragGuard() {
|
||||
if (ToolTip._dragGuardInitialized) {
|
||||
return;
|
||||
}
|
||||
ToolTip._dragGuardInitialized = true;
|
||||
const start = (event) => {
|
||||
const handle = event.target?.closest?.("[data-role='content-drag-handle']");
|
||||
if (handle || event.type === "dragstart") {
|
||||
ToolTip._setDragging(true);
|
||||
}
|
||||
};
|
||||
const end = () => {
|
||||
ToolTip._setDragging(false);
|
||||
};
|
||||
document.addEventListener("pointerdown", start, true);
|
||||
document.addEventListener("mousedown", start, true);
|
||||
document.addEventListener("dragstart", start, true);
|
||||
document.addEventListener("pointerup", end, true);
|
||||
document.addEventListener("mouseup", end, true);
|
||||
document.addEventListener("pointercancel", end, true);
|
||||
document.addEventListener("dragend", end, true);
|
||||
document.addEventListener("drop", end, true);
|
||||
window.addEventListener("blur", end);
|
||||
window.addEventListener("contentsdragging", (event) => {
|
||||
const active = Boolean(event.detail?.active);
|
||||
ToolTip._setDragging(active);
|
||||
});
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._tooltipBox = null;
|
||||
@@ -14,6 +66,7 @@ export class ToolTip extends HTMLElement {
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
ToolTip._ensureDragGuard();
|
||||
this.classList.add("relative", "block", "leading-none", "[&>*]:leading-normal");
|
||||
this._dataTipElem = this.querySelector(".data-tip");
|
||||
const tipContent = this._dataTipElem ? this._dataTipElem.innerHTML : "Tooltip";
|
||||
@@ -25,6 +78,7 @@ export class ToolTip extends HTMLElement {
|
||||
this._tooltipBox = document.createElement("div");
|
||||
this._tooltipBox.innerHTML = tipContent;
|
||||
this._tooltipBox.className = [
|
||||
"tooltip-box",
|
||||
"opacity-0",
|
||||
"hidden",
|
||||
"absolute",
|
||||
@@ -78,7 +132,32 @@ export class ToolTip extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
_forceHide() {
|
||||
clearTimeout(this._hideTimeout);
|
||||
clearTimeout(this._hiddenTimeout);
|
||||
if (!this._tooltipBox) {
|
||||
return;
|
||||
}
|
||||
this._tooltipBox.classList.remove("opacity-100");
|
||||
this._tooltipBox.classList.add("opacity-0");
|
||||
this._tooltipBox.classList.add("hidden");
|
||||
}
|
||||
|
||||
_isDragging() {
|
||||
if (window.__toolTipDragging) {
|
||||
return true;
|
||||
}
|
||||
if (document.body?.dataset?.dragging === "true") {
|
||||
return true;
|
||||
}
|
||||
return Boolean(document.querySelector("[data-dragging='true']"));
|
||||
}
|
||||
|
||||
_showTooltip() {
|
||||
if (this._isDragging()) {
|
||||
this._forceHide();
|
||||
return;
|
||||
}
|
||||
clearTimeout(this._hideTimeout);
|
||||
clearTimeout(this._hiddenTimeout);
|
||||
this._tooltipBox.classList.remove("hidden");
|
||||
|
||||
Reference in New Issue
Block a user