blob: da904d6daf529055c44bb847822558c79e236efe [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
/**
* Helper functions for an input with timeout.
*/
declare global {
interface HTMLElementEventMap {
'input-change': CustomEvent<string>;
}
}
type Constructor<T> = new (...args: any[]) => T;
export const InputMixin = dedupingMixin(
<T extends Constructor<PolymerElement>>(superClass: T): T&
Constructor<InputMixinInterface> => {
class InputMixin extends superClass {
static get properties() {
return {
lastValue_: {
type: String,
value: '',
}
};
}
private lastValue_: string|null;
/** Timeout used to delay processing of the input, in ms. */
private timeout_: number|null = null;
connectedCallback() {
super.connectedCallback();
this.getInput().addEventListener('input', () => this.resetTimeout_());
this.getInput().addEventListener(
'keydown', (e: KeyboardEvent) => this.onKeyDown_(e));
}
getInput() {
assertNotReached();
return document.createElement('input');
}
/**
* @return The delay to use for the timeout, in ms. Elements using
* this behavior must set this delay as data-timeout-delay on the
* input element returned by getInput().
*/
private getTimeoutDelayMs_(): number {
const delay = parseInt(this.getInput().dataset['timeoutDelay']!, 10);
assert(!Number.isNaN(delay));
return delay;
}
/**
* Called when a key is pressed on the input.
*/
private onKeyDown_(event: KeyboardEvent) {
if (event.code !== 'Enter' && event.code !== 'Tab') {
return;
}
this.resetAndUpdate();
}
/**
* Called when a input event occurs on the textfield. Starts an input
* timeout.
*/
private resetTimeout_() {
if (this.timeout_) {
clearTimeout(this.timeout_);
}
this.timeout_ =
setTimeout(() => this.onTimeout_(), this.getTimeoutDelayMs_());
}
/**
* Called after a timeout after user input into the textfield.
*/
private onTimeout_() {
this.timeout_ = null;
const value = this.getInput().value || '';
if (this.lastValue_ !== value) {
this.lastValue_ = value;
this.dispatchEvent(new CustomEvent(
'input-change',
{bubbles: true, composed: true, detail: value}));
}
}
resetString() {
this.lastValue_ = null;
}
resetAndUpdate() {
if (this.timeout_) {
clearTimeout(this.timeout_);
}
this.onTimeout_();
}
}
return InputMixin;
});
interface InputMixinInterface {
/**
* @return The cr-input or input element the behavior should use. Should be
* overridden by elements using this behavior.
*/
getInput(): (CrInputElement|HTMLInputElement);
// Resets the lastValue_ so that future inputs trigger a change event.
resetString(): void;
// Called to clear the timeout and update the value.
resetAndUpdate(): void;
}