11import eventsEngine from '@js/common/core/events/core/events_engine' ;
22import scrollEvents from '@js/common/core/events/gesture/emitter.gesture.scroll' ;
33import pointerEvents from '@js/common/core/events/pointer' ;
4- import { addNamespace , eventData } from '@js/common/core/events/utils/index' ;
4+ import { addNamespace , eventData , normalizeKeyName } from '@js/common/core/events/utils/index' ;
55import registerComponent from '@js/core/component_registrator' ;
66import type { dxElementWrapper } from '@js/core/renderer' ;
77import $ from '@js/core/renderer' ;
@@ -32,12 +32,22 @@ class TextArea<
3232> extends TextBox < TProperties > {
3333 _eventY ! : number ;
3434
35+ _$suggestionInput ?: dxElementWrapper ;
36+
37+ _currentSuggestion ?: string ;
38+
39+ _isSuggestionVisible = false ;
40+
3541 _getDefaultOptions ( ) : TProperties {
3642 return {
3743 ...super . _getDefaultOptions ( ) ,
3844 spellcheck : true ,
3945 autoResizeEnabled : false ,
4046 _shouldAttachKeyboardEvents : false ,
47+ smartSuggestionEnabled : false ,
48+ userRole : '' ,
49+ suggestionLength : 100 ,
50+ onSuggestionRequest : undefined ,
4151 } ;
4252 }
4353
@@ -55,6 +65,10 @@ class TextArea<
5565 this . $element ( ) . addClass ( TEXTAREA_CLASS ) ;
5666 super . _initMarkup ( ) ;
5767 this . setAria ( 'multiline' , 'true' ) ;
68+
69+ if ( this . option ( 'smartSuggestionEnabled' ) ) {
70+ this . $element ( ) . addClass ( 'dx-textarea-smart' ) ;
71+ }
5872 }
5973
6074 _renderContentImpl ( ) : void {
@@ -65,6 +79,10 @@ class TextArea<
6579 _renderInput ( ) : void {
6680 super . _renderInput ( ) ;
6781 this . _renderScrollHandler ( ) ;
82+
83+ if ( this . option ( 'smartSuggestionEnabled' ) ) {
84+ this . _renderSuggestionTextArea ( ) ;
85+ }
6886 }
6987
7088 _createInput ( ) : dxElementWrapper {
@@ -90,6 +108,34 @@ class TextArea<
90108 eventsEngine . on ( $input , addNamespace ( pointerEvents . move , this . NAME ) , this . _pointerMoveHandler . bind ( this ) ) ;
91109 }
92110
111+ _renderAdditionalKeyDownHandler ( ) : void {
112+ const $input = this . _input ( ) ;
113+
114+ if ( this . option ( 'smartSuggestionEnabled' ) ) {
115+ // @ts -expect-error ts-error
116+ eventsEngine . on ( $input , addNamespace ( 'scroll' , this . NAME ) , this . _syncScroll . bind ( this ) ) ;
117+ // @ts -expect-error ts-error
118+ eventsEngine . on ( $input , addNamespace ( 'keydown' , this . NAME ) , this . _keyDownHandler . bind ( this ) ) ;
119+ }
120+ }
121+
122+ _keyDownHandler ( e ) : void {
123+ super . _keyDownHandler ( e ) ;
124+
125+ if ( ! this . option ( 'smartSuggestionEnabled' ) ) {
126+ return ;
127+ }
128+
129+ const keyName = normalizeKeyName ( e ) ;
130+
131+ if ( keyName === 'tab' && this . _currentSuggestion && this . _isSuggestionVisible ) {
132+ e . preventDefault ( ) ;
133+ this . _acceptSuggestion ( ) ;
134+ } else if ( keyName === 'escape' && this . _currentSuggestion && this . _isSuggestionVisible ) {
135+ this . _rejectSuggestion ( ) ;
136+ }
137+ }
138+
93139 _pointerDownHandler ( e ) : void {
94140 this . _eventY = eventData ( e ) . y ;
95141 }
@@ -138,9 +184,22 @@ class TextArea<
138184 eventsEngine . on ( this . _input ( ) , addNamespace ( 'input paste' , this . NAME ) , this . _updateInputHeight . bind ( this ) ) ;
139185 }
140186
187+ if ( this . option ( 'smartSuggestionEnabled' ) ) {
188+ // @ts -expect-error ts-error
189+ eventsEngine . on ( this . _input ( ) , addNamespace ( 'input paste' , this . NAME ) , this . _inputHandler . bind ( this ) ) ;
190+ }
191+
141192 super . _renderEvents ( ) ;
142193 }
143194
195+ _inputHandler ( ) : void {
196+ if ( this . option ( 'smartSuggestionEnabled' ) ) {
197+ const { text = '' } = this . option ( ) ;
198+
199+ this . _requestSuggestion ( text ) ;
200+ }
201+ }
202+
144203 _refreshEvents ( ) : void {
145204 // @ts -expect-error ts-error
146205 eventsEngine . off ( this . _input ( ) , addNamespace ( 'input paste' , this . NAME ) ) ;
@@ -213,6 +272,10 @@ class TextArea<
213272 if ( autoHeightResizing ) {
214273 this . $element ( ) . css ( 'height' , 'auto' ) ;
215274 }
275+
276+ if ( this . option ( 'smartSuggestionEnabled' ) ) {
277+ this . _syncDimensions ( ) ;
278+ }
216279 }
217280
218281 // eslint-disable-next-line class-methods-use-this
@@ -264,6 +327,118 @@ class TextArea<
264327 }
265328 }
266329
330+ // eslint-disable-next-line class-methods-use-this
331+ _createSuggestionInput ( ) : dxElementWrapper {
332+ const $suggestionInput = $ ( '<textarea>' ) ;
333+
334+ // @ts -expect-error ts-error
335+ $suggestionInput . attr ( {
336+ readonly : 'readonly' ,
337+ tabindex : '-1' ,
338+ 'aria-hidden' : 'true' ,
339+ autocomplete : 'off' ,
340+ spellcheck : 'false' ,
341+ 'aria-label' : 'suggestion textarea' ,
342+ } ) ;
343+
344+ return $suggestionInput ;
345+ }
346+
347+ _renderSuggestionTextArea ( ) : void {
348+ this . _$suggestionInput = this . _createSuggestionInput ( ) ;
349+ this . _$suggestionInput
350+ . addClass ( 'dx-textarea-suggestion' )
351+ . addClass ( 'dx-texteditor-input' )
352+ . appendTo ( this . _$textEditorInputContainer ) ;
353+
354+ this . _syncDimensions ( ) ;
355+ this . _renderAdditionalKeyDownHandler ( ) ;
356+ }
357+
358+ _disposeSuggestionTextArea ( ) : void {
359+ if ( this . _$suggestionInput ) {
360+ eventsEngine . off ( this . _$suggestionInput ) ;
361+ this . _$suggestionInput . remove ( ) ;
362+ this . _$suggestionInput = undefined ;
363+ this . _currentSuggestion = undefined ;
364+ this . _isSuggestionVisible = false ;
365+ }
366+ }
367+
368+ _syncScroll ( ) : void {
369+ // sync scroll on big suggestion or input resize
370+ }
371+
372+ _syncDimensions ( ) : void {
373+ // sync dimentions on big suggestion or input resize
374+ }
375+
376+ _updateSuggestionDisplay ( userText : string , suggestion : string ) : void {
377+ if ( ! this . _$suggestionInput ) {
378+ return ;
379+ }
380+
381+ this . _currentSuggestion = suggestion ;
382+
383+ const fullText = userText + suggestion ;
384+ this . _$suggestionInput . val ( fullText ) ;
385+
386+ this . _syncScroll ( ) ;
387+
388+ this . _isSuggestionVisible = ! ! suggestion ;
389+ }
390+
391+ _acceptSuggestion ( ) : void {
392+ if ( ! this . _currentSuggestion ) {
393+ return ;
394+ }
395+ const { text : currentValue = '' } = this . option ( ) ;
396+ // const currentValue = this.option('value') as unknown as string || '';
397+ const newValue = currentValue + this . _currentSuggestion ;
398+
399+ this . option ( 'value' , newValue ) ;
400+
401+ this . _rejectSuggestion ( ) ;
402+ }
403+
404+ _rejectSuggestion ( ) : void {
405+ if ( ! this . _$suggestionInput ) {
406+ return ;
407+ }
408+
409+ this . _currentSuggestion = undefined ;
410+ this . _isSuggestionVisible = false ;
411+
412+ this . _$suggestionInput . val ( '' ) ;
413+ }
414+
415+ _requestSuggestion ( text : string ) : void {
416+ const { userRole, onSuggestionRequest } = this . option ( ) ;
417+
418+ if ( ! onSuggestionRequest || typeof onSuggestionRequest !== 'function' ) {
419+ return ;
420+ }
421+
422+ onSuggestionRequest ( text , userRole )
423+ . then ( ( suggestion : string ) => { this . _updateSuggestionDisplay ( text , suggestion ) ; } )
424+ // @ts -expect-error ts-error
425+ . catch ( ( ) => { this . _rejectSuggestion ( ) ; } ) ;
426+ }
427+
428+ // _valueChangeEventHandler(e): void {
429+ // super._valueChangeEventHandler(e);
430+
431+ // if (this.option('smartSuggestionEnabled')) {
432+ // const text = this.option('value') as unknown as string || '';
433+ // this._requestSuggestion(text);
434+ // }
435+ // }
436+
437+ _clean ( ) : void {
438+ this . _disposeSuggestionTextArea ( ) ;
439+ super . _clean ( ) ;
440+ }
441+
267442 _optionChanged ( args : OptionChanged < TProperties > ) : void {
268443 const { name, value } = args ;
269444
@@ -290,6 +465,20 @@ class TextArea<
290465 this . _updateInputHeight ( ) ;
291466 }
292467 break ;
468+ case 'smartSuggestionEnabled' :
469+ if ( value ) {
470+ this . $element ( ) . addClass ( 'dx-textarea-smart' ) ;
471+ this . _renderSuggestionTextArea ( ) ;
472+ } else {
473+ this . $element ( ) . removeClass ( 'dx-textarea-smart' ) ;
474+ this . _disposeSuggestionTextArea ( ) ;
475+ }
476+ this . _refreshEvents ( ) ;
477+ break ;
478+ case 'userRole' :
479+ case 'suggestionLength' :
480+ case 'onSuggestionRequest' :
481+ break ;
293482 default :
294483 super . _optionChanged ( args ) ;
295484 }
0 commit comments