Skip to content

Commit daba416

Browse files
committed
feat(chat): emit event for typing change and add side by side chats story
1 parent bebfb9a commit daba416

3 files changed

Lines changed: 108 additions & 12 deletions

File tree

src/components/chat/chat-input.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,26 @@ export default class IgcChatInputComponent extends LitElement {
6666
this.adjustTextareaHeight();
6767
}
6868

69+
private isTyping = false;
70+
6971
private handleKeyDown(e: KeyboardEvent) {
7072
if (e.key === 'Enter' && !e.shiftKey) {
7173
e.preventDefault();
7274
this.sendMessage();
75+
} else if (this.isTyping === false) {
76+
const typingEvent = new CustomEvent('typing-change', {
77+
detail: { isTyping: true },
78+
});
79+
this.dispatchEvent(typingEvent);
80+
this.isTyping = true;
81+
// wait 3 seconds and dispatch a stop-typing event
82+
setTimeout(() => {
83+
const stopTypingEvent = new CustomEvent('typing-change', {
84+
detail: { isTyping: false },
85+
});
86+
this.dispatchEvent(stopTypingEvent);
87+
this.isTyping = false;
88+
}, 3000);
7389
}
7490
}
7591

src/components/chat/chat.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import type { IgcMessage, IgcMessageReaction, IgcUser } from './types.js';
1111

1212
export interface IgcChatComponentEventMap {
1313
igcMessageSend: CustomEvent<IgcMessage>;
14+
igcTypingChange: CustomEvent<IgcTypingChangeEventArgs>;
15+
}
16+
17+
export interface IgcTypingChangeEventArgs {
18+
user: IgcUser;
19+
isTyping: boolean;
1420
}
1521

1622
/**
@@ -114,6 +120,14 @@ export default class IgcChatComponent extends EventEmitterMixin<
114120
this.emitEvent('igcMessageSend', { detail: newMessage });
115121
}
116122

123+
private handleTypingChange(e: CustomEvent) {
124+
const isTyping = e.detail.isTyping;
125+
const user = this.user;
126+
if (!user) return;
127+
const typingArgs = { user, isTyping };
128+
this.emitEvent('igcTypingChange', { detail: typingArgs });
129+
}
130+
117131
private handleAddReaction(e: CustomEvent) {
118132
const { messageId, emojiId } = e.detail;
119133

@@ -188,6 +202,7 @@ export default class IgcChatComponent extends EventEmitterMixin<
188202
.disableAttachments=${this.disableAttachments}
189203
.disableEmojis=${this.disableEmojis}
190204
@message-send=${this.handleSendMessage}
205+
@typing-change=${this.handleTypingChange}
191206
></igc-chat-input>
192207
</div>
193208
`;

stories/chat.stories.ts

Lines changed: 77 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,43 +80,43 @@ type Story = StoryObj<IgcChatArgs>;
8080

8181
// endregion
8282

83-
const currentUser: any = {
83+
const userJohn: any = {
8484
id: 'user1',
85-
name: 'You',
85+
name: 'John',
8686
avatar: 'https://www.infragistics.com/angular-demos/assets/images/men/1.jpg',
8787
};
8888

89-
const otherUser: any = {
89+
const userRichard: any = {
9090
id: 'user2',
91-
name: 'Alice',
91+
name: 'Richard',
9292
avatar: 'https://www.infragistics.com/angular-demos/assets/images/men/2.jpg',
9393
};
9494

95-
const thirdUser: any = {
95+
const userSam: any = {
9696
id: 'user3',
9797
name: 'Sam',
9898
avatar: 'https://www.infragistics.com/angular-demos/assets/images/men/3.jpg',
9999
};
100100

101-
const initialMessages: any[] = [
101+
const messages: any[] = [
102102
{
103103
id: '1',
104104
text: 'Hey there! How are you doing today?',
105-
sender: otherUser,
105+
sender: userRichard,
106106
timestamp: new Date(2025, 4, 5),
107107
status: 'read',
108108
},
109109
{
110110
id: '2',
111111
text: "I'm doing well, thanks for asking! How about you?",
112-
sender: currentUser,
112+
sender: userJohn,
113113
timestamp: new Date(Date.now() - 3500000),
114114
status: 'read',
115115
},
116116
{
117117
id: '3',
118118
text: 'Pretty good! I was wondering if you wanted to grab coffee sometime this week?',
119-
sender: otherUser,
119+
sender: userRichard,
120120
timestamp: new Date(Date.now() - 3400000),
121121
status: 'read',
122122
reactions: [
@@ -129,7 +129,7 @@ const initialMessages: any[] = [
129129
{
130130
id: '4',
131131
text: 'Hi guys! I just joined the chat.',
132-
sender: thirdUser,
132+
sender: userSam,
133133
timestamp: new Date(Date.now() - 3300000),
134134
status: 'read',
135135
attachments: [
@@ -146,8 +146,8 @@ const initialMessages: any[] = [
146146
export const Basic: Story = {
147147
render: (args) => html`
148148
<igc-chat
149-
.user=${currentUser}
150-
.messages=${initialMessages}
149+
.user=${userJohn}
150+
.messages=${messages}
151151
.headerText=${args.headerText}
152152
.disableAutoScroll=${args.disableAutoScroll}
153153
.disableReactions=${args.disableReactions}
@@ -160,3 +160,68 @@ export const Basic: Story = {
160160
</igc-chat>
161161
`,
162162
};
163+
164+
function handleMessageEntered(e: CustomEvent) {
165+
const newMessage = e.detail;
166+
messages.push(newMessage);
167+
const chatElements = document.querySelectorAll('igc-chat');
168+
chatElements.forEach((chat) => {
169+
chat.messages = [...messages];
170+
});
171+
}
172+
173+
function handleTypingChange(e: CustomEvent) {
174+
const user = e.detail.user;
175+
const isTyping = e.detail.isTyping;
176+
const chatElements = document.querySelectorAll('igc-chat');
177+
chatElements.forEach((chat) => {
178+
if (chat.user === user) {
179+
return;
180+
}
181+
182+
if (!isTyping && chat.typingUsers.includes(user)) {
183+
chat.typingUsers = chat.typingUsers.filter((u) => u !== user);
184+
} else if (isTyping && !chat.typingUsers.includes(user)) {
185+
chat.typingUsers = [...chat.typingUsers, user];
186+
}
187+
});
188+
}
189+
190+
export const SideBySide: Story = {
191+
render: (args) => html`
192+
<div style="display: flex; gap: 20px;">
193+
<igc-chat
194+
.user=${userJohn}
195+
.messages=${messages}
196+
@igcMessageSend=${handleMessageEntered}
197+
@igcTypingChange=${handleTypingChange}
198+
header-text="Richard"
199+
.disableAutoScroll=${args.disableAutoScroll}
200+
.disableReactions=${args.disableReactions}
201+
.disableAttachments=${args.disableAttachments}
202+
.disableEmojis=${args.disableEmojis}
203+
.hideAvatar=${args.hideAvatar}
204+
.hideUserName=${args.hideUserName}
205+
.hideMetaData=${args.hideMetaData}
206+
style="width: 50%;"
207+
>
208+
</igc-chat>
209+
<igc-chat
210+
.user=${userRichard}
211+
.messages=${messages}
212+
@igcMessageSend=${handleMessageEntered}
213+
@igcTypingChange=${handleTypingChange}
214+
header-text="John"
215+
.disableAutoScroll=${args.disableAutoScroll}
216+
.disableReactions=${args.disableReactions}
217+
.disableAttachments=${args.disableAttachments}
218+
.disableEmojis=${args.disableEmojis}
219+
.hideAvatar=${args.hideAvatar}
220+
.hideUserName=${args.hideUserName}
221+
.hideMetaData=${args.hideMetaData}
222+
style="width: 50%;"
223+
>
224+
</igc-chat>
225+
</div>
226+
`,
227+
};

0 commit comments

Comments
 (0)