led-controller/static/BarControlSystem.js

271 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ApiService } from "./ApiService.js";
import { DialogManager } from "./DialogManager.js";
import { throttle, createStyledElement } from "./utils.js";
export class BarControlSystem {
constructor() {
this.settings = {};
this.websockets = {};
this.init();
}
async init() {
await this.loadSettings();
this.createControlPanel();
this.renderColorPickers();
}
async loadSettings() {
this.settings = await ApiService.loadSettings();
}
createControlPanel() {
const panel = createStyledElement("div", {
position: "fixed",
top: "10px",
right: "10px",
padding: "15px",
border: "2px solid #333",
borderRadius: "8px",
backgroundColor: "#f0f0f0",
zIndex: "1000",
});
panel.id = "control-panel";
const title = createStyledElement(
"h3",
{
margin: "0 0 10px 0",
fontFamily: "Arial, sans-serif",
},
{ textContent: "Control Panel" },
);
const buttonStyles = {
padding: "8px 12px",
border: "none",
borderRadius: "4px",
cursor: "pointer",
color: "white",
};
const createButton = createStyledElement(
"button",
{
...buttonStyles,
marginRight: "10px",
backgroundColor: "#4CAF50",
},
{ textContent: "Create New Bar" },
);
const refreshButton = createStyledElement(
"button",
{
...buttonStyles,
backgroundColor: "#2196F3",
},
{ textContent: "Refresh" },
);
createButton.onclick = () => this.showCreateDialog();
refreshButton.onclick = () => this.refresh();
panel.append(title, createButton, refreshButton);
document.body.appendChild(panel);
}
showCreateDialog() {
DialogManager.showCreateDialog((barData) => this.createBar(barData));
}
async createBar(barData) {
if (!barData.barId) {
alert("Bar ID is required");
return;
}
try {
await ApiService.createBar(barData);
console.log(`Bar created: ${barData.barId}`);
await this.refresh();
} catch (error) {
console.error("Failed to create bar:", error);
alert(error.message || "Failed to create bar");
}
}
async deleteBar(barId) {
if (!confirm(`Are you sure you want to delete bar ${barId}?`)) {
return;
}
try {
await ApiService.deleteBar(barId);
console.log(`Bar deleted: ${barId}`);
await this.refresh();
} catch (error) {
console.error("Failed to delete bar:", error);
alert(error.message || "Failed to delete bar");
}
}
async refresh() {
// Close existing websockets
Object.values(this.websockets).forEach((ws) => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.close();
}
});
// Remove existing color pickers
Object.keys(this.settings).forEach((barId) => {
const form = document.getElementById("color_form_" + barId);
if (form) {
form.remove();
}
});
this.websockets = {};
await this.loadSettings();
this.renderColorPickers();
}
renderColorPickers() {
Object.keys(this.settings).forEach((barId) => {
const config = this.settings[barId];
this.createColorPicker(barId, config);
});
}
makeDraggable(element, barId) {
let isDragging = false;
let startX, startY, initialX, initialY;
element.addEventListener("mousedown", (e) => {
if (e.target.tagName === "BUTTON") return;
isDragging = true;
startX = e.clientX;
startY = e.clientY;
const rect = element.getBoundingClientRect();
initialX = rect.left;
initialY = rect.top;
element.style.cursor = "move";
e.preventDefault();
});
document.addEventListener("mousemove", (e) => {
if (!isDragging) return;
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
const newX = initialX + deltaX;
const newY = initialY + deltaY;
element.style.left = newX + "px";
element.style.top = newY + "px";
});
document.addEventListener("mouseup", () => {
if (isDragging) {
const rect = element.getBoundingClientRect();
ApiService.savePosition(barId, rect.left, rect.top);
element.style.cursor = "default";
}
isDragging = false;
});
}
createColorPicker(barId, config) {
const ws = new WebSocket(config["url"]);
this.websockets[barId] = ws;
const form = createStyledElement("form", {
position: "absolute",
left: config["x"] + "px",
top: config["y"] + "px",
padding: "10px",
border: "1px solid #ccc",
borderRadius: "5px",
backgroundColor: "white",
});
form.id = "color_form_" + barId;
const label = createStyledElement(
"label",
{
display: "block",
marginBottom: "5px",
fontFamily: "Arial, sans-serif",
fontSize: "14px",
},
{
htmlFor: "color_input_" + barId,
textContent: barId,
},
);
const colorInput = createStyledElement(
"input",
{},
{
type: "color",
id: "color_input_" + barId,
name: barId,
value: config["color"],
},
);
const deleteButton = createStyledElement(
"button",
{
position: "absolute",
top: "2px",
right: "2px",
width: "20px",
height: "20px",
backgroundColor: "#f44336",
color: "white",
border: "none",
borderRadius: "50%",
cursor: "pointer",
fontSize: "12px",
lineHeight: "1",
},
{
type: "button",
textContent: "×",
},
);
deleteButton.onclick = () => this.deleteBar(barId);
const throttledColorHandler = throttle((event) => {
const color = event.target.value;
console.log(`Color selected for ${barId}: ${color}`);
ApiService.saveColor(barId, color);
if (ws.readyState === WebSocket.OPEN) {
const message = { color1: color };
ws.send(JSON.stringify(message));
} else {
console.warn(
`WebSocket not ready for ${barId}. ReadyState: ${ws.readyState}`,
);
}
}, 500);
colorInput.addEventListener("input", throttledColorHandler);
form.append(label, colorInput, deleteButton);
document.body.appendChild(form);
this.makeDraggable(form, barId);
}
}