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