Skip to content

Commit 7fb8f58

Browse files
committed
refactor: Cleaned up chat state and smaller components
1 parent e2ab0cb commit 7fb8f58

File tree

5 files changed

+200
-215
lines changed

5 files changed

+200
-215
lines changed

src/components/chat/chat-input.ts

Lines changed: 66 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { chatContext } from '../common/context.js';
1010
import { addKeybindings } from '../common/controllers/key-bindings.js';
1111
import { watch } from '../common/decorators/watch.js';
1212
import { registerComponent } from '../common/definitions/register.js';
13+
import { isEmpty } from '../common/util.js';
1314
import IgcIconComponent from '../icon/icon.js';
1415
import { registerIconFromText } from '../icon/icon.registry.js';
1516
import 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

Comments
 (0)