v0.32.6_official

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-14 13:04:06 +02:00
1 parent fc8711d4d8
commit d2c6cf59a9
16 files changed
+1801 -1027

No files matched your search

+47 -7
View File
@@ -96,8 +96,6 @@ function applyDashboardLayout(order, hidden) {
const normalizedOrder = normalizeDashboardWidgets(order), hiddenSet = new Set(normalizeHiddenDashboardWidgets(hidden));
const grid = document.querySelector("#panel-overview .stats-grid"); if (!grid) return { order: normalizedOrder, hidden: [...hiddenSet] };
const nodes = new Map([...grid.querySelectorAll("[data-dashboard-widget]")].map(node => [node.dataset.dashboardWidget, node]));
// Re-appending an already ordered live grid can break touch-scroll
// anchoring in mobile browsers. Move the DOM only on a real order change.
const orderedNodes = normalizedOrder.map(id => nodes.get(id)).filter(Boolean);
for (const node of orderedNodes) node.hidden = hiddenSet.has(node.dataset.dashboardWidget);
const currentNodes = [...grid.querySelectorAll("[data-dashboard-widget]")];
@@ -129,23 +127,25 @@ function dashboardWidgetTypeBadge(id) {
}
function renderDashboardWidgetSettings() {
const list = $("dashboard-widget-list"); if (!list) return;
const previousScroll = list.scrollLeft;
const focusedRow = document.activeElement?.closest?.("[data-dashboard-widget-setting]");
const focusedId = focusedRow && list.contains?.(focusedRow) ? focusedRow.dataset.dashboardWidgetSetting : null;
const focusedAction = focusedId ? document.activeElement?.dataset.dashboardWidgetAction : null;
const current = adminPreferences || ADMIN_APPEARANCE_DEFAULTS, order = normalizeDashboardWidgets(current.dashboard_widgets), hidden = new Set(normalizeHiddenDashboardWidgets(current.dashboard_hidden_widgets));
const rows = order.map((id, index) => {
const row = el("div", undefined, "dashboard-widget-setting"); row.dataset.dashboardWidgetSetting = id;
const visible = !hidden.has(id);
const toggle = el("button", undefined, "dashboard-widget-toggle"); toggle.type = "button"; toggle.classList.toggle("is-hidden", !visible); toggle.setAttribute("aria-pressed", String(visible)); toggle.setAttribute("aria-label", `${t("dashboardWidgetVisible")}: ${dashboardWidgetLabel(id)}`);
const toggle = el("button", undefined, "dashboard-widget-toggle"); toggle.type = "button"; toggle.dataset.dashboardWidgetAction = "toggle"; toggle.classList.toggle("is-hidden", !visible); toggle.setAttribute("aria-pressed", String(visible)); toggle.setAttribute("aria-label", `${t("dashboardWidgetVisible")}: ${dashboardWidgetLabel(id)}`);
const descriptionKey = ADMIN_DASHBOARD_BY_ID.get(id)?.description_key;
if (descriptionKey) {
const description = t(descriptionKey);
row.title = description;
toggle.setAttribute("aria-description", description);
}
// Type badges belong in the visual layout editor, not the long
// Appearance settings list where they crowd the visibility controls.
toggle.append(el("span", undefined, "dashboard-widget-visibility"), el("span", dashboardWidgetLabel(id), "dashboard-widget-setting-label"));
const actions = el("span", undefined, "dashboard-widget-setting-actions");
const up = el("button", "↑", "quiet"); up.type = "button"; up.disabled = index === 0; up.setAttribute("aria-label", t("moveUp"));
const down = el("button", "↓", "quiet"); down.type = "button"; down.disabled = index === order.length - 1; down.setAttribute("aria-label", t("moveDown"));
const up = el("button", "←", "quiet"); up.type = "button"; up.dataset.dashboardWidgetAction = "up"; up.disabled = index === 0; up.setAttribute("aria-label", t("dashboardWidgetsMoveEarlier"));
const down = el("button", "→", "quiet"); down.type = "button"; down.dataset.dashboardWidgetAction = "down"; down.disabled = index === order.length - 1; down.setAttribute("aria-label", t("dashboardWidgetsMoveLater"));
toggle.addEventListener("click", () => {
const previous = [...hidden], next = new Set(hidden); if (visible) next.add(id); else next.delete(id);
if (adminPreferences) adminPreferences.dashboard_hidden_widgets = [...next]; applyDashboardLayout(order, [...next]); renderDashboardWidgetSettings();
@@ -160,6 +160,46 @@ function renderDashboardWidgetSettings() {
up.addEventListener("click", () => move(-1)); down.addEventListener("click", () => move(1)); actions.append(up, down); row.append(toggle, actions); return row;
});
list.replaceChildren(...rows);
list.scrollLeft = previousScroll;
if (focusedId && focusedAction) {
const row = [...list.children].find(item => item.dataset.dashboardWidgetSetting === focusedId);
row?.querySelector(`[data-dashboard-widget-action="${focusedAction}"]`)?.focus({ preventScroll: true });
}
syncDashboardWidgetListView();
if (typeof requestAnimationFrame === "function") requestAnimationFrame(syncDashboardWidgetScrollButtons);
}
let dashboardWidgetListExpanded = false;
function syncDashboardWidgetListView() {
const list = $("dashboard-widget-list"), expand = $("dashboard-widget-expand");
if (!list || !expand) return;
list.classList.toggle("is-expanded", dashboardWidgetListExpanded);
expand.setAttribute("aria-expanded", String(dashboardWidgetListExpanded));
const key = dashboardWidgetListExpanded ? "dashboardWidgetsCompact" : "dashboardWidgetsShowAll";
expand.dataset.i18n = key;
expand.textContent = t(key);
for (const id of ["dashboard-widget-scroll-prev", "dashboard-widget-scroll-next"]) {
const button = $(id); if (button) button.hidden = dashboardWidgetListExpanded;
}
syncDashboardWidgetScrollButtons();
}
function syncDashboardWidgetScrollButtons() {
const list = $("dashboard-widget-list"); if (!list) return;
const previous = $("dashboard-widget-scroll-prev"), next = $("dashboard-widget-scroll-next");
if (previous) previous.disabled = dashboardWidgetListExpanded || list.scrollLeft <= 1;
if (next) next.disabled = dashboardWidgetListExpanded || list.scrollLeft + list.clientWidth >= list.scrollWidth - 1;
}
$("dashboard-widget-expand")?.addEventListener("click", () => {
dashboardWidgetListExpanded = !dashboardWidgetListExpanded;
syncDashboardWidgetListView();
});
$("dashboard-widget-list")?.addEventListener?.("scroll", syncDashboardWidgetScrollButtons, { passive: true });
window.addEventListener?.("resize", syncDashboardWidgetScrollButtons);
for (const [id, direction] of [["dashboard-widget-scroll-prev", -1], ["dashboard-widget-scroll-next", 1]]) {
$(id)?.addEventListener("click", () => {
const list = $("dashboard-widget-list");
if (!list) return;
list.scrollBy({ left: direction * Math.max(160, list.clientWidth * .8), behavior: "smooth" });
});
}
function refreshChartStylePalette(active = adminPreferences?.chart_style || "default") {
const target = $("chart-style-palette"); if (!target || !languagePacks.size) return;