Files
navi/public/components/btn.mjs
ztimson 7b2621c264
All checks were successful
Build and publish / Build Container (push) Successful in 1m44s
Refactored code formatting to use consistent indentation and object destructuring across client and server files.
2026-03-02 12:28:18 -05:00

186 lines
4.8 KiB
JavaScript

function contrast(color) {
const exploded = color?.match(color.length >= 6 ? /[0-9a-fA-F]{2}/g : /[0-9a-fA-F]/g);
if(!exploded || exploded?.length < 3) return 'black';
const [r, g, b] = exploded.map(hex => parseInt(hex.length === 1 ? `${hex}${hex}` : hex, 16));
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
return luminance > 0.5 ? 'black' : 'white';
}
function shadeColor(hex, amount) {
function dec2Hex(num) {
const hex = Math.round(num * 255).toString(16);
return hex.length === 1 ? '0' + hex : hex;
}
function hex2Int(hex) {
let r = 0,
g = 0,
b = 0;
if(hex.length === 4) {
r = parseInt(hex[1] + hex[1], 16);
g = parseInt(hex[2] + hex[2], 16);
b = parseInt(hex[3] + hex[3], 16);
} else {
r = parseInt(hex.slice(1, 3), 16);
g = parseInt(hex.slice(3, 5), 16);
b = parseInt(hex.slice(5, 7), 16);
}
return {
r,
g,
b
};
}
function 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;
}
function int2Hex(r, g, b) {
return '#' + dec2Hex(r) + dec2Hex(g) + dec2Hex(b);
}
let {
r,
g,
b
} = hex2Int(hex);
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b),
min = Math.min(r, g, b);
let h,
s,
l = (max + min) / 2;
if(max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
default:
h = 0;
break;
}
h /= 6;
}
l = Math.max(0, Math.min(1, l + amount));
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
return int2Hex(hue2rgb(p, q, h + 1 / 3), hue2rgb(p, q, h), hue2rgb(p, q, h - 1 / 3));
}
class BtnComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<style>
:host {
--base-color: #ccc;
--dark-color: #999;
--light-color: #eee;
--text-color: #000;
}
.btn {
display: inline-flex;
min-width: 40px;
width: 100%;
height: 40px;
padding: 5px;
border-radius: 6px;
font-size: 20px;
transition: transform 0.1s, background-color 0.2s, border-color 0.2s, box-shadow 0.2s;
align-items: center;
justify-content: center;
user-select: none;
cursor: url('/assets/cursor.png'), auto;
fill: var(--text-color);
color: var(--text-color);
box-sizing: border-box;
}
.btn:not(.disabled) {
background: var(--base-color);
border: 2px solid var(--dark-color);
box-shadow: 0 4px 0 var(--dark-color);
}
.btn:not(.disabled):hover {
transform: translateY(-2px);
background: var(--light-color);
border: 2px solid var(--base-color);
box-shadow: 0 6px 0 var(--base-color);
}
.btn:not(.disabled):active {
transform: translateY(2px);
background: var(--base-color);
border: 2px solid var(--dark-color);
box-shadow: 0 2px 0 var(--dark-color);
}
.btn.disabled {
cursor: no-drop;
background: var(--dark-color);
border: 2px solid var(--base-color);
box-shadow: 0 4px 0 var(--dark-color);
}
</style>
<div class="btn">
<slot></slot>
</div>
`;
this.shadowRoot.querySelector('.btn').addEventListener('click', (e) => {
if(this.hasAttribute('disabled')) {
e.stopPropagation();
e.preventDefault();
}
});
this.updateColors();
}
static get observedAttributes() {
return ['color', 'disabled'];
}
attributeChangedCallback(name, oldValue, newValue) {
if(name === 'color') this.updateColors();
if(name === 'disabled') {
const disabled = this.hasAttribute('disabled');
this.shadowRoot.querySelector('.btn').classList[disabled ? 'add' : 'remove']('disabled');
}
}
updateColors() {
const hex = this.getAttribute('color');
this.shadowRoot.host.style.setProperty('--base-color', hex);
this.shadowRoot.host.style.setProperty('--dark-color', shadeColor(hex, -.1));
this.shadowRoot.host.style.setProperty('--light-color', shadeColor(hex, .1));
this.shadowRoot.host.style.setProperty('--text-color', contrast(hex));
}
}
customElements.define('btn-component', BtnComponent);