blob: 126269b6db0c7bc635ba38eb4a8b207697e0578e [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Exports an element: saturation-value-picker
*
* @suppress {moduleLoad}
*/
import {lib} from '../../libdot/index.js';
import {LitElement, css, html} from './lit.js';
import './terminal_knob.js';
export const ARROW_KEY_OFFSET = 1;
export class SaturationValuePickerElement extends LitElement {
static get is() { return 'saturation-value-picker'; }
/** @override */
static get properties() {
return {
hue: {
type: Number,
},
saturation: {
type: Number,
reflect: true,
},
value: {
type: Number,
reflect: true,
},
};
}
/** @override */
static get styles() {
return css`
:host {
cursor: pointer;
display: block;
height: 160px;
position: relative;
width: 200px;
}
#picker {
left: 50%;
pointer-events: none;
position: absolute;
top: 50%;
z-index: 2;
}
#black-to-transparent, #white-to-pure {
border-radius: 8px;
height: 100%;
pointer-events: none;
position: absolute;
width: 100%;
z-index: 1;
}
#black-to-transparent {
background: linear-gradient(to top, #000000 0%, #00000000 100%);
z-index: 2;
}
`;
}
/** @override */
render() {
const whiteToPureStyle = `background: linear-gradient(to right, ` +
`#ffffff 0%, hsl(${this.hue}, 100%, 50%) 100%);`;
const color = lib.colors.arrayToHSLA(
lib.colors.hsvxArrayToHslaArray(
[this.hue, this.saturation, this.value]));
const pickerStyle = `left: ${this.saturation}%; ` +
`top: ${100 - this.value}%; background-color: ${color};`;
return html`
<div id="black-to-transparent"></div>
<div id="white-to-pure" style="${whiteToPureStyle}"></div>
<terminal-knob id="picker" tabindex="0" style="${pickerStyle}"
@keydown="${this.onKeydown_}">
</terminal-knob>
`;
}
constructor() {
super();
/** @private {number} */
this.hue;
/** @private {number} */
this.saturation;
/** @private {number} */
this.value;
}
/** @override */
connectedCallback() {
super.connectedCallback();
this.addEventListener('pointerdown', this.onPointerDown_);
this.addEventListener('pointerup', this.onPointerUp_);
}
/** @override */
disconnectedCallback() {
this.removeEventListener('pointerdown', this.onPointerDown_);
this.removeEventListener('pointerup', this.onPointerUp_);
super.disconnectedCallback();
}
/** @param {!Event} event */
onPointerDown_(event) {
this.addEventListener('pointermove', this.onPointerMove_);
this.setPointerCapture(event.pointerId);
this.onPointerEvent_(event);
}
/** @param {!Event} event */
onPointerMove_(event) {
this.onPointerEvent_(event);
}
/** @param {!Event} event */
onPointerUp_(event) {
this.removeEventListener('pointermove', this.onPointerMove_);
this.releasePointerCapture(event.pointerId);
this.onPointerEvent_(event);
this.shadowRoot.getElementById('picker').focus();
}
onKeydown_(event) {
switch (event.code) {
case 'ArrowLeft':
this.update_(this.saturation - ARROW_KEY_OFFSET, this.value);
event.preventDefault();
break;
case 'ArrowRight':
this.update_(this.saturation + ARROW_KEY_OFFSET, this.value);
event.preventDefault();
break;
case 'ArrowUp':
this.update_(this.saturation, this.value + ARROW_KEY_OFFSET);
event.preventDefault();
break;
case 'ArrowDown':
this.update_(this.saturation, this.value - ARROW_KEY_OFFSET);
event.preventDefault();
break;
}
}
/** @param {!Event} event */
onPointerEvent_(event) {
this.update_(100 * (event.offsetX / this.clientWidth),
100 * (1 - event.offsetY / this.clientHeight));
}
/**
* @param {number} saturation
* @param {number} value
*/
update_(saturation, value) {
this.saturation = lib.f.clamp(saturation, 0, 100);
this.value = lib.f.clamp(value, 0, 100);
this.dispatchEvent(new CustomEvent('change', {bubbles: true}));
}
}
customElements.define(SaturationValuePickerElement.is,
SaturationValuePickerElement);