275 lines
7.0 KiB
JavaScript
275 lines
7.0 KiB
JavaScript
|
|
// Define the HueSelect custom element
|
|
class HueSelect extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({ mode: 'open' });
|
|
this.shadowRoot.innerHTML = `
|
|
<style>
|
|
.color-option {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
}
|
|
.color-patch {
|
|
width: 30px;
|
|
height: 30px;
|
|
margin-top: 18px;
|
|
margin-right: 10px;
|
|
border: 1px solid #000;
|
|
}
|
|
.hue-label {
|
|
margin-top: 18px;
|
|
width: 90px;
|
|
text-align: left;
|
|
}
|
|
.label-container {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.dropdown {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
.dropdown-content {
|
|
display: none;
|
|
position: absolute;
|
|
background-color: #f9f9f9;
|
|
min-width: 180px;
|
|
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
|
z-index: 1;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
.dropdown-content .color-option {
|
|
padding: 8px 12px;
|
|
}
|
|
.dropdown:hover .dropdown-content {
|
|
display: block;
|
|
}
|
|
</style>
|
|
<div class="dropdown">
|
|
<div class="color-option" id="selectedColor">
|
|
<span class="color-patch" style="background-color: hsl(0, 100%, 50%)"></span>
|
|
<div class="label-container">
|
|
<label class="hue-label" id="hue-label">0</label>
|
|
</div>
|
|
</div>
|
|
<div class="dropdown-content" id="colorPicker">
|
|
<!-- The options will be added dynamically using JavaScript -->
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.currentHue = 0;
|
|
this.colorList;
|
|
this.hueLabel = this.shadowRoot.getElementById('hue-label');
|
|
|
|
const selectedColorDiv = this.shadowRoot.getElementById('selectedColor');
|
|
const colorPatch = selectedColorDiv.querySelector('.color-patch');
|
|
|
|
/* colorPatch.addEventListener('mouseenter', () => {
|
|
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
|
dropdownContent.style.display = 'block';
|
|
}); */
|
|
|
|
// Add the global click event listener to hide the color picker dropdown
|
|
/*
|
|
window.addEventListener('click', (event) => {
|
|
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
|
if (!event.target.closest('.dropdown')) {
|
|
dropdownContent.style.display = 'none';
|
|
}
|
|
}); */
|
|
|
|
selectedColorDiv.addEventListener('click', () => {
|
|
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
|
dropdownContent.style.display = dropdownContent.style.display === 'block' ? 'none' : 'block';
|
|
});
|
|
|
|
// Generate the color options and add them to the dropdown
|
|
this.createColorOptions();
|
|
this.setHue(0);
|
|
}
|
|
|
|
generateColors() {
|
|
const colors = [];
|
|
for (let hue = 0; hue <= 360; hue += 10) {
|
|
if (hue == 360) { hue = 359; }
|
|
colors.push(hue);
|
|
}
|
|
|
|
colors.push(-1);
|
|
colors.push(-2);
|
|
return colors;
|
|
}
|
|
|
|
createColorOption(hue) {
|
|
const colorOption = document.createElement('div');
|
|
colorOption.classList.add('color-option');
|
|
|
|
const colorPatch = document.createElement('span');
|
|
colorPatch.classList.add('color-patch');
|
|
|
|
const colorText = document.createElement('span');
|
|
colorText.classList.add('color-text');
|
|
colorText.textContent = hue;
|
|
|
|
const rgbHex = document.createElement('span');
|
|
rgbHex.classList.add('rgb-hex');
|
|
|
|
if (hue === -2) {
|
|
colorPatch.style.backgroundColor = 'rgb(0,0,0)';
|
|
rgbHex.innerHTML = ' #000000';
|
|
} else if (hue === -1) {
|
|
colorPatch.style.backgroundColor = 'rgb(255,255,255)';
|
|
rgbHex.innerHTML = ' #FFFFFF';
|
|
} else {
|
|
colorPatch.style.backgroundColor = `hsl(${hue}, 100%, 50%)`;
|
|
const hexColor = this.hslToRgb(hue, 100, 50).toUpperCase();
|
|
rgbHex.innerHTML = `<span> ${hexColor}</span>`;
|
|
}
|
|
|
|
colorOption.appendChild(colorPatch);
|
|
colorOption.appendChild(colorText);
|
|
colorOption.appendChild(rgbHex);
|
|
|
|
// Add click event listener to each color option
|
|
colorOption.addEventListener('click', () => this.handleColorSelection(hue));
|
|
|
|
return colorOption;
|
|
}
|
|
|
|
createColorOptions() {
|
|
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
|
this.colorList = this.generateColors();
|
|
|
|
this.colorList.forEach(hue => {
|
|
const colorOption = this.createColorOption(hue);
|
|
dropdownContent.appendChild(colorOption);
|
|
});
|
|
|
|
// Create elements for white and black colors
|
|
//const whiteColorOption = this.createColorOption(-1);
|
|
//dropdownContent.appendChild(whiteColorOption);
|
|
|
|
//const blackColorOption = this.createColorOption(-2);
|
|
//dropdownContent.appendChild(blackColorOption);
|
|
}
|
|
|
|
// Function to convert HSL to RGB
|
|
hslToRgb(h, s, l) {
|
|
h /= 360;
|
|
s /= 100;
|
|
l /= 100;
|
|
let r, g, b;
|
|
if (s === 0) {
|
|
r = g = b = l; // achromatic
|
|
} else {
|
|
const hue2rgb = (p, q, t) => {
|
|
if (t < 0) t += 1;
|
|
if (t > 1) t -= 1;
|
|
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
if (t < 1 / 2) return q;
|
|
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
return p;
|
|
};
|
|
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
const p = 2 * l - q;
|
|
r = hue2rgb(p, q, h + 1 / 3);
|
|
g = hue2rgb(p, q, h);
|
|
b = hue2rgb(p, q, h - 1 / 3);
|
|
}
|
|
const toHex = (x) => {
|
|
const hex = Math.round(x * 255).toString(16);
|
|
return hex.length === 1 ? '0' + hex : hex;
|
|
};
|
|
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
}
|
|
|
|
|
|
handleColorSelection(hue) {
|
|
this.currentHue = hue;
|
|
const selectedColorDiv = this.shadowRoot.getElementById('selectedColor');
|
|
const colorPatch = selectedColorDiv.querySelector('.color-patch');
|
|
|
|
// Update the color patch and hue label
|
|
const rgb = this.getRGBfromHue(hue);
|
|
colorPatch.style.backgroundColor = rgb;
|
|
//console.log(colorPatch.style.backgroundColor);
|
|
this.setHueLabel(hue);
|
|
this.hideDropdown();
|
|
|
|
// Dispatch a 'change' event with the selected hue value
|
|
this.dispatchEvent(new CustomEvent('change', { detail: { hue, rgb } }));
|
|
}
|
|
|
|
// Method to get the hue value of the selected item
|
|
getSelectedHue() {
|
|
return this.currentHue;
|
|
}
|
|
|
|
getSelectedRGB() {
|
|
const selectedColorText = this.shadowRoot.getElementById('selectedColor').textContent.trim();
|
|
return parseFloat(selectedColorText);
|
|
}
|
|
|
|
getRGBfromHue(hue){
|
|
if(hue == -1){
|
|
return '#FFFFFF';
|
|
}else if(hue == -2){
|
|
return '#000000';
|
|
}else{
|
|
return this.hslToRgb(hue, 100, 50);;
|
|
}
|
|
}
|
|
// Method to get the RGB value of the selected item
|
|
getSelectedRgb() {
|
|
const selectedHue = this.getSelectedHue();
|
|
return getRGBfromHue(selectedHue);
|
|
}
|
|
|
|
setHue(hue) {
|
|
// Round the input hue to the nearest 10th
|
|
let roundedHue = hue;
|
|
if(hue === -1 || hue === -2){
|
|
roundedHue = hue;
|
|
}else{
|
|
roundedHue = Math.round(hue / 10) * 10;
|
|
if (roundedHue >= 360) {
|
|
roundedHue = 359;
|
|
}
|
|
|
|
if (roundedHue < 0) {
|
|
roundedHue = 0;
|
|
}
|
|
}
|
|
|
|
this.currentHue = roundedHue;
|
|
|
|
this.colorList.forEach(colorVal => {
|
|
if (colorVal === roundedHue) {
|
|
this.handleColorSelection(roundedHue);
|
|
}
|
|
});
|
|
|
|
this.hideDropdown();
|
|
}
|
|
|
|
hideDropdown() {
|
|
const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
|
|
dropdownContent.style.display = 'none';
|
|
}
|
|
|
|
setHueLabel(value){
|
|
this.hueLabel.textContent = "Hue: " + value;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register the custom element <hue-select>
|
|
customElements.define('hue-select', HueSelect);
|
|
|