v0.32.2
This commit is contained in:
1 parent
bb025dcf44
commit
b0f211555a
74 files changed
+6089
-556
No files matched your search
@@ -0,0 +1,200 @@
|
||||
window.rustProxyInlineLayout = (() => {
|
||||
const grid = $("dashboard-overview-grid");
|
||||
const unlock = $("overview-layout-unlock");
|
||||
const actions = $("overview-inline-actions");
|
||||
const saveButton = $("overview-inline-save");
|
||||
const cancelButton = $("overview-inline-cancel");
|
||||
const hint = $("overview-inline-hint");
|
||||
const hiddenShelf = $("overview-inline-hidden");
|
||||
const hiddenList = $("overview-inline-hidden-list");
|
||||
const hiddenCount = $("overview-inline-hidden-count");
|
||||
const modalButton = $("overview-layout-open");
|
||||
let draft = null;
|
||||
let original = null;
|
||||
let dragging = null;
|
||||
let opening = false;
|
||||
let saving = false;
|
||||
|
||||
const groupFor = id => window.rustProxyMasonry?.groupFor?.(id) ?? 0;
|
||||
const visibleIds = () => draft.order.filter(id => !draft.hidden.has(id))
|
||||
.sort((a, b) => groupFor(a) - groupFor(b));
|
||||
const nodes = () => [...grid.children].filter(node => node.dataset.dashboardWidget);
|
||||
const nodeFor = id => nodes().find(node => node.dataset.dashboardWidget === id);
|
||||
const label = id => dashboardWidgetLabel(id);
|
||||
const dirty = () => draft && original && (draft.order.some((id, i) => id !== original.order[i])
|
||||
|| [...draft.hidden].some(id => !original.hidden.has(id)) || [...original.hidden].some(id => !draft.hidden.has(id)));
|
||||
|
||||
function renderHidden() {
|
||||
if (!hiddenList || !draft) return;
|
||||
const entries = draft.order.filter(id => draft.hidden.has(id));
|
||||
if (hiddenCount) hiddenCount.textContent = `(${entries.length})`;
|
||||
hiddenList.replaceChildren(...(entries.length ? entries.map(id => {
|
||||
const add = control("+", `${t("overviewLayoutInlineRestore")}: ${label(id)}`, "overview-inline-restore", () => {
|
||||
draft.hidden.delete(id);
|
||||
paint();
|
||||
nodeFor(id)?.querySelector(".overview-inline-grip")?.focus();
|
||||
});
|
||||
const item = el("div", undefined, "overview-inline-hidden-item");
|
||||
item.append(el("span", label(id)), dashboardWidgetTypeBadge(id), add);
|
||||
return item;
|
||||
}) : [el("span", t("overviewLayoutInlineHiddenEmpty"), "fine")]));
|
||||
}
|
||||
function hide(id) {
|
||||
if (!draft || !visibleIds().includes(id)) return;
|
||||
draft.hidden.add(id);
|
||||
paint();
|
||||
if (hiddenShelf) { hiddenShelf.open = true; hiddenShelf.querySelector("summary")?.focus(); }
|
||||
}
|
||||
function paint() {
|
||||
applyDashboardLayout(draft.order, [...draft.hidden]);
|
||||
renderHidden();
|
||||
const visible = visibleIds();
|
||||
for (const node of nodes()) {
|
||||
const i = visible.indexOf(node.dataset.dashboardWidget);
|
||||
const up = node.querySelector(".overview-inline-up");
|
||||
const down = node.querySelector(".overview-inline-down");
|
||||
if (up) up.disabled = i <= 0 || groupFor(visible[i - 1]) !== groupFor(node.dataset.dashboardWidget);
|
||||
if (down) down.disabled = i < 0 || i === visible.length - 1 ||
|
||||
groupFor(visible[i + 1]) !== groupFor(node.dataset.dashboardWidget);
|
||||
}
|
||||
}
|
||||
function clearDrag() {
|
||||
dragging = null;
|
||||
for (const node of nodes()) node.classList.remove("is-overview-dragging", "is-overview-drop-before", "is-overview-drop-after");
|
||||
}
|
||||
function move(id, target, after = false) {
|
||||
if (!draft || id === target || groupFor(id) !== groupFor(target)) return;
|
||||
const visible = visibleIds();
|
||||
if (!visible.includes(id) || !visible.includes(target)) return;
|
||||
visible.splice(visible.indexOf(id), 1);
|
||||
visible.splice(visible.indexOf(target) + (after ? 1 : 0), 0, id);
|
||||
const iter = visible.values();
|
||||
draft.order = draft.order.map(item => draft.hidden.has(item) ? item : iter.next().value);
|
||||
paint();
|
||||
}
|
||||
function nudge(id, delta) {
|
||||
const visible = visibleIds();
|
||||
const index = visible.indexOf(id), target = visible[index + delta];
|
||||
if (!target || groupFor(id) !== groupFor(target)) return;
|
||||
move(id, target, delta > 0);
|
||||
nodeFor(id)?.querySelector(delta < 0 ? ".overview-inline-up" : ".overview-inline-down")?.focus();
|
||||
}
|
||||
function control(text, title, className, run) {
|
||||
const button = el("button", text, `quiet ${className}`);
|
||||
button.type = "button";
|
||||
button.setAttribute("aria-label", title);
|
||||
button.title = title;
|
||||
button.addEventListener("click", run);
|
||||
return button;
|
||||
}
|
||||
function toolbar(node) {
|
||||
const id = node.dataset.dashboardWidget;
|
||||
const title = label(id);
|
||||
const bar = el("div", undefined, "overview-inline-toolbar");
|
||||
const grip = control("⠿", `${t("overviewLayoutInlineDrag")}: ${title}`, "overview-inline-grip", () => { });
|
||||
grip.draggable = true;
|
||||
grip.dataset.overviewDragHandle = id;
|
||||
bar.append(grip, dashboardWidgetTypeBadge(id),
|
||||
control("←", `${t("moveUp")}: ${title}`, "overview-inline-up", () => nudge(id, -1)),
|
||||
control("→", `${t("moveDown")}: ${title}`, "overview-inline-down", () => nudge(id, 1)),
|
||||
control("×", `${t("overviewLayoutInlineHide")}: ${title}`, "overview-inline-hide", () => hide(id)));
|
||||
node.append(bar);
|
||||
}
|
||||
function showEditing() {
|
||||
grid.classList.add("is-unlocked");
|
||||
unlock.setAttribute("aria-pressed", "true");
|
||||
unlock.dataset.i18n = "overviewLayoutLock";
|
||||
unlock.textContent = t("overviewLayoutLock");
|
||||
actions.hidden = false;
|
||||
hint.hidden = false;
|
||||
hint.textContent = t("overviewLayoutInlineHint");
|
||||
if (hiddenShelf) { hiddenShelf.hidden = false; hiddenShelf.open = false; }
|
||||
if (modalButton) modalButton.disabled = true;
|
||||
for (const node of nodes()) toolbar(node);
|
||||
paint();
|
||||
}
|
||||
function finish(preserveSaved = false) {
|
||||
clearDrag();
|
||||
for (const node of nodes()) node.querySelector(".overview-inline-toolbar")?.remove();
|
||||
grid.classList.remove("is-unlocked");
|
||||
unlock.setAttribute("aria-pressed", "false");
|
||||
unlock.dataset.i18n = "overviewLayoutUnlock";
|
||||
unlock.textContent = t("overviewLayoutUnlock");
|
||||
actions.hidden = true;
|
||||
hint.hidden = true;
|
||||
if (hiddenShelf) { hiddenShelf.open = false; hiddenShelf.hidden = true; }
|
||||
if (hiddenList) hiddenList.replaceChildren();
|
||||
if (modalButton) modalButton.disabled = false;
|
||||
if (!preserveSaved && original) applyDashboardLayout(original.order, [...original.hidden]);
|
||||
draft = original = null;
|
||||
}
|
||||
async function begin() {
|
||||
if (draft || opening || saving || $("overview-layout-dialog")?.open) return;
|
||||
opening = true;
|
||||
unlock.disabled = true;
|
||||
try {
|
||||
await window.rustProxyPreferences?.load?.();
|
||||
const saved = window.rustProxyPreferences?.current?.() || ADMIN_APPEARANCE_DEFAULTS;
|
||||
const order = normalizeDashboardWidgets(saved.dashboard_widgets);
|
||||
const hidden = new Set(normalizeHiddenDashboardWidgets(saved.dashboard_hidden_widgets, saved.dashboard_widgets));
|
||||
original = { order: [...order], hidden: new Set(hidden) };
|
||||
draft = { order: [...order], hidden: new Set(hidden) };
|
||||
showEditing();
|
||||
} catch (error) { if (error?.status !== 401) toast(error.message, true); }
|
||||
finally { opening = false; unlock.disabled = false; }
|
||||
}
|
||||
async function save() {
|
||||
if (!draft || saving) return;
|
||||
if (!dirty()) { finish(true); return; }
|
||||
saving = true;
|
||||
saveButton.disabled = cancelButton.disabled = unlock.disabled = true;
|
||||
try {
|
||||
await window.rustProxyPreferences.update({
|
||||
dashboard_widgets: [...draft.order], dashboard_hidden_widgets: [...draft.hidden],
|
||||
});
|
||||
finish(true);
|
||||
toast(t("overviewLayoutEditorSaved"));
|
||||
} catch (error) { hint.textContent = error.message || t("overviewLayoutInlineError"); }
|
||||
finally { saving = false; saveButton.disabled = cancelButton.disabled = unlock.disabled = false; }
|
||||
}
|
||||
function cancel() { if (draft && !saving) finish(false); }
|
||||
function dropTarget(event) {
|
||||
const widget = event.target?.closest?.("[data-dashboard-widget]");
|
||||
return widget && grid.contains(widget) && !widget.hidden && visibleIds().includes(widget.dataset.dashboardWidget) ? widget : null;
|
||||
}
|
||||
function positionAfter(widget, event) {
|
||||
const rect = widget.getBoundingClientRect();
|
||||
return event.clientX >= rect.left + rect.width / 2;
|
||||
}
|
||||
grid?.addEventListener("dragstart", event => {
|
||||
const handle = event.target?.closest?.("[data-overview-drag-handle]");
|
||||
if (!draft || !handle || !grid.contains(handle)) { event.preventDefault(); return; }
|
||||
dragging = handle.dataset.overviewDragHandle;
|
||||
event.dataTransfer?.setData("text/plain", dragging);
|
||||
if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
|
||||
nodeFor(dragging)?.classList.add("is-overview-dragging");
|
||||
});
|
||||
grid?.addEventListener("dragover", event => {
|
||||
if (!draft || !dragging) return;
|
||||
const target = dropTarget(event);
|
||||
if (!target || target.dataset.dashboardWidget === dragging ||
|
||||
groupFor(target.dataset.dashboardWidget) !== groupFor(dragging)) return;
|
||||
event.preventDefault();
|
||||
for (const node of nodes()) node.classList.remove("is-overview-drop-before", "is-overview-drop-after");
|
||||
target.classList.add(positionAfter(target, event) ? "is-overview-drop-after" : "is-overview-drop-before");
|
||||
if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
|
||||
});
|
||||
grid?.addEventListener("drop", event => {
|
||||
if (!draft || !dragging) return;
|
||||
event.preventDefault();
|
||||
const target = dropTarget(event), from = dragging;
|
||||
clearDrag();
|
||||
if (target && target.dataset.dashboardWidget !== from)
|
||||
move(from, target.dataset.dashboardWidget, positionAfter(target, event));
|
||||
});
|
||||
grid?.addEventListener("dragend", clearDrag);
|
||||
unlock?.addEventListener("click", () => { if (draft) void save(); else void begin(); });
|
||||
saveButton?.addEventListener("click", () => { void save(); });
|
||||
cancelButton?.addEventListener("click", cancel);
|
||||
return { isEditing: () => !!draft, cancel };
|
||||
})();
|
||||
Reference in new issue
Block a user