191 lines
5.8 KiB
JavaScript
191 lines
5.8 KiB
JavaScript
document.addEventListener("DOMContentLoaded", () => {
|
|
const csrfToken = document
|
|
.querySelector("meta[name='csrf-token']")
|
|
.getAttribute("content");
|
|
|
|
const SIDEBAR_STORAGE_KEY = "bds-panel-sidebar";
|
|
const ASSISTANT_STORAGE_KEY = "bds-panel-assistant-sidebar";
|
|
const UI_LANGUAGE_STORAGE_KEY = "bds-ui-language";
|
|
|
|
const clamp = (value, min, max) => Math.max(min, Math.min(value, max));
|
|
|
|
const readStoredSize = (key, fallback, min, max) => {
|
|
const raw = window.localStorage.getItem(key);
|
|
|
|
if (!raw) {
|
|
return fallback;
|
|
}
|
|
|
|
const parsed = Number.parseInt(raw, 10);
|
|
|
|
if (Number.isNaN(parsed)) {
|
|
return fallback;
|
|
}
|
|
|
|
return clamp(parsed, min, max);
|
|
};
|
|
|
|
const shellWidth = (selector) => {
|
|
const shell = document.querySelector(selector);
|
|
|
|
if (!shell) {
|
|
return 0;
|
|
}
|
|
|
|
const width = Number.parseInt(shell.style.width || "0", 10);
|
|
return Number.isNaN(width) ? Math.round(shell.getBoundingClientRect().width) : width;
|
|
};
|
|
|
|
const setShellWidth = (selector, width) => {
|
|
const shell = document.querySelector(selector);
|
|
|
|
if (shell) {
|
|
shell.style.width = `${width}px`;
|
|
shell.classList.remove("is-hidden");
|
|
}
|
|
};
|
|
|
|
const persistWidth = (target, width) => {
|
|
const key = target === "assistant" ? ASSISTANT_STORAGE_KEY : SIDEBAR_STORAGE_KEY;
|
|
window.localStorage.setItem(key, String(width));
|
|
};
|
|
|
|
const Hooks = {
|
|
AppShell: {
|
|
mounted() {
|
|
this.syncStoredLayout();
|
|
this.syncStoredUiLanguage();
|
|
|
|
this.handleMouseDown = (event) => {
|
|
const handle = event.target.closest("[data-role='resize-handle']");
|
|
|
|
if (!handle || !this.el.contains(handle)) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
|
|
const target = handle.dataset.resize;
|
|
const startX = event.clientX;
|
|
const startWidth =
|
|
target === "assistant"
|
|
? shellWidth("[data-testid='assistant-shell']")
|
|
: shellWidth("[data-testid='sidebar-shell']");
|
|
|
|
const min = target === "assistant" ? 280 : 200;
|
|
const max = target === "assistant" ? 640 : 500;
|
|
const invert = target === "assistant";
|
|
|
|
const onMouseMove = (moveEvent) => {
|
|
const delta = invert ? startX - moveEvent.clientX : moveEvent.clientX - startX;
|
|
const width = clamp(startWidth + delta, min, max);
|
|
const selector = target === "assistant" ? "[data-testid='assistant-shell']" : "[data-testid='sidebar-shell']";
|
|
|
|
setShellWidth(selector, width);
|
|
persistWidth(target, width);
|
|
};
|
|
|
|
const onMouseUp = (upEvent) => {
|
|
const delta = invert ? startX - upEvent.clientX : upEvent.clientX - startX;
|
|
const width = clamp(startWidth + delta, min, max);
|
|
|
|
persistWidth(target, width);
|
|
this.pushEvent("resize_panel", { target, width });
|
|
|
|
window.removeEventListener("mousemove", onMouseMove);
|
|
window.removeEventListener("mouseup", onMouseUp);
|
|
};
|
|
|
|
window.addEventListener("mousemove", onMouseMove);
|
|
window.addEventListener("mouseup", onMouseUp);
|
|
};
|
|
|
|
this.el.addEventListener("mousedown", this.handleMouseDown);
|
|
|
|
this.handleNativeMenuAction = (event) => {
|
|
const action = event.detail?.action;
|
|
|
|
if (action) {
|
|
this.pushEvent("native_menu_action", { action });
|
|
}
|
|
};
|
|
|
|
this.handleChange = (event) => {
|
|
const select = event.target.closest(".status-bar-language-select");
|
|
|
|
if (select && this.el.contains(select)) {
|
|
window.localStorage.setItem(UI_LANGUAGE_STORAGE_KEY, select.value);
|
|
}
|
|
};
|
|
|
|
window.addEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
|
this.el.addEventListener("change", this.handleChange);
|
|
},
|
|
|
|
destroyed() {
|
|
this.el.removeEventListener("mousedown", this.handleMouseDown);
|
|
this.el.removeEventListener("change", this.handleChange);
|
|
window.removeEventListener("bds:native-menu-action", this.handleNativeMenuAction);
|
|
},
|
|
|
|
syncStoredLayout() {
|
|
this.pushEvent("sync_layout", {
|
|
sidebar_width: readStoredSize(SIDEBAR_STORAGE_KEY, shellWidth("[data-testid='sidebar-shell']"), 200, 500),
|
|
assistant_sidebar_width: readStoredSize(ASSISTANT_STORAGE_KEY, 360, 280, 640)
|
|
});
|
|
},
|
|
|
|
syncStoredUiLanguage() {
|
|
const stored = window.localStorage.getItem(UI_LANGUAGE_STORAGE_KEY);
|
|
|
|
if (stored) {
|
|
this.pushEvent("sync_ui_language", { language: stored });
|
|
}
|
|
}
|
|
},
|
|
|
|
SidebarInteractions: {
|
|
mounted() {
|
|
this.handleDblClick = (event) => {
|
|
const button = event.target.closest("[data-testid='sidebar-open-item']");
|
|
|
|
if (!button || !this.el.contains(button)) {
|
|
return;
|
|
}
|
|
|
|
this.pushEvent("pin_sidebar_item", {
|
|
route: button.dataset.route,
|
|
id: button.dataset.itemId,
|
|
title: button.dataset.openTitle || "",
|
|
subtitle: button.dataset.openSubtitle || ""
|
|
});
|
|
};
|
|
|
|
this.el.addEventListener("dblclick", this.handleDblClick);
|
|
},
|
|
|
|
destroyed() {
|
|
this.el.removeEventListener("dblclick", this.handleDblClick);
|
|
}
|
|
}
|
|
};
|
|
|
|
const liveSocket = new LiveView.LiveSocket("/live", Phoenix.Socket, {
|
|
params: { _csrf_token: csrfToken },
|
|
hooks: Hooks,
|
|
metadata: {
|
|
keydown: (event) => ({
|
|
key: event.key,
|
|
meta: event.metaKey,
|
|
ctrl: event.ctrlKey,
|
|
alt: event.altKey,
|
|
shift: event.shiftKey,
|
|
tag: event.target?.tagName || null,
|
|
contentEditable: event.target?.isContentEditable || false
|
|
})
|
|
}
|
|
});
|
|
|
|
liveSocket.connect();
|
|
window.liveSocket = liveSocket;
|
|
}); |