diff --git a/textcomplete/__init__.py b/textcomplete/__init__.py index 215980c..7b3272d 100644 --- a/textcomplete/__init__.py +++ b/textcomplete/__init__.py @@ -139,6 +139,8 @@ def textcomplete( stop_enter_propagation: bool = False, dynamic_width: bool = True, dropdown_style: str = "", + debounce_enabled: bool = True, + debounce_timeout: int = 300, args: Optional[Tuple] = tuple(), key: Optional[str] = None, ) -> Optional[TextcompleteResult]: @@ -169,6 +171,8 @@ def textcomplete( strategies=[strategy.to_dict() for strategy in strategies], dropdown_option=dropdown_option, stop_enter_propagation=stop_enter_propagation, + debounce_enabled=debounce_enabled, + debounce_timeout=debounce_timeout, key=key, ) diff --git a/textcomplete/frontend/src/index.js b/textcomplete/frontend/src/index.js index c4f7804..042658c 100644 --- a/textcomplete/frontend/src/index.js +++ b/textcomplete/frontend/src/index.js @@ -2,6 +2,7 @@ import { Streamlit } from './streamlit'; import { Textcomplete } from '@textcomplete/core'; import { TextareaEditor } from '@textcomplete/textarea'; +import { debounce } from 'lodash.debounce'; import { parseTextcompleteLabel, parseTextcompleteCss, @@ -49,28 +50,42 @@ function onRender(event) { option.dropdown.parent = textareaElement.parentElement || rootElement; const strategies = parseTextcompleteStrategies(args); + const debounceEnabled = args.debounce_enabled !== false; + const debounceWait = args.debounce_wait || 300; + + // Create an instance of TextareaEditor const editor = new TextareaEditor(textareaElement); + + if (debounceEnabled) { + editor.onInput = debounce(editor.onInput.bind(editor), debounceWait); + + // Reattach the event listeners to use the debounced onInput + editor.stopListening(); + editor.startListening(); + } + const textcomplete = new Textcomplete(editor, strategies, option); // Mark the textarea as initialized textareaElement.textcompleteInitialized = true; - // Store the Textcomplete options on the textarea element for potential disposal + // Store the Textcomplete options on the textarea element textareaElement.setAttribute( 'data-textcomplete', JSON.stringify(event.detail.args.dropdown_option) ); - if (!!args.stop_enter_propagation) { + if (args.stop_enter_propagation) { textareaElement.setAttribute('data-textcomplete-stopenterpropagation', true); } - /** - * Adjust position of dropdown when rendered - */ + + // Adjust position of dropdown when rendered textcomplete.on('rendered', () => { const dropdownElement = textareaElement.parentElement.querySelector( '.textcomplete-dropdown' ); dropdownElement.style.top = '4px'; }); + + /** * Event handler for 'selected' event * @param {string} ename @@ -98,6 +113,8 @@ function onRender(event) { writable: false, value: textareaElement, }); + + // Create a new synthetic event object with the same properties and methods as the synthetic event object that is created by React const changeEvent = new Event('change', { bubbles: true,