// @ts-ignore: esbuild custom loader
import styles from './fields.pcss';
import { CUSTOM_EVENT_COLOR_ALPHA_CHANGED, CUSTOM_EVENT_COLOR_HSV_CHANGED, CUSTOM_EVENT_COLOR_HUE_CHANGED, sendAlphaCustomEvent, sendHsvCustomEvent } from '../../domain/events-provider';
import { TinyColor } from '@ctrl/tinycolor';
import { fixPercent, fixRGB, parseColor } from '../../domain/color-provider';
import { getUniqueId } from '../../domain/common-provider';
/*
Usage:
------
*/
class Fields extends HTMLElement {
// this id attribute is used for custom events
private cid: string;
private color: TinyColor = new TinyColor('#000');
private $hex: HTMLInputElement;
private $r: HTMLInputElement;
private $g: HTMLInputElement;
private $b: HTMLInputElement;
private $a: HTMLInputElement;
private hex = '';
private r = 0;
private g = 0;
private b = 0;
private a = 1;
static get observedAttributes() {
return ['color'];
}
constructor() {
super();
this.attachShadow({
mode: 'open', // 'closed', 'open',
});
this.hsvChanged = this.hsvChanged.bind(this);
this.hueChanged = this.hueChanged.bind(this);
this.alphaChanged = this.alphaChanged.bind(this);
this.onHexChange = this.onHexChange.bind(this);
this.render = this.render.bind(this);
this.onRedChange = this.onRedChange.bind(this);
this.onGreenChange = this.onGreenChange.bind(this);
this.onBlueChange = this.onBlueChange.bind(this);
this.onAlphaChange = this.onAlphaChange.bind(this);
this.onRedKeyDown = this.onRedKeyDown.bind(this);
this.onBlueKeyDown = this.onBlueKeyDown.bind(this);
this.onGreenKeyDown = this.onGreenKeyDown.bind(this);
this.onAlphaKeyDown = this.onAlphaKeyDown.bind(this);
}
hueChanged(evt: CustomEvent) {
if (!evt || !evt.detail || !evt.detail.cid) return;
// handle only current instance
if (evt.detail.cid !== this.cid) return;
const hsv = this.color.toHsv();
this.color = new TinyColor({
h: Number(evt.detail.h),
s: hsv.s,
v: hsv.v,
a: hsv.a,
});
this.render();
}
alphaChanged(evt: CustomEvent) {
if (!evt || !evt.detail || !evt.detail.cid) return;
// handle only current instance
if (evt.detail.cid !== this.cid) return;
const rgba = this.color.toRgb();
rgba.a = evt.detail.a;
this.color = new TinyColor(rgba);
this.render();
}
hsvChanged(evt: CustomEvent) {
if (!evt || !evt.detail || !evt.detail.cid) return;
// handle only current instance
if (evt.detail.cid !== this.cid) return;
this.color = new TinyColor({
h: evt.detail.h,
s: evt.detail.s,
v: evt.detail.v,
a: this.color.toHsv().a,
});
this.render();
}
render() {
const rgba = this.color.toRgb();
this.r = rgba.r;
this.g = rgba.g;
this.b = rgba.b;
this.a = rgba.a;
this.hex = this.color.toHex();
if (this.$hex && this.shadowRoot?.activeElement !== this.$hex) {
this.$hex.value = this.hex.toUpperCase();
}
if (this.$r && this.shadowRoot?.activeElement !== this.$r) {
this.$r.value = this.r.toString();
}
if (this.$g && this.shadowRoot?.activeElement !== this.$g) {
this.$g.value = this.g.toString();
}
if (this.$b && this.shadowRoot?.activeElement !== this.$b) {
this.$b.value = this.b.toString();
}
if (this.$a && this.shadowRoot?.activeElement !== this.$a) {
this.$a.value = Math.round(this.a * 100).toString();
}
}
onFieldKeyDown(evt: KeyboardEvent, type: string) {
const rgba = this.color.toRgb();
switch (evt.key) {
case 'ArrowUp': {
if (type === 'r') {
this.r = Math.min(255, rgba.r + 1);
rgba.r = this.r;
const hsv = new TinyColor(rgba).toHsv();
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
this.$r.value = this.r.toString();
this.render();
}
if (type === 'g') {
this.g = Math.min(255, rgba.g + 1);
rgba.g = this.g;
const hsv = new TinyColor(rgba).toHsv();
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
this.$g.value = this.g.toString();
this.render();
}
if (type === 'b') {
this.b = Math.min(255, rgba.b + 1);
rgba.b = this.b;
const hsv = new TinyColor(rgba).toHsv();
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
this.$b.value = this.b.toString();
this.render();
}
if (type === 'a') {
this.a = Math.max(0, Math.min(this.a + 0.01, 1));
this.$a.value = Math.round(this.a * 100).toString();
const rgba = this.color.toRgb();
rgba.a = this.a;
this.color = new TinyColor(rgba);
this.render();
sendAlphaCustomEvent(this.cid, this.a);
}
break;
}
case 'ArrowDown': {
if (type === 'r') {
this.r = Math.max(0, rgba.r - 1);
rgba.r = this.r;
const hsv = new TinyColor(rgba).toHsv();
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
this.$r.value = this.r.toString();
this.render();
}
if (type === 'g') {
this.g = Math.max(0, rgba.g - 1);
rgba.g = this.g;
const hsv = new TinyColor(rgba).toHsv();
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
this.$g.value = this.g.toString();
this.render();
}
if (type === 'b') {
this.b = Math.max(0, rgba.b - 1);
rgba.b = this.b;
const hsv = new TinyColor(rgba).toHsv();
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
this.$b.value = this.b.toString();
this.render();
}
if (type === 'a') {
this.a = Math.max(0, this.a - 0.01);
this.$a.value = Math.round(this.a * 100).toString();
const rgba = this.color.toRgb();
rgba.a = this.a;
this.color = new TinyColor(rgba);
this.render();
sendAlphaCustomEvent(this.cid, this.a);
}
break;
}
case 'Escape': {
if (this.shadowRoot?.activeElement) {
const $el = this.shadowRoot.activeElement as HTMLElement;
$el.blur();
}
this.render();
break;
}
case 'Enter': {
if (this.shadowRoot?.activeElement) {
const $el = this.shadowRoot.activeElement as HTMLElement;
$el.blur();
}
this.render();
break;
}
}
}
onRedKeyDown(evt: KeyboardEvent) {
this.onFieldKeyDown(evt, 'r');
}
onGreenKeyDown(evt: KeyboardEvent) {
this.onFieldKeyDown(evt, 'g');
}
onBlueKeyDown(evt: KeyboardEvent) {
this.onFieldKeyDown(evt, 'b');
}
onAlphaKeyDown(evt: KeyboardEvent) {
this.onFieldKeyDown(evt, 'a');
}
onHexChange(evt: Event) {
const $target = evt.target as HTMLInputElement;
if ($target.value.length !== 6) return;
const updatedColor = new TinyColor(`#${$target.value}`);
if (updatedColor.isValid) {
this.color = updatedColor;
const hsv = this.color.toHsv();
// update outer color to change the button, and
// send the updated color to the user
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
}
}
onRedChange(evt: Event) {
const $target = evt.target as HTMLInputElement;
const fixedValue = fixRGB($target.value);
if (fixedValue.toString() === $target.value) {
const rgba = this.color.toRgb();
rgba.r = fixedValue;
const hsv = new TinyColor(rgba).toHsv();
// update outer color to change the button, and
// send the updated color to the user
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
}
}
onGreenChange(evt: Event) {
const $target = evt.target as HTMLInputElement;
const fixedValue = fixRGB($target.value);
if (fixedValue.toString() === $target.value) {
const rgba = this.color.toRgb();
rgba.g = fixedValue;
const hsv = new TinyColor(rgba).toHsv();
// update outer color to change the button, and
// send the updated color to the user
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
}
}
onBlueChange(evt: Event) {
const $target = evt.target as HTMLInputElement;
const fixedValue = fixRGB($target.value);
if (fixedValue.toString() === $target.value) {
const rgba = this.color.toRgb();
rgba.b = fixedValue;
const hsv = new TinyColor(rgba).toHsv();
// update outer color to change the button, and
// send the updated color to the user
sendHsvCustomEvent(this.cid, hsv.h, hsv.s, hsv.v);
}
}
onAlphaChange(evt: Event) {
const $target = evt.target as HTMLInputElement;
const fixedValue = fixPercent($target.value);
if (fixedValue.toString() === $target.value) {
sendAlphaCustomEvent(this.cid, fixedValue / 100);
}
}
/**
* when the custom element connected to DOM
*/
connectedCallback() {
if (!this.shadowRoot) return;
this.cid = this.getAttribute('cid') || '';
this.color = parseColor(this.getAttribute('color'));
const rgba = this.color.toRgb();
this.r = rgba.r;
this.g = rgba.g;
this.b = rgba.b;
this.a = rgba.a;
this.hex = this.color.toHex();
const hexId = getUniqueId();
const rId = getUniqueId();
const gId = getUniqueId();
const bId = getUniqueId();
const aId = getUniqueId();
this.shadowRoot.innerHTML = `
`;
this.$hex = this.shadowRoot.getElementById(`hex-${hexId}`) as HTMLInputElement;
this.$r = this.shadowRoot.getElementById(`r-${rId}`) as HTMLInputElement;
this.$g = this.shadowRoot.getElementById(`g-${gId}`) as HTMLInputElement;
this.$b = this.shadowRoot.getElementById(`b-${bId}`) as HTMLInputElement;
this.$a = this.shadowRoot.getElementById(`a-${aId}`) as HTMLInputElement;
// custom event from other parts of the app
document.addEventListener(CUSTOM_EVENT_COLOR_HSV_CHANGED, this.hsvChanged);
document.addEventListener(CUSTOM_EVENT_COLOR_HUE_CHANGED, this.hueChanged);
document.addEventListener(CUSTOM_EVENT_COLOR_ALPHA_CHANGED, this.alphaChanged);
this.$hex.addEventListener('input', this.onHexChange);
this.$r.addEventListener('input', this.onRedChange);
this.$g.addEventListener('input', this.onGreenChange);
this.$b.addEventListener('input', this.onBlueChange);
this.$a.addEventListener('input', this.onAlphaChange);
this.$hex.addEventListener('blur', this.render);
this.$r.addEventListener('blur', this.render);
this.$g.addEventListener('blur', this.render);
this.$b.addEventListener('blur', this.render);
this.$a.addEventListener('blur', this.render);
this.$r.addEventListener('keydown', this.onRedKeyDown);
this.$g.addEventListener('keydown', this.onGreenKeyDown);
this.$b.addEventListener('keydown', this.onBlueKeyDown);
this.$a.addEventListener('keydown', this.onAlphaKeyDown);
}
/**
* when the custom element disconnected from DOM
*/
disconnectedCallback() {
document.removeEventListener(CUSTOM_EVENT_COLOR_HSV_CHANGED, this.hsvChanged);
document.removeEventListener(CUSTOM_EVENT_COLOR_HUE_CHANGED, this.hueChanged);
document.removeEventListener(CUSTOM_EVENT_COLOR_ALPHA_CHANGED, this.alphaChanged);
this.$hex.removeEventListener('input', this.onHexChange);
this.$r.removeEventListener('input', this.onRedChange);
this.$g.removeEventListener('input', this.onGreenChange);
this.$b.removeEventListener('input', this.onBlueChange);
this.$a.removeEventListener('input', this.onAlphaChange);
this.$hex.removeEventListener('blur', this.render);
this.$r.removeEventListener('blur', this.render);
this.$g.removeEventListener('blur', this.render);
this.$b.removeEventListener('blur', this.render);
this.$a.removeEventListener('blur', this.render);
this.$r.removeEventListener('keydown', this.onRedKeyDown);
this.$g.removeEventListener('keydown', this.onGreenKeyDown);
this.$b.removeEventListener('keydown', this.onBlueKeyDown);
this.$a.removeEventListener('keydown', this.onAlphaKeyDown);
}
/**
* when attributes change
*/
attributeChangedCallback(_attrName: string, _oldVal: string, newVal: string) {
this.color = parseColor(newVal);
this.render();
}
}
export default Fields;