@@ -10,6 +10,7 @@ import { chatContext } from '../common/context.js';
1010import { addKeybindings } from '../common/controllers/key-bindings.js' ;
1111import { watch } from '../common/decorators/watch.js' ;
1212import { registerComponent } from '../common/definitions/register.js' ;
13+ import { isEmpty } from '../common/util.js' ;
1314import IgcIconComponent from '../icon/icon.js' ;
1415import { registerIconFromText } from '../icon/icon.registry.js' ;
1516import IgcTextareaComponent from '../textarea/textarea.js' ;
@@ -25,6 +26,7 @@ import {
2526 sendButtonIcon ,
2627 starIcon ,
2728} from './types.js' ;
29+ import { getChatAcceptedFiles , getIconName } from './utils.js' ;
2830
2931/**
3032 * A web component that provides the input area for the `igc-chat` interface.
@@ -60,11 +62,8 @@ export default class IgcChatInputComponent extends LitElement {
6062
6163 public static override styles = [ styles , shared ] ;
6264
63- @consume ( { context : chatContext , subscribe : true } )
64- private _chatState ! : ChatState ;
65-
6665 /* blazorSuppress */
67- public static register ( ) {
66+ public static register ( ) : void {
6867 registerComponent (
6968 IgcChatInputComponent ,
7069 IgcTextareaComponent ,
@@ -74,15 +73,20 @@ export default class IgcChatInputComponent extends LitElement {
7473 ) ;
7574 }
7675
76+ @consume ( { context : chatContext , subscribe : true } )
77+ private readonly _chatState ! : ChatState ;
78+
79+ private get _acceptedTypes ( ) {
80+ return this . _chatState . acceptedFileTypes ;
81+ }
82+
7783 @query ( IgcTextareaComponent . tagName )
78- private _textInputElement ! : IgcTextareaComponent ;
84+ private readonly _textInputElement ! : IgcTextareaComponent ;
7985
8086 @query ( '#input_attachments' )
81- protected _inputAttachmentsButton ! : IgcIconButtonComponent ;
82- // private readonly _textAreaRef = createRef<IgcTextareaComponent>();
83- private readonly _attachmentsButtonInputRef = createRef < HTMLInputElement > ( ) ;
87+ protected readonly _inputAttachmentsButton ! : IgcIconButtonComponent ;
8488
85- // private readonly _textAreaRef = createRef<IgcTextareaComponent >();
89+ private readonly _attachmentsButtonInputRef = createRef < HTMLInputElement > ( ) ;
8690
8791 @watch ( 'acceptedFiles' , { waitUntilFirstUpdate : true } )
8892 protected acceptedFilesChange ( ) : void {
@@ -134,19 +138,18 @@ export default class IgcChatInputComponent extends LitElement {
134138 }
135139 }
136140
137- private handleDragEnter ( e : DragEvent ) {
138- e . preventDefault ( ) ;
139- e . stopPropagation ( ) ;
140-
141- const files = Array . from ( e . dataTransfer ?. items || [ ] ) . filter (
142- ( item ) => item . kind === 'file'
143- ) ;
144- const hasValidFiles = files . some ( ( item ) =>
145- this . _chatState . isFileTypeAccepted ( item . getAsFile ( ) as File , item . type )
141+ private _handleFocusState ( event : FocusEvent ) : void {
142+ this . _chatState . emitEvent (
143+ event . type === 'focus' ? 'igcInputFocus' : 'igcInputBlur'
146144 ) ;
145+ }
147146
148- this . containerPart = `input-container ${ hasValidFiles ? ' dragging' : '' } ` ;
147+ private handleDragEnter ( event : DragEvent ) {
148+ event . preventDefault ( ) ;
149+ event . stopPropagation ( ) ;
149150
151+ const validFiles = getChatAcceptedFiles ( event , this . _acceptedTypes ) ;
152+ this . containerPart = `input-container ${ ! isEmpty ( validFiles ) ? ' dragging' : '' } ` ;
150153 this . _chatState . emitEvent ( 'igcAttachmentDrag' ) ;
151154 }
152155
@@ -179,15 +182,8 @@ export default class IgcChatInputComponent extends LitElement {
179182 e . stopPropagation ( ) ;
180183 this . containerPart = 'input-container' ;
181184
182- const files = Array . from ( e . dataTransfer ?. files || [ ] ) ;
183- if ( files . length === 0 ) return ;
184-
185- const validFiles = files . filter ( ( file ) =>
186- this . _chatState . isFileTypeAccepted ( file )
187- ) ;
188-
185+ const validFiles = getChatAcceptedFiles ( e , this . _acceptedTypes ) ;
189186 this . _chatState . emitEvent ( 'igcAttachmentDrop' ) ;
190-
191187 this . _chatState . attachFiles ( validFiles ) ;
192188 this . requestUpdate ( ) ;
193189 }
@@ -197,20 +193,22 @@ export default class IgcChatInputComponent extends LitElement {
197193 * Updates internal inputValue and emits 'igcInputChange' event.
198194 * @param e Input event from the text area
199195 */
200- private handleInput = ( { detail } : CustomEvent < string > ) => {
196+ private _handleInput ( { detail } : CustomEvent < string > ) : void {
201197 if ( detail === this . _chatState ?. inputValue ) return ;
202198
203199 this . _chatState . inputValue = detail ;
204200 this . _chatState ?. emitEvent ( 'igcInputChange' , { detail : { value : detail } } ) ;
205- } ;
201+ }
202+
203+ private _handleFileUpload ( event : Event ) : void {
204+ const input = event . target as HTMLInputElement ;
206205
207- private handleFileUpload = ( e : Event ) => {
208- const input = e . target as HTMLInputElement ;
209- if ( ! input . files || input . files . length === 0 ) return ;
206+ if ( ! input . files || input . files . length === 0 ) {
207+ return ;
208+ }
210209
211- const files = Array . from ( input . files ) ;
212- this . _chatState ?. attachFiles ( files ) ;
213- } ;
210+ this . _chatState . attachFiles ( Array . from ( input . files ) ) ;
211+ }
214212 /**
215213 * Default attachments area template used when no custom template is provided.
216214 * Renders the list of input attachments as chips.
@@ -226,9 +224,7 @@ export default class IgcChatInputComponent extends LitElement {
226224 >
227225 < igc-icon
228226 slot ="prefix "
229- name =${ this . _chatState ?. getIconName (
230- attachment . file ?. type ?? attachment . type
231- ) }
227+ name =${ getIconName ( attachment . file ?. type ?? attachment . type ) }
232228 collection ="material"
233229 > </ igc-icon >
234230 < span part ="attachment-name "> ${ attachment . name } </ span >
@@ -243,17 +239,19 @@ export default class IgcChatInputComponent extends LitElement {
243239 * Renders a text area for user input.
244240 * @returns TemplateResult containing the text area
245241 */
246- private renderTextArea ( ) {
247- return html ` < igc-textarea
248- part ="text-input "
249- .placeholder =${ this . _chatState ?. options ?. inputPlaceholder }
250- resize ="auto"
251- rows="1"
252- .value=${ this . _chatState ?. inputValue }
253- @igcInput=${ this . handleInput }
254- @focus=${ ( ) => this . _chatState ?. emitEvent ( 'igcInputFocus' ) }
255- @blur=${ ( ) => this . _chatState ?. emitEvent ( 'igcInputBlur' ) }
256- > </ igc-textarea > ` ;
242+ private _renderTextArea ( ) {
243+ return html `
244+ < igc-textarea
245+ part ="text-input "
246+ placeholder =${ ifDefined ( this . _chatState ?. options ?. inputPlaceholder ) }
247+ resize ="auto"
248+ rows="1"
249+ .value=${ this . _chatState ?. inputValue }
250+ @igcInput=${ this . _handleInput }
251+ @focus=${ this . _handleFocusState }
252+ @blur=${ this . _handleFocusState }
253+ > </ igc-textarea >
254+ ` ;
257255 }
258256
259257 /**
@@ -262,7 +260,7 @@ export default class IgcChatInputComponent extends LitElement {
262260 * @returns TemplateResult containing the file upload button
263261 */
264262 private renderFileUploadButton ( ) {
265- if ( this . _chatState ?. options ?. disableInputAttachments ) return html `` ;
263+ if ( this . _chatState ?. options ?. disableInputAttachments ) return nothing ;
266264 return html `
267265 < label for ="input_attachments " part ="upload-button ">
268266 < igc-icon-button
@@ -278,7 +276,7 @@ export default class IgcChatInputComponent extends LitElement {
278276 ${ ref ( this . _attachmentsButtonInputRef ) }
279277 multiple
280278 accept =${ ifDefined ( this . _chatState ?. options ?. acceptedFiles === '' ? undefined : this . _chatState ?. options ?. acceptedFiles ) }
281- @change =${ this . handleFileUpload } >
279+ @change =${ this . _handleFileUpload } >
282280 </ input >
283281 </ label >
284282 ` ;
@@ -289,21 +287,23 @@ export default class IgcChatInputComponent extends LitElement {
289287 * Renders a send button that submits the current input value and attachments.
290288 * @returns TemplateResult containing the send button
291289 */
292- private renderSendButton ( ) {
293- return html ` < igc-icon-button
294- aria-label ="Send message "
295- name ="send-message "
296- collection ="material "
297- variant ="contained "
298- part ="send-button "
299- ?disabled =${ ! this . _chatState ?. inputValue . trim ( ) &&
300- this . _chatState ?. inputAttachments . length === 0 }
301- @click =${ this . _chatState ?. sendMessage }
302- > </ igc-icon-button > ` ;
290+ private _renderSendButton ( ) {
291+ return html `
292+ < igc-icon-button
293+ aria-label ="Send message "
294+ name ="send-message "
295+ collection ="material "
296+ variant ="contained "
297+ part ="send-button "
298+ ?disabled =${ ! this . _chatState ?. inputValue . trim ( ) &&
299+ this . _chatState ?. inputAttachments . length === 0 }
300+ @click =${ this . _chatState ?. sendMessage }
301+ > </ igc-icon-button >
302+ ` ;
303303 }
304304
305- private renderActionsArea ( ) {
306- return html ` ${ this . renderFileUploadButton ( ) } ${ this . renderSendButton ( ) } ` ;
305+ private _renderActionsArea ( ) {
306+ return html `${ this . renderFileUploadButton ( ) } ${ this . _renderSendButton ( ) } ` ;
307307 }
308308
309309 protected override render ( ) {
@@ -315,8 +315,8 @@ export default class IgcChatInputComponent extends LitElement {
315315 ${ this . renderAttachmentsArea ( this . _chatState . inputAttachments ) }
316316 </ div > `
317317 : nothing }
318- < div part ="input-wrapper "> ${ this . renderTextArea ( ) } </ div >
319- < div part ="buttons-container "> ${ this . renderActionsArea ( ) } </ div >
318+ < div part ="input-wrapper "> ${ this . _renderTextArea ( ) } </ div >
319+ < div part ="buttons-container "> ${ this . _renderActionsArea ( ) } </ div >
320320 </ div >
321321 ` ;
322322 }
0 commit comments