@@ -5,7 +5,7 @@ import { useEffect, useMemo, useState, useCallback, useRef } from "react";
55import type { RefCallback , FormEvent } from "react" ;
66import { runes } from "runes2" ;
77import type { Rule } from "antd/es/form" ;
8- import { forEach , isObject , omitBy } from "lodash-es" ;
8+ import { forEach , isObject , omitBy , debounce } from "lodash-es" ;
99import IMask , { type InputMask } from "imask" ;
1010import type { EnsembleWidgetProps } from "../../shared/types" ;
1111import { WidgetRegistry } from "../../registry" ;
@@ -29,7 +29,9 @@ export type TextInputProps = {
2929 "none" | "enforced" | "truncateAfterCompositionEnds"
3030 > ;
3131 inputType ?: "email" | "phone" | "number" | "text" | "url" ; //| "ipAddress";
32- onChange ?: EnsembleAction ;
32+ onChange ?: {
33+ debounceMs ?: number ;
34+ } & EnsembleAction ;
3335 mask ?: string ;
3436 validator ?: {
3537 minLength ?: number ;
@@ -61,12 +63,20 @@ export const TextInput: React.FC<TextInputProps> = (props) => {
6163 const action = useEnsembleAction ( props . onChange ) ;
6264 const onKeyDownAction = useEnsembleAction ( props . onKeyDown ) ;
6365
66+ const debouncedOnChange = useMemo (
67+ ( ) =>
68+ debounce ( ( inputValue : string ) => {
69+ action ?. callback ( { value : inputValue } ) ;
70+ } , values ?. onChange ?. debounceMs ?? 0 ) ,
71+ [ action ?. callback , values ?. onChange ?. debounceMs ] ,
72+ ) ;
73+
6474 const handleChange = useCallback (
6575 ( newValue : string ) => {
6676 setValue ( newValue ) ;
67- action ?. callback ( { value : newValue } ) ;
77+ debouncedOnChange ( newValue ) ;
6878 } ,
69- [ action ?. callback ] ,
79+ [ debouncedOnChange ] ,
7080 ) ;
7181
7282 const handleRef : RefCallback < never > = ( node ) => {
@@ -123,6 +133,19 @@ export const TextInput: React.FC<TextInputProps> = (props) => {
123133 }
124134 } , [ values ?. mask ] ) ;
125135
136+ // cleanup debounced function when component unmounts or changes
137+ useEffect ( ( ) => {
138+ return ( ) => {
139+ if (
140+ debouncedOnChange &&
141+ typeof debouncedOnChange === "function" &&
142+ "cancel" in debouncedOnChange
143+ ) {
144+ ( debouncedOnChange as ReturnType < typeof debounce > ) . cancel ( ) ;
145+ }
146+ } ;
147+ } , [ debouncedOnChange ] ) ;
148+
126149 const inputType = useMemo ( ( ) => {
127150 switch ( values ?. inputType ) {
128151 case "email" :
0 commit comments