Inital slider
This commit is contained in:
parent
e0a0e083be
commit
29d7a5bcfc
|
@ -0,0 +1,14 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>RGB Slider Tabs</title>
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="tabs"></div>
|
||||||
|
<div class="tab-content"></div>
|
||||||
|
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,81 @@
|
||||||
|
import "./rgb-slider.js";
|
||||||
|
|
||||||
|
const ws = new WebSocket("ws://localhost:8000/ws");
|
||||||
|
|
||||||
|
ws.onopen = () => {
|
||||||
|
console.log("WebSocket connection established");
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = () => {
|
||||||
|
console.log("WebSocket connection closed");
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onerror = (error) => {
|
||||||
|
console.error("WebSocket error:", error);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Number of sliders (tabs) you want to create
|
||||||
|
const numTabs = 3;
|
||||||
|
|
||||||
|
// Select the container for tabs and content
|
||||||
|
const tabsContainer = document.querySelector(".tabs");
|
||||||
|
const tabContentContainer = document.querySelector(".tab-content");
|
||||||
|
|
||||||
|
// Create tabs dynamically
|
||||||
|
for (let i = 1; i <= numTabs; i++) {
|
||||||
|
// Create the tab button
|
||||||
|
const tabButton = document.createElement("button");
|
||||||
|
tabButton.classList.add("tab");
|
||||||
|
tabButton.id = `tab${i}`;
|
||||||
|
tabButton.textContent = `Tab ${i}`;
|
||||||
|
|
||||||
|
// Add the tab button to the container
|
||||||
|
tabsContainer.appendChild(tabButton);
|
||||||
|
|
||||||
|
// Create the corresponding tab content (RGB slider)
|
||||||
|
const tabContent = document.createElement("div");
|
||||||
|
tabContent.classList.add("tab-pane");
|
||||||
|
tabContent.id = `content${i}`;
|
||||||
|
const slider = document.createElement("rgb-slider");
|
||||||
|
slider.id = i;
|
||||||
|
tabContent.appendChild(slider);
|
||||||
|
|
||||||
|
// Add the tab content to the container
|
||||||
|
tabContentContainer.appendChild(tabContent);
|
||||||
|
|
||||||
|
// Listen for color change on each RGB slider
|
||||||
|
slider.addEventListener("color-change", (e) => {
|
||||||
|
const { r, g, b } = e.detail;
|
||||||
|
console.log(`Color changed in tab ${i}:`, e.detail);
|
||||||
|
// Send RGB data to WebSocket server
|
||||||
|
if (ws.readyState === WebSocket.OPEN) {
|
||||||
|
const colorData = { r, g, b };
|
||||||
|
ws.send(JSON.stringify(colorData));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to switch tabs
|
||||||
|
function switchTab(tabId) {
|
||||||
|
const tabs = document.querySelectorAll(".tab");
|
||||||
|
const tabContents = document.querySelectorAll(".tab-pane");
|
||||||
|
|
||||||
|
tabs.forEach((tab) => tab.classList.remove("active"));
|
||||||
|
tabContents.forEach((content) => content.classList.remove("active"));
|
||||||
|
|
||||||
|
// Activate the clicked tab and corresponding content
|
||||||
|
document.getElementById(tabId).classList.add("active");
|
||||||
|
document
|
||||||
|
.getElementById("content" + tabId.replace("tab", ""))
|
||||||
|
.classList.add("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add event listeners to tabs
|
||||||
|
tabsContainer.addEventListener("click", (e) => {
|
||||||
|
if (e.target.classList.contains("tab")) {
|
||||||
|
switchTab(e.target.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initially set the first tab as active
|
||||||
|
switchTab("tab1");
|
|
@ -0,0 +1,195 @@
|
||||||
|
// rgb-slider.js
|
||||||
|
|
||||||
|
export class RGBSlider extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
const shadow = this.attachShadow({ mode: "open" });
|
||||||
|
|
||||||
|
shadow.innerHTML = `
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 50%;
|
||||||
|
|
||||||
|
font-family: sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
width: 50%;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sliders {
|
||||||
|
display: flex;
|
||||||
|
gap: 50px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-group input[type="range"] {
|
||||||
|
writing-mode: vertical-lr;
|
||||||
|
direction: rtl;
|
||||||
|
|
||||||
|
width: 10px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-group label {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rgb-inputs {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rgb-inputs input {
|
||||||
|
width: 6ch;
|
||||||
|
padding: 2px;
|
||||||
|
font-family: monospace;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rgb-inputs label {
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rgb-input-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile styles */
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.preview {
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-group input[type="range"] {
|
||||||
|
height: 180px;
|
||||||
|
width: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rgb-inputs input {
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 4px;
|
||||||
|
width: 7ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-group label,
|
||||||
|
.rgb-inputs label {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="preview" id="preview"></div>
|
||||||
|
|
||||||
|
<div class="sliders">
|
||||||
|
<div class="slider-group">
|
||||||
|
<input type="range" min="0" max="255" value="0" id="r">
|
||||||
|
<label>R</label>
|
||||||
|
</div>
|
||||||
|
<div class="slider-group">
|
||||||
|
<input type="range" min="0" max="255" value="0" id="g">
|
||||||
|
<label>G</label>
|
||||||
|
</div>
|
||||||
|
<div class="slider-group">
|
||||||
|
<input type="range" min="0" max="255" value="0" id="b">
|
||||||
|
<label>B</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rgb-inputs">
|
||||||
|
<div class="rgb-input-group">
|
||||||
|
<label for="rInput">R</label>
|
||||||
|
<input type="number" min="0" max="255" id="rInput" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="rgb-input-group">
|
||||||
|
<label for="gInput">G</label>
|
||||||
|
<input type="number" min="0" max="255" id="gInput" value="0">
|
||||||
|
</div>
|
||||||
|
<div class="rgb-input-group">
|
||||||
|
<label for="bInput">B</label>
|
||||||
|
<input type="number" min="0" max="255" id="bInput" value="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const get = (id) => shadow.querySelector(id);
|
||||||
|
this.r = get("#r");
|
||||||
|
this.g = get("#g");
|
||||||
|
this.b = get("#b");
|
||||||
|
this.rInput = get("#rInput");
|
||||||
|
this.gInput = get("#gInput");
|
||||||
|
this.bInput = get("#bInput");
|
||||||
|
this.preview = get("#preview");
|
||||||
|
|
||||||
|
const updateColor = (r, g, b) => {
|
||||||
|
this.preview.style.backgroundColor = `rgb(${r}, ${g}, ${b})`;
|
||||||
|
this.rInput.value = r;
|
||||||
|
this.gInput.value = g;
|
||||||
|
this.bInput.value = b;
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent("color-change", {
|
||||||
|
detail: { r, g, b },
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncFromSliders = () => {
|
||||||
|
const r = +this.r.value;
|
||||||
|
const g = +this.g.value;
|
||||||
|
const b = +this.b.value;
|
||||||
|
updateColor(r, g, b);
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncFromInputs = () => {
|
||||||
|
const r = Math.min(255, Math.max(0, +this.rInput.value));
|
||||||
|
const g = Math.min(255, Math.max(0, +this.gInput.value));
|
||||||
|
const b = Math.min(255, Math.max(0, +this.bInput.value));
|
||||||
|
this.r.value = r;
|
||||||
|
this.g.value = g;
|
||||||
|
this.b.value = b;
|
||||||
|
updateColor(r, g, b);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.r.addEventListener("input", syncFromSliders);
|
||||||
|
this.g.addEventListener("input", syncFromSliders);
|
||||||
|
this.b.addEventListener("input", syncFromSliders);
|
||||||
|
|
||||||
|
this.rInput.addEventListener("change", syncFromInputs);
|
||||||
|
this.gInput.addEventListener("change", syncFromInputs);
|
||||||
|
this.bInput.addEventListener("change", syncFromInputs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("rgb-slider", RGBSlider);
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* General tab styles */
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding: 10px 20px;
|
||||||
|
margin: 0 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:hover {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane.active {
|
||||||
|
display: block;
|
||||||
|
}
|
Loading…
Reference in New Issue