blob: 2234bd4911a455fb13cd15e0348db305de0302a4 [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: terminal-slider
*
* @suppress {moduleLoad}
*/
import {lib} from '../../libdot/index.js';
import {LitElement, css, html} from './lit.js';
import {CHROME_VERSION} from './terminal_common.js';
export const ARROW_KEY_OFFSET = 0.01;
/** A slider element. It dispatches "change" event when the value changes. The
* `value` ranges from 0 to 1. */
export class TerminalSlider extends LitElement {
/** @override */
static get properties() {
return {
value: {
type: Number,
reflect: true,
},
// For slotted elements (e.g. <terminal-knob>) to style itself.
focusVisible: {
type: Boolean,
reflect: true,
},
};
}
/** @override */
static get styles() {
return css`
:host {
cursor: pointer;
display: block;
height: 16px;
position: relative;
}
#knob-container {
outline: none;
pointer-events: none;
position: absolute;
top: 50%;
z-index: 2;
}
`;
}
/** @override */
render() {
return html`
<slot></slot>
<div id="knob-container" tabindex=0 style="left: ${100 * this.value}%;"
@keydown="${this.onKeydown_}"
@focus="${this.onFocus_}"
@blur="${() => this.focusVisible = false}">
<slot name="knob"></slot>
</div>
`;
}
constructor() {
super();
/** @public {number} */
this.value;
/** @public {boolean} */
this.focusVisible;
}
/** @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();
}
onFocus_() {
this.focusVisible = this.shadowRoot.getElementById(
'knob-container').matches(
CHROME_VERSION >= 87 ? ':focus-visible' : ':focus');
}
onKeydown_(event) {
switch (event.code) {
case 'ArrowLeft':
case 'ArrowUp':
this.update_(this.value - ARROW_KEY_OFFSET);
event.preventDefault();
break;
case 'ArrowRight':
case 'ArrowDown':
this.update_(this.value + ARROW_KEY_OFFSET);
event.preventDefault();
break;
}
}
/** @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('knob-container').focus();
}
/** @param {!Event} event */
onPointerEvent_(event) {
this.update_(event.offsetX / this.clientWidth);
}
update_(value) {
this.value = lib.f.clamp(value, 0, 1);
this.dispatchEvent(new CustomEvent('change', {bubbles: true}));
}
}
customElements.define('terminal-slider', TerminalSlider);