led-controller/static/rgb-slider.js

196 lines
4.8 KiB
JavaScript

// 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);