diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index 0d1f975c3d..f67837c764 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -1155,6 +1155,7 @@ declare global { checked?: boolean; clearInput?: boolean; clearOnEdit?: boolean; + debounce?: number; disabled?: boolean; inputmode?: string; max?: string; @@ -2880,6 +2881,7 @@ declare global { autofocus?: boolean; clearOnEdit?: boolean; cols?: number; + debounce?: number; disabled?: boolean; maxlength?: number; minlength?: number; diff --git a/packages/core/src/components/input/input.tsx b/packages/core/src/components/input/input.tsx index b7569d1051..c4cb705d1e 100644 --- a/packages/core/src/components/input/input.tsx +++ b/packages/core/src/components/input/input.tsx @@ -1,5 +1,6 @@ import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core'; +import { debounce } from '../../utils/helpers'; import { createThemedClasses } from '../../utils/theme'; import { InputComponent } from './input-base'; @@ -23,6 +24,11 @@ export class Input implements InputComponent { @Element() private el: HTMLElement; + /** + * @output {Event} Emitted when the input value has changed. + */ + @Event() ionInput: EventEmitter; + /** * @output {Event} Emitted when the styles change. */ @@ -83,6 +89,18 @@ export class Input implements InputComponent { */ @Prop({ mutable: true }) clearOnEdit: boolean; + /** + * @input {number} Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `0`. + */ + @Prop() debounce: number = 0; + @PropDidChange('debounce') + private debounceInput() { + this.ionInput.emit = debounce( + this.ionInput.emit.bind(this.ionInput), + this.debounce + ); + } + /** * @input {boolean} If true, the user cannot interact with the input. Defaults to `false`. */ @@ -192,6 +210,7 @@ export class Input implements InputComponent { componentDidLoad() { + this.debounceInput(); this.emitStyle(); // By default, password inputs clear after focus when they have content @@ -225,6 +244,7 @@ export class Input implements InputComponent { inputChanged(ev: any) { this.value = ev.target && ev.target.value; + this.ionInput.emit(ev); this.emitStyle(); } @@ -242,15 +262,15 @@ export class Input implements InputComponent { } } - inputKeydown() { - this.checkClearOnEdit(); + inputKeydown(ev: any) { + this.checkClearOnEdit(ev); } /** * Check if we need to clear the text input if clearOnEdit is enabled */ - checkClearOnEdit() { + checkClearOnEdit(ev: any) { if (!this.clearOnEdit) { return; } @@ -258,15 +278,16 @@ export class Input implements InputComponent { // Did the input value change after it was blurred and edited? if (this.didBlurAfterEdit && this.hasValue()) { // Clear the input - this.clearTextInput(); + this.clearTextInput(ev); } // Reset the flag this.didBlurAfterEdit = false; } - clearTextInput() { + clearTextInput(ev: any) { this.value = ''; + this.ionInput.emit(ev); } hasFocus(): boolean { diff --git a/packages/core/src/components/input/readme.md b/packages/core/src/components/input/readme.md index 0b411eafe0..7cdb954c1f 100644 --- a/packages/core/src/components/input/readme.md +++ b/packages/core/src/components/input/readme.md @@ -47,6 +47,11 @@ boolean boolean +#### debounce + +number + + #### disabled boolean @@ -179,6 +184,11 @@ boolean boolean +#### debounce + +number + + #### disabled boolean @@ -277,6 +287,9 @@ string #### ionFocus +#### ionInput + + #### ionStyle diff --git a/packages/core/src/components/range/range.tsx b/packages/core/src/components/range/range.tsx index 8c756fe3e7..1d068fe571 100644 --- a/packages/core/src/components/range/range.tsx +++ b/packages/core/src/components/range/range.tsx @@ -1,6 +1,6 @@ import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core'; import { BaseInputComponent, GestureDetail } from '../../index'; -import { clamp } from '../../utils/helpers'; +import { clamp, debounce } from '../../utils/helpers'; @Component({ tag: 'ion-range', @@ -75,6 +75,13 @@ export class Range implements BaseInputComponent { * `ionChange` event after each change in the range value. Default `0`. */ @Prop() debounce: number = 0; + @PropDidChange('debounce') + private debounceChange() { + this.ionChange.emit = debounce( + this.ionChange.emit.bind(this.ionChange), + this.debounce + ); + } /* * @input {boolean} If true, the user cannot interact with the range. Default false. @@ -126,13 +133,14 @@ export class Range implements BaseInputComponent { @PropDidChange('value') protected valueChanged(val: boolean) { - setTimeout(() => this.ionChange.emit({value: val}), this.debounce); + this.ionChange.emit({value: val}); this.emitStyle(); } componentWillLoad() { this.inputUpdated(); this.createTicks(); + this.debounceChange(); this.emitStyle(); } diff --git a/packages/core/src/components/searchbar/searchbar.tsx b/packages/core/src/components/searchbar/searchbar.tsx index 7c160db6cc..d119a0fd04 100644 --- a/packages/core/src/components/searchbar/searchbar.tsx +++ b/packages/core/src/components/searchbar/searchbar.tsx @@ -1,4 +1,5 @@ -import { Component, Element, Event, EventEmitter, Prop, State } from '@stencil/core'; +import { Component, Element, Event, EventEmitter, Prop, PropDidChange, State } from '@stencil/core'; +import { debounce } from '../../utils/helpers'; @Component({ @@ -86,6 +87,13 @@ export class Searchbar { * @input {number} Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `250`. */ @Prop({ mutable: true }) debounce: number = 250; + @PropDidChange('debounce') + private debounceInput() { + this.ionInput.emit = debounce( + this.ionInput.emit.bind(this.ionInput), + this.debounce + ); + } /** * @input {string} Set the input's placeholder. Default `"Search"`. @@ -115,6 +123,7 @@ export class Searchbar { componentDidLoad() { this.positionElements(); + this.debounceInput(); } /** @@ -153,9 +162,7 @@ export class Searchbar { */ inputChanged(ev: any) { this.value = ev.target.value; - setTimeout(() => { - this.ionInput.emit(ev); - }, this.debounce); + this.ionInput.emit(ev); } inputUpdated() { diff --git a/packages/core/src/components/textarea/readme.md b/packages/core/src/components/textarea/readme.md index 51eb673077..1c067c18ff 100644 --- a/packages/core/src/components/textarea/readme.md +++ b/packages/core/src/components/textarea/readme.md @@ -66,6 +66,11 @@ boolean number +#### debounce + +number + + #### disabled boolean @@ -148,6 +153,11 @@ boolean number +#### debounce + +number + + #### disabled boolean @@ -211,6 +221,9 @@ string #### ionFocus +#### ionInput + + #### ionStyle diff --git a/packages/core/src/components/textarea/textarea.tsx b/packages/core/src/components/textarea/textarea.tsx index 8247c35ce5..28b49003cf 100644 --- a/packages/core/src/components/textarea/textarea.tsx +++ b/packages/core/src/components/textarea/textarea.tsx @@ -1,5 +1,6 @@ import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core'; +import { debounce } from '../../utils/helpers'; import { createThemedClasses } from '../../utils/theme'; import { TextareaComponent } from '../input/input-base'; @@ -29,6 +30,11 @@ export class Textarea implements TextareaComponent { @Element() private el: HTMLElement; + /** + * @output {Event} Emitted when the input value has changed. + */ + @Event() ionInput: EventEmitter; + /** * @output {Event} Emitted when the styles change. */ @@ -64,6 +70,18 @@ export class Textarea implements TextareaComponent { */ @Prop({ mutable: true }) clearOnEdit: boolean; + /** + * @input {number} Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `0`. + */ + @Prop() debounce: number = 0; + @PropDidChange('debounce') + private debounceInput() { + this.ionInput.emit = debounce( + this.ionInput.emit.bind(this.ionInput), + this.debounce + ); + } + /** * @input {boolean} If true, the user cannot interact with the textarea. Defaults to `false`. */ @@ -141,6 +159,7 @@ export class Textarea implements TextareaComponent { } componentDidLoad() { + this.debounceInput(); this.emitStyle(); } @@ -160,8 +179,9 @@ export class Textarea implements TextareaComponent { }); } - clearTextInput() { + clearTextInput(ev: any) { this.value = ''; + this.ionInput.emit(ev); } inputBlurred(ev: any) { @@ -173,6 +193,7 @@ export class Textarea implements TextareaComponent { inputChanged(ev: any) { this.value = ev.target && ev.target.value; + this.ionInput.emit(ev); this.emitStyle(); } @@ -183,14 +204,14 @@ export class Textarea implements TextareaComponent { this.emitStyle(); } - inputKeydown() { - this.checkClearOnEdit(); + inputKeydown(ev: any) { + this.checkClearOnEdit(ev); } /** * Check if we need to clear the text input if clearOnEdit is enabled */ - checkClearOnEdit() { + checkClearOnEdit(ev: any) { if (!this.clearOnEdit) { return; } @@ -198,7 +219,7 @@ export class Textarea implements TextareaComponent { // Did the input value change after it was blurred and edited? if (this.didBlurAfterEdit && this.hasValue()) { // Clear the input - this.clearTextInput(); + this.clearTextInput(ev); } // Reset the flag diff --git a/packages/core/src/utils/helpers.ts b/packages/core/src/utils/helpers.ts index bd4a2e3193..3ee36eea0b 100644 --- a/packages/core/src/utils/helpers.ts +++ b/packages/core/src/utils/helpers.ts @@ -295,3 +295,11 @@ export function domControllerAsync(domControllerFunction: Function, callback?: F }); }); } + +export function debounce(func: Function, wait: number = 250) { + let timer: number; + return (...args: any[]): void => { + clearTimeout(timer); + timer = setTimeout(func, wait, ...args); + }; +}