Skip to content

Commit 4b6f657

Browse files
committed
fix: Strip significant whitespace from empty template containers
1 parent 258e457 commit 4b6f657

File tree

6 files changed

+35
-46
lines changed

6 files changed

+35
-46
lines changed

src/components/chat/chat-input.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { chatContext, chatUserInputContext } from '../common/context.js';
1111
import { enterKey } from '../common/controllers/key-bindings.js';
1212
import { registerComponent } from '../common/definitions/register.js';
1313
import { partMap } from '../common/part-map.js';
14-
import { bindIf, hasFiles, isEmpty } from '../common/util.js';
14+
import { bindIf, hasFiles, isEmpty, trimmedHtml } from '../common/util.js';
1515
import IgcIconComponent from '../icon/icon.js';
1616
import IgcTextareaComponent from '../textarea/textarea.js';
1717
import type { ChatState } from './chat-state.js';
@@ -333,7 +333,7 @@ export default class IgcChatInputComponent extends LitElement {
333333
private _renderActionsArea() {
334334
const ctx: ChatRenderContext = { instance: this._state.host };
335335

336-
return html`
336+
return trimmedHtml`
337337
<div part="file-upload">
338338
${this._getRenderer('fileUploadButton')(ctx)}
339339
</div>

src/components/chat/chat-message.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import IgcIconButtonComponent from '../button/icon-button.js';
88
import { chatContext } from '../common/context.js';
99
import { registerComponent } from '../common/definitions/register.js';
1010
import { partMap } from '../common/part-map.js';
11-
import { isEmpty } from '../common/util.js';
11+
import { isEmpty, trimmedHtml } from '../common/util.js';
1212
import type { ChatState } from './chat-state.js';
1313
import IgcMessageAttachmentsComponent from './message-attachments.js';
1414
import { styles } from './themes/message.base.css.js';
@@ -234,7 +234,7 @@ export default class IgcChatMessageComponent extends LitElement {
234234
instance: this._state.host,
235235
};
236236

237-
return html`
237+
return trimmedHtml`
238238
<div part="message-header">
239239
${until(this._getRenderer('messageHeader')(ctx))}
240240
</div>
@@ -271,7 +271,9 @@ export default class IgcChatMessageComponent extends LitElement {
271271
exportparts="attachment: message-attachment, attachments-container: message-attachments-container"
272272
>
273273
${cache(
274-
messageRenderer ? messageRenderer(ctx) : this._renderMessage()
274+
messageRenderer
275+
? until(messageRenderer(ctx))
276+
: this._renderMessage()
275277
)}
276278
</div>
277279
`

src/components/chat/chat.ts

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { registerComponent } from '../common/definitions/register.js';
1111
import { IgcChatResourceStringEN } from '../common/i18n/chat.resources.js';
1212
import type { Constructor } from '../common/mixins/constructor.js';
1313
import { EventEmitterMixin } from '../common/mixins/event-emitter.js';
14-
import { isEmpty, last } from '../common/util.js';
14+
import { isEmpty } from '../common/util.js';
1515
import IgcIconComponent from '../icon/icon.js';
1616
import IgcListComponent from '../list/list.js';
1717
import IgcToastComponent from '../toast/toast.js';
@@ -280,10 +280,8 @@ export default class IgcChatComponent extends EventEmitterMixin<
280280
this._input?.focusInput();
281281
}
282282

283-
// REVIEW: Maybe accept an `IgcMessage` type as well?
284283
/**
285284
* Scrolls the view to a specific message by id.
286-
* @param messageId - The id of the message to scroll to
287285
*/
288286
public scrollToMessage(messageId: string): void {
289287
if (!isEmpty(this.messages)) {
@@ -292,36 +290,6 @@ export default class IgcChatComponent extends EventEmitterMixin<
292290
}
293291
}
294292

295-
// REVIEW
296-
public addMessage(message: Partial<IgcChatMessage>): IgcChatMessage {
297-
this._state.addMessage(message);
298-
return last(this.messages);
299-
}
300-
301-
// REVIEW
302-
public updateMessage(
303-
message: IgcChatMessage,
304-
data: Partial<IgcChatMessage>,
305-
scrollIntoView = false
306-
) {
307-
Object.assign(message, data);
308-
const messageElement =
309-
this.renderRoot.querySelector<IgcChatMessageComponent>(
310-
`#message-${message.id}`
311-
);
312-
313-
if (messageElement) {
314-
messageElement.requestUpdate();
315-
if (scrollIntoView) {
316-
messageElement.updateComplete.then(() =>
317-
messageElement.scrollIntoView({ block: 'end', inline: 'end' })
318-
);
319-
}
320-
} else {
321-
this.requestUpdate('messages');
322-
}
323-
}
324-
325293
protected override updated(properties: PropertyValues<this>): void {
326294
if (
327295
(properties.has('messages') ||

src/components/chat/message-attachments.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import IgcIconButtonComponent from '../button/icon-button.js';
99
import { chatContext } from '../common/context.js';
1010
import { registerComponent } from '../common/definitions/register.js';
1111
import { partMap } from '../common/part-map.js';
12+
import { trimmedHtml } from '../common/util.js';
1213
import IgcIconComponent from '../icon/icon.js';
1314
import type { ChatState } from './chat-state.js';
1415
import { all } from './themes/attachments.js';
@@ -188,7 +189,7 @@ export default class IgcMessageAttachmentsComponent extends LitElement {
188189
${repeat(
189190
attachments,
190191
(attachment) => attachment.id,
191-
(attachment) => html`
192+
(attachment) => trimmedHtml`
192193
<div part="${partMap(attachmentParts)}">
193194
${until(
194195
this._getRenderer('attachment')({

src/components/common/util.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isServer, nothing } from 'lit';
1+
import { html, isServer, nothing, type TemplateResult } from 'lit';
22
import type IgcFileInputComponent from '../file-input/file-input.js';
33

44
export const asPercent = (part: number, whole: number) => (part / whole) * 100;
@@ -627,3 +627,21 @@ export function hasFiles(
627627
): boolean {
628628
return input.files != null && input.files.length > 0;
629629
}
630+
631+
const trimmedCache = new WeakMap<TemplateStringsArray, TemplateStringsArray>();
632+
633+
/** @internal */
634+
export function trimmedHtml(
635+
strings: TemplateStringsArray,
636+
...values: unknown[]
637+
): TemplateResult {
638+
if (!trimmedCache.has(strings)) {
639+
const trimmedStrings = strings.map((s) => s.trim().replaceAll('\n', ''));
640+
trimmedCache.set(
641+
strings,
642+
Object.assign([...trimmedStrings], { raw: [...strings.raw] })
643+
);
644+
}
645+
646+
return html(trimmedCache.get(strings)!, ...values);
647+
}

stories/chat.stories.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,10 @@ function handleMessageSend(event: CustomEvent<IgcChatMessage>): void {
207207

208208
isResponseSent = false;
209209
setTimeout(async () => {
210-
chat.addMessage({ sender: 'bot', attachments });
210+
chat.messages = [
211+
...chat.messages,
212+
{ id: crypto.randomUUID(), text: '', sender: 'bot', attachments },
213+
];
211214

212215
await showResponse(chat, generateAIResponse(message.text).split(' '));
213216

@@ -223,11 +226,8 @@ async function showResponse(chat: IgcChatComponent, responseParts: string[]) {
223226

224227
for (const part of responseParts) {
225228
await new Promise((resolve) => requestAnimationFrame(resolve));
226-
chat.updateMessage(
227-
lastMessage,
228-
{ text: `${lastMessage.text} ${part}` },
229-
true
230-
);
229+
lastMessage.text = `${lastMessage.text} ${part}`;
230+
chat.messages = [...chat.messages];
231231
}
232232
}
233233

0 commit comments

Comments
 (0)